From 9e3e6be1c66e5c77650f1ed2db7b0b9af64ece28 Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Fri, 4 Feb 2022 13:45:52 -0500 Subject: [PATCH 01/21] Revert "Fixed a bug causing double scroll bars to appear (#2217)" This reverts commit 55879bdf110e18e69c480ba6575c65ba0a58444b. --- webapp/src/pages/boardPage.scss | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/webapp/src/pages/boardPage.scss b/webapp/src/pages/boardPage.scss index f94317e68..881213ed7 100644 --- a/webapp/src/pages/boardPage.scss +++ b/webapp/src/pages/boardPage.scss @@ -39,7 +39,7 @@ } } - @media screen and (max-width: 768px) { + @media screen and (max-width: 768px) { .mobileWarning {display: flex;} } } @@ -48,7 +48,3 @@ width: 600px; height: 468px; } - -.mainContentRow { - height: calc(100% - 40px); -} From 6389d542b259b9bfc81bf369597526ef5f6f5047 Mon Sep 17 00:00:00 2001 From: Asaad Mahmood Date: Tue, 8 Feb 2022 22:08:59 +0500 Subject: [PATCH 02/21] GH-2249 - Updating template selector screen (#2279) * GH-2249 - Updating template selector screen * Updating snapshots --- .../boardTemplateSelector.test.tsx.snap | 24 +--- ...boardTemplateSelectorPreview.test.tsx.snap | 3 - .../boardTemplateSelector.scss | 4 + .../boardTemplateSelectorPreview.scss | 8 +- .../boardTemplateSelectorPreview.tsx | 105 +++++++++--------- 5 files changed, 61 insertions(+), 83 deletions(-) diff --git a/webapp/src/components/boardTemplateSelector/__snapshots__/boardTemplateSelector.test.tsx.snap b/webapp/src/components/boardTemplateSelector/__snapshots__/boardTemplateSelector.test.tsx.snap index e1d2c8bc2..6cfb85141 100644 --- a/webapp/src/components/boardTemplateSelector/__snapshots__/boardTemplateSelector.test.tsx.snap +++ b/webapp/src/components/boardTemplateSelector/__snapshots__/boardTemplateSelector.test.tsx.snap @@ -110,11 +110,7 @@ exports[`components/boardTemplateSelector/boardTemplateSelector a focalboard Plu >
-
-
+ />
@@ -239,11 +235,7 @@ exports[`components/boardTemplateSelector/boardTemplateSelector a focalboard Plu >
-
-
+ />
@@ -368,11 +360,7 @@ exports[`components/boardTemplateSelector/boardTemplateSelector a focalboard Plu >
-
-
+ />
@@ -507,11 +495,7 @@ exports[`components/boardTemplateSelector/boardTemplateSelector not a focalboard >
-
-
+ />
diff --git a/webapp/src/components/boardTemplateSelector/__snapshots__/boardTemplateSelectorPreview.test.tsx.snap b/webapp/src/components/boardTemplateSelector/__snapshots__/boardTemplateSelectorPreview.test.tsx.snap index b26f668e3..f96559440 100644 --- a/webapp/src/components/boardTemplateSelector/__snapshots__/boardTemplateSelectorPreview.test.tsx.snap +++ b/webapp/src/components/boardTemplateSelector/__snapshots__/boardTemplateSelectorPreview.test.tsx.snap @@ -7,9 +7,6 @@ exports[`components/boardTemplateSelector/boardTemplateSelectorPreview should ma
-
diff --git a/webapp/src/components/boardTemplateSelector/boardTemplateSelector.scss b/webapp/src/components/boardTemplateSelector/boardTemplateSelector.scss index 80431f4ae..e795b9af1 100644 --- a/webapp/src/components/boardTemplateSelector/boardTemplateSelector.scss +++ b/webapp/src/components/boardTemplateSelector/boardTemplateSelector.scss @@ -82,6 +82,10 @@ justify-content: center; } + .empty-board { + background-color: rgb(var(--center-channel-bg-rgb)); + } + .Button { &:first-child { margin-right: 16px; diff --git a/webapp/src/components/boardTemplateSelector/boardTemplateSelectorPreview.scss b/webapp/src/components/boardTemplateSelector/boardTemplateSelectorPreview.scss index 97220abb5..c51efaa39 100644 --- a/webapp/src/components/boardTemplateSelector/boardTemplateSelectorPreview.scss +++ b/webapp/src/components/boardTemplateSelector/boardTemplateSelectorPreview.scss @@ -4,15 +4,9 @@ width: 158%; height: 480px; border-radius: var(--modal-rad); + pointer-events: none; .Kanban { overflow: hidden; } - - .prevent-click { - position: absolute; - width: 100%; - height: 100%; - z-index: 1100; - } } diff --git a/webapp/src/components/boardTemplateSelector/boardTemplateSelectorPreview.tsx b/webapp/src/components/boardTemplateSelector/boardTemplateSelectorPreview.tsx index af7cfc2d1..cd2a4bfcb 100644 --- a/webapp/src/components/boardTemplateSelector/boardTemplateSelectorPreview.tsx +++ b/webapp/src/components/boardTemplateSelector/boardTemplateSelectorPreview.tsx @@ -65,28 +65,27 @@ const BoardTemplateSelectorPreview = React.memo((props: Props) => { return (
-
{activeView && -
- - null} - addCardFromTemplate={() => null} - addCardTemplate={() => null} - editCardTemplate={() => null} - readonly={false} - showShared={false} - /> -
} +
+ + null} + addCardFromTemplate={() => null} + addCardTemplate={() => null} + editCardTemplate={() => null} + readonly={false} + showShared={false} + /> +
} {activeView?.fields.viewType === 'board' && { showCard={() => null} />} {activeView?.fields.viewType === 'table' && - null} - addCard={() => Promise.resolve()} - showCard={() => null} - />} +
null} + addCard={() => Promise.resolve()} + showCard={() => null} + />} {activeView?.fields.viewType === 'gallery' && - null} - addCard={() => Promise.resolve()} - />} + null} + addCard={() => Promise.resolve()} + />} {activeView?.fields.viewType === 'calendar' && - null} - addCard={() => Promise.resolve()} - />} + null} + addCard={() => Promise.resolve()} + />} ) }) From 9071424abafe0043edf2bcde010d771e4e19d659 Mon Sep 17 00:00:00 2001 From: Chen-I Lim <46905241+chenilim@users.noreply.github.com> Date: Tue, 8 Feb 2022 09:56:58 -0800 Subject: [PATCH 03/21] Set theme jekyll-theme-architect --- docs/_config.yml | 1 + docs/index.md | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 docs/_config.yml create mode 100644 docs/index.md diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 000000000..3397c9a49 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-architect \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..01c76ac6e --- /dev/null +++ b/docs/index.md @@ -0,0 +1,37 @@ +## Welcome to GitHub Pages + +You can use the [editor on GitHub](https://github.com/mattermost/focalboard/edit/main/docs/index.md) to maintain and preview the content for your website in Markdown files. + +Whenever you commit to this repository, GitHub Pages will run [Jekyll](https://jekyllrb.com/) to rebuild the pages in your site, from the content in your Markdown files. + +### Markdown + +Markdown is a lightweight and easy-to-use syntax for styling your writing. It includes conventions for + +```markdown +Syntax highlighted code block + +# Header 1 +## Header 2 +### Header 3 + +- Bulleted +- List + +1. Numbered +2. List + +**Bold** and _Italic_ and `Code` text + +[Link](url) and ![Image](src) +``` + +For more details see [Basic writing and formatting syntax](https://docs.github.com/en/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax). + +### Jekyll Themes + +Your Pages site will use the layout and styles from the Jekyll theme you have selected in your [repository settings](https://github.com/mattermost/focalboard/settings/pages). The name of this theme is saved in the Jekyll `_config.yml` configuration file. + +### Support or Contact + +Having trouble with Pages? Check out our [documentation](https://docs.github.com/categories/github-pages-basics/) or [contact support](https://support.github.com/contact) and we’ll help you sort it out. From 03c240c7998ff45d6693bf90f7769e6dd39cafc3 Mon Sep 17 00:00:00 2001 From: Chen-I Lim <46905241+chenilim@users.noreply.github.com> Date: Tue, 8 Feb 2022 10:58:24 -0800 Subject: [PATCH 04/21] API Readme: Add note on Boards API usage. (#2293) --- server/swagger/README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/server/swagger/README.md b/server/swagger/README.md index 504df0d02..f349f1129 100644 --- a/server/swagger/README.md +++ b/server/swagger/README.md @@ -18,3 +18,27 @@ brew install openapi-generator # Server API documentation See the generated [server API documentation here](https://htmlpreview.github.io/?https://github.com/mattermost/focalboard/blob/main/server/swagger/docs/html/index.html). + +# Differences for Mattermost Boards + +The auto-generated Swagger API documentation is for Focalboard Personal Server. If you are calling the API on Mattermost Boards, the additional changes are: + +### API URLs endpoint + +The API endpoint is at `https://SERVERNAME/plugins/focalboard/api/`, e.g. `https://community.mattermost.com/plugins/focalboard/api/`. + +### Use the Mattermost auth token + +Refer to the [Mattermost API documentation here](https://api.mattermost.com/#tag/authentication) on how to obtain the auth token. + +Pass this token as a bearer token to the Boards APIs, e.g. + +``` +curl -i -H "X-Requested-With: XMLHttpRequest" -H 'Authorization: Bearer abcdefghijklmnopqrstuvwxyz' https://community.mattermost.com/plugins/focalboard/api/v1/workspaces +``` + +Note that the `X-Requested-With: XMLHttpRequest` header is required to pass the CSRF check. + +# We want to hear from you! + +If you are planning on using the Boards API, we would love to hear about what you'd like to do, and how we can improve the APIs in the future. [See here](https://github.com/mattermost/focalboard/discussions/2139) for more details on how to connect. From 761e268bf6e593ec4ba34a72169f94a7e98cd3c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Tue, 8 Feb 2022 22:44:15 +0100 Subject: [PATCH 05/21] Preview the first view of the template (#2295) --- .../boardTemplateSelector/boardTemplateSelectorPreview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/components/boardTemplateSelector/boardTemplateSelectorPreview.tsx b/webapp/src/components/boardTemplateSelector/boardTemplateSelectorPreview.tsx index cd2a4bfcb..96f2de13a 100644 --- a/webapp/src/components/boardTemplateSelector/boardTemplateSelectorPreview.tsx +++ b/webapp/src/components/boardTemplateSelector/boardTemplateSelectorPreview.tsx @@ -33,7 +33,7 @@ const BoardTemplateSelectorPreview = React.memo((props: Props) => { setActiveTemplateCards([]) octoClient.getSubtree(activeTemplate.id, activeView?.fields.viewType === 'gallery' ? 3 : 2, activeTemplate.workspaceId).then((blocks) => { const cards = blocks.filter((b) => b.type === 'card') - const views = blocks.filter((b) => b.type === 'view') + const views = blocks.filter((b) => b.type === 'view').sort((a, b) => a.title.localeCompare(b.title)) if (views.length > 0) { setActiveView(views[0] as BoardView) } From ad9e83e8fd008c256aabfaea0e9298c975334361 Mon Sep 17 00:00:00 2001 From: Chen-I Lim <46905241+chenilim@users.noreply.github.com> Date: Tue, 8 Feb 2022 13:46:14 -0800 Subject: [PATCH 06/21] Port contributors guide to GitHub pages (#2296) * Port contributor's guide to GitHub * Fix #2178. Port contributors guide to GitHub pages * Fix wrong file change * typo * Fix link * Replace PR with pull request * Remove comment * Doc at-mention, headings * Add sudo apt-get install autoconf dh-autoreconf * database typo * Update docs/dev-tips.md Co-authored-by: Doug Lauder * Update docs/index.md Co-authored-by: Doug Lauder * Update index.md * Update dev-tips.md Co-authored-by: Doug Lauder Co-authored-by: Justine Geffen --- docs/code-review.md | 55 +++++++++++ docs/contribution-checklist.md | 16 ++++ docs/contributions-without-ticket.md | 15 +++ docs/dev-tips.md | 96 +++++++++++++++++++ docs/index.md | 38 ++------ .../contribute/getting-started/dev-tips.md | 2 +- 6 files changed, 193 insertions(+), 29 deletions(-) create mode 100644 docs/code-review.md create mode 100644 docs/contribution-checklist.md create mode 100644 docs/contributions-without-ticket.md create mode 100644 docs/dev-tips.md diff --git a/docs/code-review.md b/docs/code-review.md new file mode 100644 index 000000000..baf55da32 --- /dev/null +++ b/docs/code-review.md @@ -0,0 +1,55 @@ +# Code Review Checklist + +Currently, all changes to the product must be reviewed by a [core committer](/contribute/getting-started/core-committers/#core-committers). + +### If you are a community member seeking a review + +1. Submit your pull request (PR). + * Follow the [contribution checklist](../contribution-checklist/). +2. Wait for a reviewer to be assigned. + * Product managers are on the lookout for new pull requests and usually handle this for you automatically. + * If you have been working alongside a core committer, feel free to message them for help. + * When in doubt, ask for help in the [Focalboard](https://community.mattermost.com/core/channels/focalboard) channel on our community server. + * If you are still stuck, please message Chen Lim (@chenilim on GitHub). +3. [Wait for a review](#if-you-are-awaiting-a-review). + * Expect some interaction with at least one reviewer within 5 business days (weekdays, Monday through Friday, excluding [statutory holidays](https://docs.mattermost.com/process/working-at-mattermost.html#holidays)). + * Keep in mind that core committers are geographically distributed around the world and likely in a different time zone than your own. + * If no interaction has occurred after 5 business days, please [at-mention](https://github.blog/2011-03-23-mention-somebody-they-re-notified/) a reviewer with a comment on your pull request. +4. Make any necessary changes. + * If a reviewer requests changes, your pull request will disappear from their queue of reviews. + * Once you've addressed the concerns, please at-mention the reviewer with a comment on your pull request. +5. Wait for your code to be merged. + * Larger pull requests may require more time to review. + * Once all reviewers have approved your changes, they will handle merging your code. + +### If you are awaiting a review + +1. Wait patiently for reviews to complete. + * Expect some interaction with each of your reviewers within 5 business days. + * There is no need to explicitly mention them on the pull request or to explicitly reach out on our community server. +2. Make any necessary changes. + * If a reviewer requests changes, your pull request will disappear from their queue of reviews. + * Once you've addressed the concerns, assign them as a reviewer again to put your pull request back in their queue. + +### If you are a core committer asked to give a review + +1. Respond promptly to requested reviews. + * Assume the requested review is urgent and blocking unless explicitly stated otherwise. + * Try to interact with the author within 2 business days. + * Configure the GitHub plugin to automate notifications. + * Review your outstanding requested reviews daily to avoid blocking authors. + * Prioritize earlier milestones when reviewing to help with the release process. + * Responding quickly doesn't necessarily mean reviewing quickly! Just don't leave the author hanging. +2. Feel free to clarify expectations with the author. + * If the code is experimental, they may need only a cursory glance and thumbs up to proceed with productizing their changes. + * If the review is large or complex, additional time may be required to complete your review. Be upfront with the author. + * If you are not comfortable reviewing the code, avoid "rubber stamping" the review. Be honest with the author and ask them to consider another core committer. +3. Never rush a review. + * Take the time necessary to review the code thoroughly. + * Don't be afraid to ask for changes repeatedly until all concerns are addressed. + * Feel free to challenge assumptions and timelines. Rushing a change into a patch release may cause more harm than good. +4. Avoid leaving a review hanging. + * Try to accept or reject the review instead of just leaving comments. +5. Merge the pull request. + * Do not merge if there are outstanding changes requested. + * Merge the pull request, and delete the branch if not from a fork. diff --git a/docs/contribution-checklist.md b/docs/contribution-checklist.md new file mode 100644 index 000000000..9e85e1370 --- /dev/null +++ b/docs/contribution-checklist.md @@ -0,0 +1,16 @@ +# Contribution Checklist + +Thanks for your interest in contributing code! + +Follow this checklist for submitting a pull request (PR): + +1. You've signed the [Contributor License Agreement](http://www.mattermost.org/mattermost-contributor-agreement/), so you can be added to the Mattermost [Approved Contributor List](https://docs.google.com/spreadsheets/d/1NTCeG-iL_VS9bFqtmHSfwETo5f-8MQ7oMDE5IUYJi_Y/pubhtml?gid=0&single=true). +2. Your ticket is a Help Wanted GitHub issue for the project you're contributing to. + - If not, follow the process [here](contributions-without-ticket). +3. Your code is thoroughly tested, including appropriate unit tests, and manual testing. +4. If applicable, user interface strings are included in the localization file ([en.json](https://github.com/mattermost/focalboard/blob/main/webapp/i18n/en.json)) + - In the webapp folder, run `npm run i18n-extract` to generate the new/updated strings. +5. The PR is submitted against the `main` branch from your fork. +6. The PR title begins with the GitHub Ticket ID (e.g. `[GH-394]`) and the summary template is filled out. + +Once submitted, the automated build process must pass in order for the PR to be accepted. Any errors or failures need to be addressed in order for the PR to be accepted. Next, the PR goes through [code review](code-review). To learn about the review process for each project, read the [CONTRIBUTING.md](https://github.com/mattermost/focalboard/blob/main/CONTRIBUTING.md) file of that GitHub repository. diff --git a/docs/contributions-without-ticket.md b/docs/contributions-without-ticket.md new file mode 100644 index 000000000..e61282b5c --- /dev/null +++ b/docs/contributions-without-ticket.md @@ -0,0 +1,15 @@ +# Contributions Without Ticket + +Contributions for minor corrections and improvements without a corresponding `Help Wanted` ticket are welcome. For example, a pull request for a bug or incremental improvement, with less than 20 lines of code change, is usually accepted if the change to existing behaviour is minor. + +All pull requests submitted without a corresponding ticket will first be reviewed by a core team product manager. Some examples of minor corrections and improvements include: + +- [Fix a formatting error in help text](https://github.com/mattermost/mattermost-server/pull/5640) +- [Fix success typo in Makefile](https://github.com/mattermost/mattermost-server/pull/5809) +- [Fix broken Cancel button in Edit Webhooks screen](https://github.com/mattermost/mattermost-server/pull/5612) +- [Fix Android app crashing when saving user notification settings](https://github.com/mattermost/mattermost-mobile/pull/364) +- [Fix recent mentions search not working](https://github.com/mattermost/mattermost-server/pull/5878) + +**Note:** For pull requests greater than 20 lines of code, a `Help Wanted` ticket should be opened by the core team. This helps ensure that everything going into the project aligns with a unified vision. Core committers who review the PR are entitled to reject it if there isn't a `Help Wanted` ticket and feel it significantly changes behavior or user expectations. + +The best way to discuss opening a `Help Wanted` ticket with the core team is by [starting a conversation in Contributors channel](https://community.mattermost.com/core/channels/focalboard) or [opening an issue in the GitHub repository](https://github.com/mattermost/focalboard/issues/new). diff --git a/docs/dev-tips.md b/docs/dev-tips.md new file mode 100644 index 000000000..d32d79a6f --- /dev/null +++ b/docs/dev-tips.md @@ -0,0 +1,96 @@ +# Developer Tips and Tricks + +## Installation prerequisites + +Check that you have recent versions of the basic dependencies installed: +* [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) + * On Windows, install [Git for Windows](https://gitforwindows.org/) and use the git-bash terminal shell +* [Go](https://golang.org/doc/install) +* [Node](https://nodejs.org/en/download/) (v10+) and [npm](https://www.npmjs.com/get-npm) + +On Windows: +* Install [Mingw64](https://chocolatey.org/packages/mingw) via [Chocolatey](https://chocolatey.org/) + +On macOS, to build the Mac app: +* Install [Xcode](https://apps.apple.com/us/app/xcode/id497799835?mt=12) (v12+) +* Install the Xcode commandline tools, via the IDE or run `xcode-select --install` + +On Linux, to build the Linux app: +* `sudo apt-get install libgtk-3-dev` +* `sudo apt-get install libwebkit2gtk-4.0-dev` +* `sudo apt-get install autoconf dh-autoreconf` + +## Clone the project source code + +Clone the [GitHub repo here](https://github.com/mattermost/focalboard). + +## Build and run from the terminal + +Follow the steps in the [main readme file](https://github.com/mattermost/focalboard#building-the-server). In summary, to build and run the server: + +``` +make prebuild +make + ./bin/focalboard-server +``` + +Then open a browser to `http://localhost:8000` to access it. The port is configured in `config.json`. + +Once the server is running, you can rebuild just the webapp with `make webapp` (in a separate terminal window), then reload the browser. + +## VSCode setup + +Here's a recommended dev-test loop using VSCode: +* Open a bash terminal window to the project folder +* Run `make prebuild` to npm install + * Do this again when dependencies change in `webapp/package.json` +* Run `cd webapp && npm run watchdev` + * This will auto-build the webapp on file changes +* Open VSCode + * Install the [Go](https://marketplace.visualstudio.com/items?itemName=golang.Go) and [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) VSCode extensions if you haven't already +* Hit F5 and select `Go: Launch Server` + * Or, press `Cmd+P` and type `debug ` and pick the option +* Open a browser to `http://localhost:8000` + * The port is configured in `config.json` + +You can now edit the webapp code and refresh the browser to see your changes. + +## Debugging the web app + +You can use your favorite browser to debug the webapp code. With Chrome, open the dev tools with `Cmd+Alt+I` (`Ctrl+Alt+I` in Windows). +* `npm run watchdev` builds the dev package, which includes source maps from js to typescript +* In the Chrome devtools source tab, press `Cmd+P` to jump to a source file + +As a starting point, add a breakpoint to the `render()` function in `BoardPage.tsx`, then refresh the browser to walk through page rendering. + +## Debugging the server + +Debug the Go code in VSCode. This is set up automatically when you launch the server from there. + +To start, add a breakpoint to `handleGetBlocks()` in `server/api/api.go`, then refresh the browser to see how data is retrieved. + +## Localization/Internationalization/Translation + +We use `i18n` to localize the web app. Localized string generally use `intl.formatMessage`. When adding or modifying localized strings, run `npm run i18n-extract` in `webapp` to rebuild `webapp/i18n/en.json`. + +Translated strings are stored in other json files under `webapp/i18n`, e.g. `es.json` for Spanish. + +## Database + +By default, data is stored in a sqlite database `focalboard.db`. You can view and edit this directly using `sqlite3 focalboard.db` from bash. + +## Unit tests + +Before checking-in commits, run: `make ci`, which is simlar to the ci.yml workflow and includes: +* Server unit tests: `make server-test` +* Webapp eslint: `cd webapp; npm run check` +* Webapp unit tests: `make webapp-test` +* Webapp UI tests: `cd webapp; npm run cypress:ci` + +## Running into problems or have questions? + +If you run into any issues with the steps here, or have any general questions, please don't hesitate to reach out either on [GitHub](https://github.com/mattermost/focalboard) or our [Mattermost community channel](https://community.mattermost.com/core/channels/focalboard). + +We welcome everyone, and appreciate any feedback. + +glhf! :) diff --git a/docs/index.md b/docs/index.md index 01c76ac6e..9975f9d57 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,37 +1,19 @@ -## Welcome to GitHub Pages +# Focalboard/Mattermost Boards Contributors Guide -You can use the [editor on GitHub](https://github.com/mattermost/focalboard/edit/main/docs/index.md) to maintain and preview the content for your website in Markdown files. +Welcome to the Focalboard/Mattermost Boards project! -Whenever you commit to this repository, GitHub Pages will run [Jekyll](https://jekyllrb.com/) to rebuild the pages in your site, from the content in your Markdown files. +We're very glad you want to check it out and perhaps contribute code our repository in GitHub. -### Markdown +Our goal is to make your experience as great as possible. Follow these simple steps to contribute: -Markdown is a lightweight and easy-to-use syntax for styling your writing. It includes conventions for +1. [Clone the project from GitHub](https://github.com/mattermost/focalboard) and follow the steps in the README to build. Read the [developer tips & tricks](dev-tips) to get started. -```markdown -Syntax highlighted code block +2. Find [help wanted tickets in GitHub](https://github.com/mattermost/focalboard/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22). Comment to let everyone know you’re working on it, and to allow a core contributor to assign the issue to you. If there’s no ticket for what you want to work on see [contributions without a ticket.](contributions-without-ticket). -# Header 1 -## Header 2 -### Header 3 +3. When your changes are ready, run through our [checklist for pull requests](contribution-checklist). Note that if it’s your first contribution, there is a standard [CLA](https://www.mattermost.org/mattermost-contributor-agreement/) to sign. -- Bulleted -- List +## Just ask if you need help! -1. Numbered -2. List +You can find us on our [public Focalboard channel](https://community.mattermost.com/core/channels/focalboard) on the Mattermost community server. Also feel free to [file a bug](https://github.com/mattermost/focalboard/issues/new/choose) for any issues you run into, or [start a discussion](https://github.com/mattermost/focalboard/discussions). -**Bold** and _Italic_ and `Code` text - -[Link](url) and ![Image](src) -``` - -For more details see [Basic writing and formatting syntax](https://docs.github.com/en/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax). - -### Jekyll Themes - -Your Pages site will use the layout and styles from the Jekyll theme you have selected in your [repository settings](https://github.com/mattermost/focalboard/settings/pages). The name of this theme is saved in the Jekyll `_config.yml` configuration file. - -### Support or Contact - -Having trouble with Pages? Check out our [documentation](https://docs.github.com/categories/github-pages-basics/) or [contact support](https://support.github.com/contact) and we’ll help you sort it out. +We're glad ❤️ you're here! Good luck and have fun! diff --git a/website/site/content/contribute/getting-started/dev-tips.md b/website/site/content/contribute/getting-started/dev-tips.md index cd4038f52..5df65626c 100644 --- a/website/site/content/contribute/getting-started/dev-tips.md +++ b/website/site/content/contribute/getting-started/dev-tips.md @@ -83,7 +83,7 @@ Translated strings are stored in other json files under `webapp/i18n`, e.g. `es. ## Database -By default, data is stored in a sqlite datebase `focalboard.db`. You can view and edit this directly using `sqlite3 focalboard.db` from bash. +By default, data is stored in a sqlite database `focalboard.db`. You can view and edit this directly using `sqlite3 focalboard.db` from bash. ## Unit tests From 1d7e789ac9806ad378a9ee17aa49ef805e8300e6 Mon Sep 17 00:00:00 2001 From: Chen-I Lim Date: Tue, 8 Feb 2022 14:05:17 -0800 Subject: [PATCH 07/21] GH-pages: Formatting and settings --- docs/_config.yml | 3 +++ docs/code-review.md | 6 +++--- docs/index.md | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/_config.yml b/docs/_config.yml index 3397c9a49..372ac3824 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1 +1,4 @@ +title: Focalboard Developers +show_downloads: true +google_analytics: UA-64458817-2 theme: jekyll-theme-architect \ No newline at end of file diff --git a/docs/code-review.md b/docs/code-review.md index baf55da32..b76271dce 100644 --- a/docs/code-review.md +++ b/docs/code-review.md @@ -2,7 +2,7 @@ Currently, all changes to the product must be reviewed by a [core committer](/contribute/getting-started/core-committers/#core-committers). -### If you are a community member seeking a review +## If you are a community member seeking a review 1. Submit your pull request (PR). * Follow the [contribution checklist](../contribution-checklist/). @@ -22,7 +22,7 @@ Currently, all changes to the product must be reviewed by a [core committer](/co * Larger pull requests may require more time to review. * Once all reviewers have approved your changes, they will handle merging your code. -### If you are awaiting a review +## If you are awaiting a review 1. Wait patiently for reviews to complete. * Expect some interaction with each of your reviewers within 5 business days. @@ -31,7 +31,7 @@ Currently, all changes to the product must be reviewed by a [core committer](/co * If a reviewer requests changes, your pull request will disappear from their queue of reviews. * Once you've addressed the concerns, assign them as a reviewer again to put your pull request back in their queue. -### If you are a core committer asked to give a review +## If you are a core committer asked to give a review 1. Respond promptly to requested reviews. * Assume the requested review is urgent and blocking unless explicitly stated otherwise. diff --git a/docs/index.md b/docs/index.md index 9975f9d57..479bf2805 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,6 @@ -# Focalboard/Mattermost Boards Contributors Guide +# Focalboard / Mattermost Boards Contributors Guide -Welcome to the Focalboard/Mattermost Boards project! +Welcome to the [Focalboard](https://www.focalboard.com) / [Mattermost Boards](https://mattermost.com/boards/?utm_source=focalboard) project! We're very glad you want to check it out and perhaps contribute code our repository in GitHub. From 9d12efa2a2577336c25cfc7a99e6bcc11f860106 Mon Sep 17 00:00:00 2001 From: Chen-I Lim Date: Tue, 8 Feb 2022 14:08:50 -0800 Subject: [PATCH 08/21] GH-pages: download settings --- docs/_config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/_config.yml b/docs/_config.yml index 372ac3824..74ecfc801 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,3 @@ title: Focalboard Developers -show_downloads: true google_analytics: UA-64458817-2 theme: jekyll-theme-architect \ No newline at end of file From 092cf2397249a0a887313d0146235826e24ace14 Mon Sep 17 00:00:00 2001 From: Scott Bishel Date: Wed, 9 Feb 2022 05:25:28 -0700 Subject: [PATCH 09/21] Growth share board modal (#2286) * Skeleton dialog * Updating share board modal * first changes * Separate button * Separate button * Skeleton dialog * Updating share board modal * first changes * Separate button * Separate button * add code to make it work, add telemetry, check config * update icons * add classes for personal server * some cleanup * increase padding * add unit tests * add unit tests * more cleanup * i18n extract and fix styles * remove old shareBoardComponent * remove old sharedBoardComponent * lint fixes * update additional snapshot tests * remove more code * more test cleanup Co-authored-by: Harshil Sharma Co-authored-by: Asaad Mahmood --- webapp/i18n/en.json | 8 +- .../__snapshots__/centerPanel.test.tsx.snap | 2830 +++++++++++------ .../shareBoardComponent.test.tsx.snap | 601 ---- .../__snapshots__/workspace.test.tsx.snap | 554 ++-- .../boardTemplateSelectorPreview.tsx | 1 - webapp/src/components/centerPanel.scss | 8 + webapp/src/components/centerPanel.test.tsx | 18 + webapp/src/components/centerPanel.tsx | 25 +- webapp/src/components/dialog.tsx | 4 +- .../globalHeaderSettingsMenu.test.tsx.snap | 6 +- .../__snapshots__/shareBoard.test.tsx.snap | 950 ++++++ .../shareBoardButton.test.tsx.snap | 22 + .../src/components/shareBoard/shareBoard.scss | 66 + .../shareBoard.test.tsx} | 201 +- .../src/components/shareBoard/shareBoard.tsx | 178 ++ .../shareBoard/shareBoardButton.scss | 20 + .../shareBoard/shareBoardButton.test.tsx | 30 + .../shareBoard/shareBoardButton.tsx | 52 + .../src/components/shareBoardComponent.scss | 36 - webapp/src/components/shareBoardComponent.tsx | 167 - .../sidebarSettingsMenu.test.tsx.snap | 8 +- .../__snapshots__/filterValue.test.tsx.snap | 2 +- .../viewHeaderActionsMenu.test.tsx.snap | 302 +- .../viewHeaderPropertiesMenu.test.tsx.snap | 22 +- .../components/viewHeader/viewHeader.test.tsx | 2 - .../src/components/viewHeader/viewHeader.tsx | 4 +- .../viewHeader/viewHeaderActionsMenu.test.tsx | 25 +- .../viewHeader/viewHeaderActionsMenu.tsx | 26 +- .../__snapshots__/centerContent.test.tsx.snap | 20 +- .../__snapshots__/dashboardPage.test.tsx.snap | 2 +- webapp/src/styles/main.scss | 4 + webapp/src/telemetry/telemetryClient.ts | 2 + webapp/src/widgets/switch.scss | 16 + webapp/src/widgets/switch.tsx | 8 +- 34 files changed, 3735 insertions(+), 2485 deletions(-) delete mode 100644 webapp/src/components/__snapshots__/shareBoardComponent.test.tsx.snap create mode 100644 webapp/src/components/shareBoard/__snapshots__/shareBoard.test.tsx.snap create mode 100644 webapp/src/components/shareBoard/__snapshots__/shareBoardButton.test.tsx.snap create mode 100644 webapp/src/components/shareBoard/shareBoard.scss rename webapp/src/components/{shareBoardComponent.test.tsx => shareBoard/shareBoard.test.tsx} (67%) create mode 100644 webapp/src/components/shareBoard/shareBoard.tsx create mode 100644 webapp/src/components/shareBoard/shareBoardButton.scss create mode 100644 webapp/src/components/shareBoard/shareBoardButton.test.tsx create mode 100644 webapp/src/components/shareBoard/shareBoardButton.tsx delete mode 100644 webapp/src/components/shareBoardComponent.scss delete mode 100644 webapp/src/components/shareBoardComponent.tsx diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json index af934a051..b260ef3ea 100644 --- a/webapp/i18n/en.json +++ b/webapp/i18n/en.json @@ -11,7 +11,7 @@ "BoardPage.syncFailed": "Board may be deleted or access revoked.", "BoardTemplateSelector.add-template": "New template", "BoardTemplateSelector.create-empty-board": "Create empty board", - "BoardTemplateSelector.delete-template": "Delete template", + "BoardTemplateSelector.delete-template": "Delete", "BoardTemplateSelector.description": "Choose a template to help you get started. Easily customize the template to fit your needs, or create an empty board to start from scratch.", "BoardTemplateSelector.edit-template": "Edit", "BoardTemplateSelector.plugin.no-content-description": "Add a board to the sidebar using any of the templates defined below or start from scratch.{lineBreak} Members of \"{workspaceName}\" will have access to boards created here.", @@ -81,6 +81,7 @@ "CardDialog.delete-confirmation-dialog-heading": "Confirm card delete!", "CardDialog.editing-template": "You're editing a template.", "CardDialog.nocard": "This card doesn't exist or is inaccessible.", + "CenterPanel.Share": "Share", "ColorOption.selectColor": "Select {color} Color", "Comment.delete": "Delete", "CommentsList.send": "Send", @@ -161,9 +162,13 @@ "RegistrationLink.description": "Share this link for others to create accounts:", "RegistrationLink.regenerateToken": "Regenerate token", "RegistrationLink.tokenRegenerated": "Registration link regenerated", + "ShareBoard.PublishDescription": "Publish and share a “read only” link with everyone on the web", + "ShareBoard.PublishTitle": "Publish to the web", + "ShareBoard.Title": "Share Board", "ShareBoard.confirmRegenerateToken": "This will invalidate previously shared links. Continue?", "ShareBoard.copiedLink": "Copied!", "ShareBoard.copyLink": "Copy link", + "ShareBoard.regenerate": "Regenerate token", "ShareBoard.regenerateToken": "Regenerate token", "ShareBoard.share": "Publish and share this board with anyone who has the link", "ShareBoard.tokenRegenrated": "Token regenerated", @@ -232,6 +237,7 @@ "ViewHeader.share-board": "Share board", "ViewHeader.sort": "Sort", "ViewHeader.untitled": "Untitled", + "ViewHeader.view-header-menu": "View header menu", "ViewHeader.view-menu": "View menu", "ViewTitle.hide-description": "hide description", "ViewTitle.pick-icon": "Pick icon", diff --git a/webapp/src/components/__snapshots__/centerPanel.test.tsx.snap b/webapp/src/components/__snapshots__/centerPanel.test.tsx.snap index 6eb6c21c6..7284ee18c 100644 --- a/webapp/src/components/__snapshots__/centerPanel.test.tsx.snap +++ b/webapp/src/components/__snapshots__/centerPanel.test.tsx.snap @@ -30,106 +30,110 @@ exports[`components/centerPanel return centerPanel and click on card to show car
- -
-
- + + + + hide description + +
- -
-
-
+ + i + +
+
+
+ +
+
+
+
+
+
@@ -139,6 +143,21 @@ exports[`components/centerPanel return centerPanel and click on card to show car
+
+ +
- -
-
- + + + + hide description + +
- -
-
-
+ + i + +
+
+
+ +
+
+
+
+
+
@@ -688,6 +711,21 @@ exports[`components/centerPanel return centerPanel and click on new card to edit
+
+ +
-
- -
- -
-
-
+ + i + +
+
+
+ +
+
+
+
+
+
@@ -1626,106 +1668,110 @@ exports[`components/centerPanel return centerPanel and press touch ctrl+d for on
- -
-
- + + + + hide description + +
- -
-
-
+ + i + +
+
+
+ +
+
+
+
+
+
@@ -1735,6 +1781,21 @@ exports[`components/centerPanel return centerPanel and press touch ctrl+d for on
+
+ +
- -
-
- + + + + hide description + +
- -
-
-
+ + i + +
+
+
+ +
+
+
+
+
+
@@ -2343,6 +2408,21 @@ exports[`components/centerPanel return centerPanel and press touch del for one c
+
+ +
- -
-
- + + + + hide description + +
- -
-
-
+ + i + +
+
+
+ +
+
+
+
+
+
@@ -2951,6 +3035,21 @@ exports[`components/centerPanel return centerPanel and press touch esc for one c
+
+ +
- -
-
- + + + + hide description + +
- -
-
-
+ + i + +
+
+
+ +
+
+
+
+
+
@@ -3559,6 +3662,21 @@ exports[`components/centerPanel return centerPanel and press touch esc for one c
+
+ +
- -
-
- + + + + hide description + +
- -
-
-
+ + i + +
+
+
+ +
+
+
+
+
+
@@ -4167,6 +4289,21 @@ exports[`components/centerPanel return centerPanel and press touch esc for two c
+
+ +
- -
-
- + + + + hide description + +
- -
-
-
+ + i + +
+
+
+ +
+
+
+
+
+
@@ -4775,6 +4916,21 @@ exports[`components/centerPanel return centerPanel and press touch esc for two c
+
+ +
- -
-
- + + + + hide description + +
- -
-
-
+ + i + +
+
+
+ +
+
+
+
+
+
@@ -5383,6 +5543,21 @@ exports[`components/centerPanel return centerPanel and press touch esc for two c
+
+ +
- -
-
- + + + + hide description + +
- -
-
-
+ + i + +
+
+
+ +
+
+
+
+
+
@@ -5991,6 +6170,21 @@ exports[`components/centerPanel return centerPanel and select one card and click
+
+ +
- -
-
- + + + + hide description + +
- -
-
-
+ + i + +
+
+
+ +
+
+
+
+
+
@@ -6599,6 +6797,21 @@ exports[`components/centerPanel return centerPanel and select one card and click
+
+ +
- -
-
- + + + + hide description + +
- -
-
-
+ + i + +
+
+
+ +
+
+
+
+
+
@@ -7207,6 +7424,21 @@ exports[`components/centerPanel should match snapshot for Gallery 1`] = `
+
+ +
+
+ +
+
+
+ +
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+ + +
+
+ +
+
+ +
+ + +
+ +
+
+
+
+ New +
+ +
+
+
+
+
+
+ + No name + + +
+ +
+
+ + +
+
+ + + +
+ +
+
+ + +
+
+ + + +
+ +
+
+ + +
+
+ +
+
+
+
+ +
+
+
- +
+ +
+
+
+
+
+`; + +exports[`components/centerPanel should match snapshot for Kanban, not shared 1`] = ` +
+
+
+ +
+
+ +
+
-
+ + i + +
+
+
+ +
+
+
+
+
+
@@ -7900,106 +8671,110 @@ exports[`components/centerPanel should match snapshot for Table 1`] = `
- -
-
- + + + + hide description + +
- -
-
-
+ + i + +
+
+
+ +
+
+
+
+
+
@@ -8009,6 +8784,21 @@ exports[`components/centerPanel should match snapshot for Table 1`] = `
+
+ +
- -`; - -exports[`src/components/shareBoardComponent return shareBoardComponent and click Regenerate token 1`] = ` -
- -`; - -exports[`src/components/shareBoardComponent return shareBoardComponent and click Switch 1`] = ` -
- -`; - -exports[`src/components/shareBoardComponent return shareBoardComponent and click Switch without sharing 1`] = ` -
- -`; - -exports[`src/components/shareBoardComponent should match snapshot 1`] = ` -
- -`; - -exports[`src/components/shareBoardComponent should match snapshot with sharing 1`] = ` -
- -`; - -exports[`src/components/shareBoardComponent should match snapshot with sharing and subpath 1`] = ` -
- -`; - -exports[`src/components/shareBoardComponent should match snapshot with sharing and without workspaceId 1`] = ` -
- -`; - -exports[`src/components/shareBoardComponent should match snapshot with sharing and without workspaceId and subpath 1`] = ` -
- -`; diff --git a/webapp/src/components/__snapshots__/workspace.test.tsx.snap b/webapp/src/components/__snapshots__/workspace.test.tsx.snap index c20b9fe69..841d0656d 100644 --- a/webapp/src/components/__snapshots__/workspace.test.tsx.snap +++ b/webapp/src/components/__snapshots__/workspace.test.tsx.snap @@ -250,106 +250,110 @@ exports[`src/components/workspace return workspace and showcard 1`] = `
- -
-
- + + + + hide description + +
- -
-
-
+ + i + +
+
+
+ +
+
+
+
+
+
@@ -359,6 +363,21 @@ exports[`src/components/workspace return workspace and showcard 1`] = `
+
+ +
-
- -
- -
-
-
+ + i + +
+
+
+ +
+
+
+
+
+
@@ -1451,106 +1474,110 @@ exports[`src/components/workspace should match snapshot 1`] = `
- -
-
- + + + + hide description + +
- -
-
-
+ + i + +
+
+
+ +
+
+
+
+
+
@@ -1560,6 +1587,21 @@ exports[`src/components/workspace should match snapshot 1`] = `
+
+ +
-
- -
- -
-
-
+ + i + +
+
+
+ +
+
+
+
+
+
diff --git a/webapp/src/components/boardTemplateSelector/boardTemplateSelectorPreview.tsx b/webapp/src/components/boardTemplateSelector/boardTemplateSelectorPreview.tsx index 96f2de13a..acfb25ad5 100644 --- a/webapp/src/components/boardTemplateSelector/boardTemplateSelectorPreview.tsx +++ b/webapp/src/components/boardTemplateSelector/boardTemplateSelectorPreview.tsx @@ -83,7 +83,6 @@ const BoardTemplateSelectorPreview = React.memo((props: Props) => { addCardTemplate={() => null} editCardTemplate={() => null} readonly={false} - showShared={false} />
} diff --git a/webapp/src/components/centerPanel.scss b/webapp/src/components/centerPanel.scss index 56fa05085..cfe3aa3fe 100644 --- a/webapp/src/components/centerPanel.scss +++ b/webapp/src/components/centerPanel.scss @@ -44,7 +44,15 @@ left: 0; background: rgb(var(--center-channel-bg-rgb)); z-index: 100; + + > .mid-head { + flex: 0 0 auto; + display: flex; + flex-direction: row; + justify-content: space-between; + } } + > div:nth-child(2) { padding: 0 0 0 1px; diff --git a/webapp/src/components/centerPanel.test.tsx b/webapp/src/components/centerPanel.test.tsx index bc4055c8a..0d900724e 100644 --- a/webapp/src/components/centerPanel.test.tsx +++ b/webapp/src/components/centerPanel.test.tsx @@ -110,6 +110,24 @@ describe('components/centerPanel', () => { activeView.fields.viewType = 'board' jest.clearAllMocks() }) + test('should match snapshot for Kanban, not shared', () => { + const {container} = render(wrapDNDIntl( + + + , + )) + expect(container).toMatchSnapshot() + }) test('should match snapshot for Kanban', () => { const {container} = render(wrapDNDIntl( diff --git a/webapp/src/components/centerPanel.tsx b/webapp/src/components/centerPanel.tsx index 81f38e9d3..1d1ee0305 100644 --- a/webapp/src/components/centerPanel.tsx +++ b/webapp/src/components/centerPanel.tsx @@ -20,11 +20,12 @@ import {UserSettings} from '../userSettings' import {addCard, addTemplate} from '../store/cards' import {updateView} from '../store/views' import {getVisibleAndHiddenGroups} from '../boardUtils' +import TelemetryClient, {TelemetryCategory, TelemetryActions} from '../../../webapp/src/telemetry/telemetryClient' + +import ShareBoardButton from './shareBoard/shareBoardButton' import './centerPanel.scss' -import TelemetryClient, {TelemetryCategory, TelemetryActions} from '../../../webapp/src/telemetry/telemetryClient' - import CardDialog from './cardDialog' import RootPortal from './rootPortal' import TopBar from './topBar' @@ -59,6 +60,7 @@ type Props = { type State = { selectedCardIds: string[] cardIdToFocusOnRender: string + showShareDialog: boolean } class CenterPanel extends React.Component { @@ -102,6 +104,7 @@ class CenterPanel extends React.Component { this.state = { selectedCardIds: [], cardIdToFocusOnRender: '', + showShareDialog: false, } } @@ -146,11 +149,18 @@ class CenterPanel extends React.Component {
- +
+ + {!this.props.readonly && this.props.showShared && + + } +
{ addCardTemplate={this.addCardTemplate} editCardTemplate={this.editCardTemplate} readonly={this.props.readonly} - showShared={this.props.showShared} />
diff --git a/webapp/src/components/dialog.tsx b/webapp/src/components/dialog.tsx index 42bbd2d93..151a2df4e 100644 --- a/webapp/src/components/dialog.tsx +++ b/webapp/src/components/dialog.tsx @@ -16,12 +16,13 @@ type Props = { toolbar?: React.ReactNode hideCloseButton?: boolean className?: string + title?: string onClose: () => void, } const Dialog = React.memo((props: Props) => { const {toolsMenu} = props - const {toolbar} = props + const {toolbar, title} = props const intl = useIntl() const closeDialogText = intl.formatMessage({ @@ -47,6 +48,7 @@ const Dialog = React.memo((props: Props) => { className='dialog' >
+ {title &&

{title}

} { !props.hideCloseButton &&
+
+
+ +
+
+`; + +exports[`src/components/shareBoard/shareBoard return shareBoard and click Copy link 2`] = ` +
+
+
+ +
+
+`; + +exports[`src/components/shareBoard/shareBoard return shareBoard and click Regenerate token 1`] = ` +
+
+
+ +
+
+`; + +exports[`src/components/shareBoard/shareBoard return shareBoard, and click switch 1`] = ` +
+
+
+ +
+
+`; + +exports[`src/components/shareBoard/shareBoard return shareBoardComponent and click Switch without sharing 1`] = ` +
+
+
+ +
+
+`; + +exports[`src/components/shareBoard/shareBoard should match snapshot 1`] = ` +
+
+
+ +
+
+`; + +exports[`src/components/shareBoard/shareBoard should match snapshot with sharing 1`] = ` +
+
+
+ +
+
+`; + +exports[`src/components/shareBoard/shareBoard should match snapshot with sharing and subpath 1`] = ` +
+
+
+ +
+
+`; + +exports[`src/components/shareBoard/shareBoard should match snapshot with sharing and without workspaceId and subpath 1`] = ` +
+
+
+ +
+
+`; diff --git a/webapp/src/components/shareBoard/__snapshots__/shareBoardButton.test.tsx.snap b/webapp/src/components/shareBoard/__snapshots__/shareBoardButton.test.tsx.snap new file mode 100644 index 000000000..8e2341beb --- /dev/null +++ b/webapp/src/components/shareBoard/__snapshots__/shareBoardButton.test.tsx.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`src/components/shareBoard/shareBoard should match snapshot 1`] = ` +
+
+ +
+
+`; diff --git a/webapp/src/components/shareBoard/shareBoard.scss b/webapp/src/components/shareBoard/shareBoard.scss new file mode 100644 index 000000000..50d8c3104 --- /dev/null +++ b/webapp/src/components/shareBoard/shareBoard.scss @@ -0,0 +1,66 @@ +.ShareBoardDialog { + .dialog { + @media not screen and (max-width: 975px) { + max-width: 600px; + height: auto; + } + } + + .tabs-modal { + padding: 24px; + border-radius: 8px; + background: rgba(var(--center-channel-bg-rgb), 1); + border-top: 1px solid rgba(var(--center-channel-color-rgb), 0.16); + + .d-flex { + display: flex; + } + + .justify-content-between { + justify-content: space-between; + } + + .flex-column { + flex-direction: column; + } + + .tabs-inputs { + margin-top: 16px; + + .input-container { + border: 1px solid rgba(var(--center-channel-color-rgb), 0.16); + border-radius: 4px; + width: 400px; + height: 40px; + } + + .IconButton { + height: 100%; + width: 40px; + } + + .icon-refresh { + margin: 4px; + font-size: 18px; + } + + .icon-content-copy { + margin-right: 4px; + font-size: 18px; + } + + .shareUrl { + flex-grow: 1; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin: auto 4px; + padding: 0 16px; + + :hover { + background-color: rgb(var(--center-channel-color-rgb), 0.1); + } + } + } + } +} diff --git a/webapp/src/components/shareBoardComponent.test.tsx b/webapp/src/components/shareBoard/shareBoard.test.tsx similarity index 67% rename from webapp/src/components/shareBoardComponent.test.tsx rename to webapp/src/components/shareBoard/shareBoard.test.tsx index 57b341a87..08a263065 100644 --- a/webapp/src/components/shareBoardComponent.test.tsx +++ b/webapp/src/components/shareBoard/shareBoard.test.tsx @@ -7,13 +7,13 @@ import React from 'react' import {MemoryRouter} from 'react-router' import {mocked} from 'ts-jest/utils' -import {ISharing} from '../blocks/sharing' -import {TestBlockFactory} from '../test/testBlockFactory' -import {wrapDNDIntl} from '../testUtils' -import client from '../octoClient' -import {Utils} from '../utils' +import {ISharing} from '../../blocks/sharing' +import {TestBlockFactory} from '../../test/testBlockFactory' +import {wrapDNDIntl} from '../../testUtils' +import client from '../../octoClient' +import {Utils} from '../../utils' -import ShareBoardComponent from './shareBoardComponent' +import ShareBoard from './shareBoard' jest.useFakeTimers() @@ -21,8 +21,8 @@ const boardId = '1' const workspaceId: string|undefined = boardId const viewId = boardId -jest.mock('../octoClient') -jest.mock('../utils') +jest.mock('../../octoClient') +jest.mock('../../utils') const mockedOctoClient = mocked(client, true) const mockedUtils = mocked(Utils, true) @@ -46,7 +46,7 @@ jest.mock('react-router', () => { const board = TestBlockFactory.createBoard() board.id = boardId -describe('src/components/shareBoardComponent', () => { +describe('src/components/shareBoard/shareBoard', () => { const w = (window as any) const oldBaseURL = w.baseURL @@ -69,15 +69,22 @@ describe('src/components/shareBoardComponent', () => { mockedOctoClient.getSharing.mockResolvedValue(undefined) let container await act(async () => { - const result = render(wrapDNDIntl( - ), {wrapper: MemoryRouter}) + const result = render( + wrapDNDIntl( + ), + {wrapper: MemoryRouter}, + ) container = result.container }) + expect(container).toMatchSnapshot() + const closeButton = screen.getByRole('button', {name: 'Close dialog'}) + expect(closeButton).toBeDefined() }) + test('should match snapshot with sharing', async () => { const sharing:ISharing = { id: boardId, @@ -85,62 +92,64 @@ describe('src/components/shareBoardComponent', () => { token: 'oneToken', } mockedOctoClient.getSharing.mockResolvedValue(sharing) + let container await act(async () => { - const result = render(wrapDNDIntl( - ), {wrapper: MemoryRouter}) - container = result.container - }) - expect(container).toMatchSnapshot() - }) - test('should match snapshot with sharing and without workspaceId', async () => { - const sharing:ISharing = { - id: boardId, - enabled: true, - token: 'oneToken', - } - params = { - boardId, - viewId, - } - mockedOctoClient.getSharing.mockResolvedValue(sharing) - let container - await act(async () => { - const result = render(wrapDNDIntl( - ), {wrapper: MemoryRouter}) - container = result.container - }) - expect(container).toMatchSnapshot() - }) - test('return shareBoardComponent and click Copy link', async () => { - const sharing:ISharing = { - id: boardId, - enabled: true, - token: 'oneToken', - } - mockedOctoClient.getSharing.mockResolvedValue(sharing) - let container: Element | undefined - await act(async () => { - const result = render(wrapDNDIntl( - ), {wrapper: MemoryRouter}) + const result = render( + wrapDNDIntl( + ), + {wrapper: MemoryRouter}, + ) container = result.container }) const copyLinkElement = screen.getByRole('button', {name: 'Copy link'}) expect(copyLinkElement).toBeDefined() - userEvent.click(copyLinkElement) - expect(mockedUtils.copyTextToClipboard).toBeCalledTimes(1) + expect(container).toMatchSnapshot() }) - test('return shareBoardComponent and click Regenerate token', async () => { + + test('return shareBoard and click Copy link', async () => { + const sharing:ISharing = { + id: boardId, + enabled: true, + token: 'oneToken', + } + mockedOctoClient.getSharing.mockResolvedValue(sharing) + + let container + await act(async () => { + const result = render( + wrapDNDIntl( + ), + {wrapper: MemoryRouter}, + ) + container = result.container + }) + + expect(container).toMatchSnapshot() + + const copyLinkElement = screen.getByRole('button', {name: 'Copy link'}) + expect(copyLinkElement).toBeDefined() + + await act(async () => { + userEvent.click(copyLinkElement!) + }) + + expect(mockedUtils.copyTextToClipboard).toBeCalledTimes(1) + expect(container).toMatchSnapshot() + + const copiedLinkElement = screen.getByRole('button', {name: 'Copy link'}) + expect(copiedLinkElement).toBeDefined() + expect(copiedLinkElement.textContent).toContain('Copied!') + }) + + test('return shareBoard and click Regenerate token', async () => { window.confirm = jest.fn(() => { return true }) @@ -150,15 +159,20 @@ describe('src/components/shareBoardComponent', () => { token: 'oneToken', } mockedOctoClient.getSharing.mockResolvedValue(sharing) - let container: Element | undefined + + let container await act(async () => { - const result = render(wrapDNDIntl( - ), {wrapper: MemoryRouter}) + const result = render( + wrapDNDIntl( + ), + {wrapper: MemoryRouter}, + ) container = result.container }) + sharing.token = 'anotherToken' mockedUtils.createGuid.mockReturnValue('anotherToken') mockedOctoClient.getSharing.mockResolvedValue(sharing) @@ -172,7 +186,7 @@ describe('src/components/shareBoardComponent', () => { expect(mockedOctoClient.setSharing).toBeCalledTimes(1) expect(container).toMatchSnapshot() }) - test('return shareBoardComponent and click Switch', async () => { + test('return shareBoard, and click switch', async () => { const sharing:ISharing = { id: boardId, enabled: true, @@ -181,18 +195,24 @@ describe('src/components/shareBoardComponent', () => { mockedOctoClient.getSharing.mockResolvedValue(sharing) let container: Element | undefined await act(async () => { - const result = render(wrapDNDIntl( - ), {wrapper: MemoryRouter}) + const result = render( + wrapDNDIntl( + ), + {wrapper: MemoryRouter}, + ) container = result.container }) const switchElement = container?.querySelector('.Switch') expect(switchElement).toBeDefined() - userEvent.click(switchElement!) + await act(async () => { + userEvent.click(switchElement!) + }) + expect(mockedOctoClient.setSharing).toBeCalledTimes(1) - expect(mockedOctoClient.getSharing).toBeCalledTimes(1) + expect(mockedOctoClient.getSharing).toBeCalledTimes(2) expect(container).toMatchSnapshot() }) test('return shareBoardComponent and click Switch without sharing', async () => { @@ -200,11 +220,14 @@ describe('src/components/shareBoardComponent', () => { mockedUtils.createGuid.mockReturnValue('aToken') let container: Element | undefined await act(async () => { - const result = render(wrapDNDIntl( - ), {wrapper: MemoryRouter}) + const result = render( + wrapDNDIntl( + ), + {wrapper: MemoryRouter}, + ) container = result.container mockedOctoClient.getSharing.mockResolvedValue({ id: boardId, @@ -215,11 +238,12 @@ describe('src/components/shareBoardComponent', () => { expect(switchElement).toBeDefined() userEvent.click(switchElement!) jest.runOnlyPendingTimers() - result.rerender(wrapDNDIntl( - )) + result.rerender( + wrapDNDIntl( + )) }) expect(mockedOctoClient.setSharing).toBeCalledTimes(1) @@ -227,7 +251,6 @@ describe('src/components/shareBoardComponent', () => { expect(mockedUtils.createGuid).toBeCalledTimes(1) expect(container).toMatchSnapshot() }) - test('should match snapshot with sharing and without workspaceId and subpath', async () => { w.baseURL = '/test-subpath/plugins/boards' const sharing:ISharing = { @@ -243,7 +266,7 @@ describe('src/components/shareBoardComponent', () => { let container await act(async () => { const result = render(wrapDNDIntl( - ), {wrapper: MemoryRouter}) @@ -263,7 +286,7 @@ describe('src/components/shareBoardComponent', () => { let container await act(async () => { const result = render(wrapDNDIntl( - ), {wrapper: MemoryRouter}) diff --git a/webapp/src/components/shareBoard/shareBoard.tsx b/webapp/src/components/shareBoard/shareBoard.tsx new file mode 100644 index 000000000..dccb063b1 --- /dev/null +++ b/webapp/src/components/shareBoard/shareBoard.tsx @@ -0,0 +1,178 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React, {useState, useEffect} from 'react' +import {useIntl, FormattedMessage} from 'react-intl' +import {generatePath, useRouteMatch} from 'react-router' + +import {Utils, IDType} from '../../utils' +import Tooltip from '../../widgets/tooltip' + +import {ISharing} from '../../blocks/sharing' + +import client from '../../octoClient' +import Dialog from '../dialog' +import Switch from '../../widgets/switch' +import Button from '../../widgets/buttons/button' +import {sendFlashMessage} from '../flashMessages' + +import TelemetryClient, {TelemetryActions, TelemetryCategory} from '../../telemetry/telemetryClient' + +import CompassIcon from '../../widgets/icons/compassIcon' +import IconButton from '../../widgets/buttons/iconButton' +import './shareBoard.scss' + +type Props = { + boardId: string + onClose: () => void +} + +export default function ShareBoardDialog(props: Props): JSX.Element { + const [wasCopied, setWasCopied] = useState(false) + const [sharing, setSharing] = useState(undefined) + + const intl = useIntl() + const match = useRouteMatch<{workspaceId?: string, boardId: string, viewId: string}>() + + const loadData = async () => { + const newSharing = await client.getSharing(props.boardId) + setSharing(newSharing) + setWasCopied(false) + } + + const createSharingInfo = () => { + const newSharing: ISharing = { + id: props.boardId, + enabled: true, + token: Utils.createGuid(IDType.Token), + } + return newSharing + } + + const onShareChanged = async (isOn: boolean) => { + const newSharing: ISharing = sharing || createSharingInfo() + newSharing.id = props.boardId + newSharing.enabled = isOn + TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.ShareBoard, {board: props.boardId, shareBoardEnabled: isOn}) + await client.setSharing(newSharing) + await loadData() + } + + const onRegenerateToken = async () => { + // eslint-disable-next-line no-alert + const accept = window.confirm(intl.formatMessage({id: 'ShareBoard.confirmRegenerateToken', defaultMessage: 'This will invalidate previously shared links. Continue?'})) + if (accept) { + const newSharing: ISharing = sharing || createSharingInfo() + newSharing.token = Utils.createGuid(IDType.Token) + await client.setSharing(newSharing) + await loadData() + + const description = intl.formatMessage({id: 'ShareBoard.tokenRegenrated', defaultMessage: 'Token regenerated'}) + sendFlashMessage({content: description, severity: 'low'}) + } + } + + useEffect(() => { + loadData() + }, []) + + const isSharing = Boolean(sharing && sharing.id === props.boardId && sharing.enabled) + const readToken = (sharing && isSharing) ? sharing.token : '' + const shareUrl = new URL(window.location.toString()) + shareUrl.searchParams.set('r', readToken) + + if (match.params.workspaceId) { + const newPath = generatePath('/workspace/:workspaceId/shared/:boardId/:viewId', { + boardId: match.params.boardId, + viewId: match.params.viewId, + workspaceId: match.params.workspaceId, + }) + shareUrl.pathname = Utils.buildURL(newPath) + } else { + const newPath = generatePath('/shared/:boardId/:viewId', { + boardId: match.params.boardId, + viewId: match.params.viewId, + }) + shareUrl.pathname = Utils.buildURL(newPath) + } + + return ( + +
+
+
+
+
{intl.formatMessage({id: 'ShareBoard.PublishTitle', defaultMessage: 'Publish to the web'})}
+
{intl.formatMessage({id: 'ShareBoard.PublishDescription', defaultMessage: 'Publish and share a “read only” link with everyone on the web'})}
+
+
+ +
+
+
+ {isSharing && + (
+
+ + {shareUrl.toString()} + + + } + title={intl.formatMessage({id: 'ShareBoard.regenerate', defaultMessage: 'Regenerate token'})} + className='IconButton--large' + /> + +
+ +
) + } +
+
+ ) +} diff --git a/webapp/src/components/shareBoard/shareBoardButton.scss b/webapp/src/components/shareBoard/shareBoardButton.scss new file mode 100644 index 000000000..f5c429d4d --- /dev/null +++ b/webapp/src/components/shareBoard/shareBoardButton.scss @@ -0,0 +1,20 @@ +.ShareBoardButton { + flex: 0 0 auto; + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 8px; + + > .Button { + margin-top: 36px; + padding: 3px 10px; + background-color: rgb(var(--button-bg-rgb)); + color: rgb(var(--button-color-rgb)); + border-radius: 5px; + + &:hover { + background-color: rgba(var(--button-bg-rgb), 0.8); + } + } +} + diff --git a/webapp/src/components/shareBoard/shareBoardButton.test.tsx b/webapp/src/components/shareBoard/shareBoardButton.test.tsx new file mode 100644 index 000000000..fb3027018 --- /dev/null +++ b/webapp/src/components/shareBoard/shareBoardButton.test.tsx @@ -0,0 +1,30 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +import {render} from '@testing-library/react' +import React from 'react' + +import {TestBlockFactory} from '../../test/testBlockFactory' +import {wrapDNDIntl} from '../../testUtils' + +import ShareBoardButton from './shareBoardButton' + +jest.useFakeTimers() + +const boardId = '1' + +const board = TestBlockFactory.createBoard() +board.id = boardId + +describe('src/components/shareBoard/shareBoard', () => { + test('should match snapshot', async () => { + const result = render( + wrapDNDIntl( + )) + + const renderer = result.container + + expect(renderer).toMatchSnapshot() + }) +}) diff --git a/webapp/src/components/shareBoard/shareBoardButton.tsx b/webapp/src/components/shareBoard/shareBoardButton.tsx new file mode 100644 index 000000000..4e7ec1475 --- /dev/null +++ b/webapp/src/components/shareBoard/shareBoardButton.tsx @@ -0,0 +1,52 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React, {useState} from 'react' +import {FormattedMessage} from 'react-intl' + +import Button from '../../widgets/buttons/button' +import TelemetryClient, {TelemetryActions, TelemetryCategory} from '../../telemetry/telemetryClient' + +import CompassIcon from '../../widgets/icons/compassIcon' + +import './shareBoardButton.scss' + +import ShareBoardDialog from './shareBoard' + +type Props = { + boardId: string +} +const ShareBoardButton = React.memo((props: Props) => { + const [showShareDialog, setShowShareDialog] = useState(false) + + return ( +
+ + {showShareDialog && + setShowShareDialog(false)} + boardId={props.boardId} + /> + } +
+ ) +}) + +export default ShareBoardButton diff --git a/webapp/src/components/shareBoardComponent.scss b/webapp/src/components/shareBoardComponent.scss deleted file mode 100644 index 64d0bef21..000000000 --- a/webapp/src/components/shareBoardComponent.scss +++ /dev/null @@ -1,36 +0,0 @@ -.ShareBoardComponent { - display: flex; - flex-direction: column; - padding: 5px; - color: rgb(var(--center-channel-color-rgb)); - max-width: 500px; - white-space: normal; - - .Switch { - margin-left: 8px; - } - - > .row { - display: flex; - flex-direction: row; - align-items: center; - margin: 0 0 10px; - } - - > .row:last-child { - margin-bottom: 0; - } - - .spacer { - flex-grow: 1; - } - - a.shareUrl { - max-width: 320px; - flex-grow: 1; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - margin-right: 5px; - } -} diff --git a/webapp/src/components/shareBoardComponent.tsx b/webapp/src/components/shareBoardComponent.tsx deleted file mode 100644 index 474eef985..000000000 --- a/webapp/src/components/shareBoardComponent.tsx +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. -import React, {useState, useEffect} from 'react' -import {FormattedMessage, useIntl} from 'react-intl' -import {generatePath, useRouteMatch} from 'react-router' - -import {ISharing} from '../blocks/sharing' - -import client from '../octoClient' - -import {Utils, IDType} from '../utils' -import {sendFlashMessage} from '../components/flashMessages' - -import Button from '../widgets/buttons/button' -import Switch from '../widgets/switch' - -import TelemetryClient, {TelemetryActions, TelemetryCategory} from '../telemetry/telemetryClient' - -import Modal from './modal' -import './shareBoardComponent.scss' - -type Props = { - boardId: string - onClose: () => void -} - -const ShareBoardComponent = React.memo((props: Props): JSX.Element => { - const [wasCopied, setWasCopied] = useState(false) - const [sharing, setSharing] = useState(undefined) - const intl = useIntl() - const match = useRouteMatch<{workspaceId?: string, boardId: string, viewId: string}>() - - const loadData = async () => { - const newSharing = await client.getSharing(props.boardId) - setSharing(newSharing) - setWasCopied(false) - } - - const createSharingInfo = () => { - const newSharing: ISharing = { - id: props.boardId, - enabled: true, - token: Utils.createGuid(IDType.Token), - } - return newSharing - } - - const onShareChanged = async (isOn: boolean) => { - const newSharing: ISharing = sharing || createSharingInfo() - newSharing.id = props.boardId - newSharing.enabled = isOn - TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.ShareBoard, {board: props.boardId, shareBoardEnabled: isOn}) - await client.setSharing(newSharing) - await loadData() - } - - const onRegenerateToken = async () => { - // eslint-disable-next-line no-alert - const accept = window.confirm(intl.formatMessage({id: 'ShareBoard.confirmRegenerateToken', defaultMessage: 'This will invalidate previously shared links. Continue?'})) - if (accept) { - const newSharing: ISharing = sharing || createSharingInfo() - newSharing.token = Utils.createGuid(IDType.Token) - await client.setSharing(newSharing) - await loadData() - - const description = intl.formatMessage({id: 'ShareBoard.tokenRegenrated', defaultMessage: 'Token regenerated'}) - sendFlashMessage({content: description, severity: 'low'}) - } - } - - useEffect(() => { - loadData() - }, []) - - const isSharing = Boolean(sharing && sharing.id === props.boardId && sharing.enabled) - const readToken = (sharing && isSharing) ? sharing.token : '' - - const shareUrl = new URL(window.location.toString()) - shareUrl.searchParams.set('r', readToken) - - if (match.params.workspaceId) { - const newPath = generatePath('/workspace/:workspaceId/shared/:boardId/:viewId', { - boardId: match.params.boardId, - viewId: match.params.viewId, - workspaceId: match.params.workspaceId, - }) - shareUrl.pathname = Utils.buildURL(newPath) - } else { - const newPath = generatePath('/shared/:boardId/:viewId', { - boardId: match.params.boardId, - viewId: match.params.viewId, - }) - shareUrl.pathname = Utils.buildURL(newPath) - } - - return ( - -
-
-
- {isSharing && - } - {!isSharing && - } -
- -
- {isSharing && <> -
- - {shareUrl.toString()} - - -
-
- -
- } -
-
- ) -}) - -export default ShareBoardComponent diff --git a/webapp/src/components/sidebar/__snapshots__/sidebarSettingsMenu.test.tsx.snap b/webapp/src/components/sidebar/__snapshots__/sidebarSettingsMenu.test.tsx.snap index 53c0da2b1..5c2fb9bba 100644 --- a/webapp/src/components/sidebar/__snapshots__/sidebarSettingsMenu.test.tsx.snap +++ b/webapp/src/components/sidebar/__snapshots__/sidebarSettingsMenu.test.tsx.snap @@ -259,7 +259,7 @@ exports[`components/sidebar/SidebarSettingsMenu imports menu open should match s Random icons
+
+ +
+
+`; + +exports[`components/viewHeader/viewHeaderActionsMenu return menu and verify call to board archive 1`] = ` +
+
+