From 63083480b86ac498f7aa83d3e81bf9adb333c774 Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Thu, 25 Jun 2020 14:07:14 -0400 Subject: [PATCH 01/46] WIP --- components/channel_view/channel_view.jsx | 7 ++ components/channel_view/index.js | 1 + components/next_steps_view/index.ts | 26 +++++++ .../next_steps_view/next_steps_view.scss | 23 +++++++ .../next_steps_view/next_steps_view.tsx | 69 +++++++++++++++++++ i18n/en.json | 3 + utils/constants.jsx | 8 +++ 7 files changed, 137 insertions(+) create mode 100644 components/next_steps_view/index.ts create mode 100644 components/next_steps_view/next_steps_view.scss create mode 100644 components/next_steps_view/next_steps_view.tsx diff --git a/components/channel_view/channel_view.jsx b/components/channel_view/channel_view.jsx index f07027a0a78f..be5432490f83 100644 --- a/components/channel_view/channel_view.jsx +++ b/components/channel_view/channel_view.jsx @@ -10,6 +10,7 @@ import deferComponentRender from 'components/deferComponentRender'; import ChannelHeader from 'components/channel_header'; import CreatePost from 'components/create_post'; import FileUploadOverlay from 'components/file_upload_overlay'; +import NextStepsView from 'components/next_steps_view'; import PostView from 'components/post_view'; import TutorialView from 'components/tutorial'; import {clearMarks, mark, measure, trackEvent} from 'actions/diagnostics_actions.jsx'; @@ -123,6 +124,12 @@ export default class ChannelView extends React.PureComponent { ); } + if (this.props.showNextSteps) { + return ( + + ); + } + let createPost; if (this.props.deactivatedChannel) { createPost = ( diff --git a/components/channel_view/index.js b/components/channel_view/index.js index 8a3d6d153233..b5969d26f08c 100644 --- a/components/channel_view/index.js +++ b/components/channel_view/index.js @@ -57,6 +57,7 @@ function mapStateToProps(state) { channelRolesLoading, deactivatedChannel: channel ? getDeactivatedChannel(state, channel.id) : false, showTutorial: enableTutorial && tutorialStep <= TutorialSteps.INTRO_SCREENS, + showNextSteps: true, channelIsArchived: channel ? channel.delete_at !== 0 : false, viewArchivedChannels, }; diff --git a/components/next_steps_view/index.ts b/components/next_steps_view/index.ts new file mode 100644 index 000000000000..97f311cc9641 --- /dev/null +++ b/components/next_steps_view/index.ts @@ -0,0 +1,26 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {connect} from 'react-redux'; +import {bindActionCreators, Dispatch} from 'redux'; + +import {GlobalState} from 'types/store'; + +import NextStepsView from './next_steps_view'; + +function mapStateToProps(state: GlobalState) { + return { + }; +} + +function mapDispatchToProps(dispatch: Dispatch) { + return { + actions: bindActionCreators({ + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(NextStepsView); diff --git a/components/next_steps_view/next_steps_view.scss b/components/next_steps_view/next_steps_view.scss new file mode 100644 index 000000000000..f60f3a0888d2 --- /dev/null +++ b/components/next_steps_view/next_steps_view.scss @@ -0,0 +1,23 @@ +.NextStepsView__header { + +} + +.NextStepsView__header-headerText { + +} + +.NextStepsView__header-headerTopText { + +} + +.NextStepsView__header-headerBottomText { + +} + +.NextStepsView__header-logo { + +} + +.NextStepsView__body { + +} \ No newline at end of file diff --git a/components/next_steps_view/next_steps_view.tsx b/components/next_steps_view/next_steps_view.tsx new file mode 100644 index 000000000000..fdad00a8b512 --- /dev/null +++ b/components/next_steps_view/next_steps_view.tsx @@ -0,0 +1,69 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from "react"; +import {FormattedMessage} from "react-intl"; + +import './next_steps_view.scss' +import { Form } from "react-bootstrap/lib/Navbar"; + + +type Props = { + +}; + +type State = { + +} + +export default class NextStepsView extends React.PureComponent { + + getBottomText = () => { + // lmao + const {isFinished} = {isFinished: false}; + + if (isFinished) { + return ( + + ); + } + + return ( + + ) + } + + render() { + return ( +
+
+
+
+ +
+
+ {this.getBottomText()} +
+
+
+ +
+
+
+
+
+ ); + } +} \ No newline at end of file diff --git a/i18n/en.json b/i18n/en.json index 6a6e39d54ff9..7d4a0b694de0 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -3053,6 +3053,9 @@ "navbar.toggle2": "Toggle sidebar", "navbar.viewInfo": "View Info", "navbar.viewPinnedPosts": "View Pinned Posts", + "next_steps_view.allSetToGo": "You're all set to go!", + "next_steps_view.hereAreSomeNextSteps": "Here are some recommended next steps to help you collaborate", + "next_steps_view.welcomeToMattermost": "Welcome to Mattermost", "no_results.channel_search.subtitle": "Check the spelling or try another search.", "no_results.channel_search.title": "No results for {channelName}", "no_results.flagged_posts.subtitle": "Flagged messages are only visible to you. Use flags to mark messages for follow-up or save something for later by clicking the {icon} to save them here.", diff --git a/utils/constants.jsx b/utils/constants.jsx index 8fa508ef1700..3275315bca09 100644 --- a/utils/constants.jsx +++ b/utils/constants.jsx @@ -98,6 +98,7 @@ export const Preferences = { NAME_NAME_FORMAT: 'name_format', CATEGORY_SYSTEM_NOTICE: 'system_notice', TEAMS_ORDER: 'teams_order', + RECOMMENDED_NEXT_STEPS: 'recommended_next_steps', }; export const ActionTypes = keyMirror({ @@ -349,6 +350,13 @@ export const TutorialSteps = { FINISHED: 999, }; +export const RecommendedNextSteps = { + COMPLETE_PROFILE: 'complete_profile', + TEAM_SETUP: 'team_setup', + INVITE_MEMBERS: 'invite_members', + HIDE: 'hide', +}; + export const PostTypes = { JOIN_LEAVE: 'system_join_leave', JOIN_CHANNEL: 'system_join_channel', From a2450a8da57fcec449ea5f917f556f0545e65ad7 Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Tue, 30 Jun 2020 09:05:46 -0400 Subject: [PATCH 02/46] WIP --- .../next_steps_view/next_steps_view.scss | 55 ++++++++++-- .../next_steps_view/next_steps_view.tsx | 14 ++- images/getting-started.svg | 88 +++++++++++++++++++ images/waves.svg | 21 +++++ 4 files changed, 165 insertions(+), 13 deletions(-) create mode 100644 images/getting-started.svg create mode 100644 images/waves.svg diff --git a/components/next_steps_view/next_steps_view.scss b/components/next_steps_view/next_steps_view.scss index f60f3a0888d2..c4ff7bf3ed05 100644 --- a/components/next_steps_view/next_steps_view.scss +++ b/components/next_steps_view/next_steps_view.scss @@ -1,5 +1,6 @@ .NextStepsView__header { - + padding: 32px 40px; + display: flex; } .NextStepsView__header-headerText { @@ -7,17 +8,61 @@ } .NextStepsView__header-headerTopText { - + font-weight: 600; + font-size: 32px; + line-height: 40px; + letter-spacing: -0.02em; } .NextStepsView__header-headerBottomText { - + font-size: 16px; + line-height: 24px; + margin-top: 8px; } .NextStepsView__header-logo { + margin-left: auto; + // TODO DEVIN: temp + width: 272px; + height: 100%; + background-color: red; } .NextStepsView__body { - -} \ No newline at end of file + display: flex; + background-image: url(../../images/waves.svg); + background-repeat: no-repeat; + background-position-y: bottom; + background-position-x: -305px; + background-size: calc(100% + 305px); + height: 100%; +} + +.NextStepsView__body-main { + flex: 3 0 75%; + + &:after { + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOmNjPSJodHRwOi8vd2ViLnJlc291cmNlLm9yZy9jYy8iIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIiB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIgdmVyc2lvbj0iMS4xIiBiYXNlUHJvZmlsZT0iZnVsbCIgd2lkdGg9IjhweCIgaGVpZ2h0PSI4cHgiIHZpZXdCb3g9IjAgMCA4IDgiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIG1lZXQiIGlkPSJzdmdfZG9jdW1lbnQiIHN0eWxlPSJ6b29tOiAxOyI+CiAgICA8dGl0bGUgaWQ9InN2Z19kb2N1bWVudF90aXRsZSI+VW50aXRsZWQuc3ZnPC90aXRsZT4KICAgIDxkZWZzIGlkPSJzdmdfZG9jdW1lbnRfZGVmcyI+PC9kZWZzPgogICAgPGcgaWQ9Im1haW5fZ3JvdXAiPgogICAgICAgIDxyZWN0IGlkPSJiYWNrZ3JvdW5kX3JlY3QiIGZpbGw9IiMxNjZERTAiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iMXB4IiBoZWlnaHQ9IjFweCI+PC9yZWN0PgogICAgPC9nPgo8L3N2Zz4=) repeat; + background-size: 30px 30px; + opacity: 0.16; + transform: matrix(0.71, 0.64, -0.78, 0.71, 0, 0); + display: block; + content: ""; + + height: 50vh; + width: 50vh; + position: absolute; + left: 83%; + bottom: 3%; + } +} + +.NextStepsView__body-graphic { + background-image: url(../../images/getting-started.svg); + background-repeat: no-repeat; + background-size: 120%; + background-position-y: 33%; + flex: 1 0 25%; + z-index: 2; +} diff --git a/components/next_steps_view/next_steps_view.tsx b/components/next_steps_view/next_steps_view.tsx index fdad00a8b512..c970d02aa150 100644 --- a/components/next_steps_view/next_steps_view.tsx +++ b/components/next_steps_view/next_steps_view.tsx @@ -5,16 +5,10 @@ import React from "react"; import {FormattedMessage} from "react-intl"; import './next_steps_view.scss' -import { Form } from "react-bootstrap/lib/Navbar"; - type Props = { - -}; -type State = { - -} +}; export default class NextStepsView extends React.PureComponent { @@ -62,8 +56,12 @@ export default class NextStepsView extends React.PureComponent {
+
+ +
+
); } -} \ No newline at end of file +} diff --git a/images/getting-started.svg b/images/getting-started.svg new file mode 100644 index 000000000000..e406c900bd18 --- /dev/null +++ b/images/getting-started.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/waves.svg b/images/waves.svg new file mode 100644 index 000000000000..3483369b094d --- /dev/null +++ b/images/waves.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + From 6f376e3dd249528bba6dc7224f79e05f2e301949 Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Tue, 30 Jun 2020 10:25:54 -0400 Subject: [PATCH 03/46] [MM-26465] Background and general layout for cloud onboarding --- components/next_steps_view/index.ts | 5 + .../next_steps_view/next_steps_view.scss | 36 ++--- .../next_steps_view/next_steps_view.tsx | 19 ++- images/cloud-logos/professional.svg | 22 +++ images/onboarding-bg.svg | 143 ++++++++++++++++++ images/waves.svg | 21 --- 6 files changed, 194 insertions(+), 52 deletions(-) create mode 100644 images/cloud-logos/professional.svg create mode 100644 images/onboarding-bg.svg delete mode 100644 images/waves.svg diff --git a/components/next_steps_view/index.ts b/components/next_steps_view/index.ts index 97f311cc9641..1271c79bdff2 100644 --- a/components/next_steps_view/index.ts +++ b/components/next_steps_view/index.ts @@ -7,12 +7,17 @@ import {connect} from 'react-redux'; import {bindActionCreators, Dispatch} from 'redux'; +import {getLicense} from 'mattermost-redux/selectors/entities/general'; + import {GlobalState} from 'types/store'; import NextStepsView from './next_steps_view'; function mapStateToProps(state: GlobalState) { + const license = getLicense(state); + return { + skuName: license.SkuShortName, }; } diff --git a/components/next_steps_view/next_steps_view.scss b/components/next_steps_view/next_steps_view.scss index c4ff7bf3ed05..dafc3e4738ee 100644 --- a/components/next_steps_view/next_steps_view.scss +++ b/components/next_steps_view/next_steps_view.scss @@ -1,3 +1,11 @@ +#app-content.NextStepsView { + background-image: url(../../images/onboarding-bg.svg); + background-repeat: no-repeat; + background-position-y: bottom; + background-position-x: -305px; + background-size: calc(100% + 305px); +} + .NextStepsView__header { padding: 32px 40px; display: flex; @@ -22,40 +30,16 @@ .NextStepsView__header-logo { margin-left: auto; - - // TODO DEVIN: temp - width: 272px; - height: 100%; - background-color: red; + align-self: center; } .NextStepsView__body { display: flex; - background-image: url(../../images/waves.svg); - background-repeat: no-repeat; - background-position-y: bottom; - background-position-x: -305px; - background-size: calc(100% + 305px); height: 100%; } .NextStepsView__body-main { flex: 3 0 75%; - - &:after { - background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOmNjPSJodHRwOi8vd2ViLnJlc291cmNlLm9yZy9jYy8iIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIiB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIgdmVyc2lvbj0iMS4xIiBiYXNlUHJvZmlsZT0iZnVsbCIgd2lkdGg9IjhweCIgaGVpZ2h0PSI4cHgiIHZpZXdCb3g9IjAgMCA4IDgiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIG1lZXQiIGlkPSJzdmdfZG9jdW1lbnQiIHN0eWxlPSJ6b29tOiAxOyI+CiAgICA8dGl0bGUgaWQ9InN2Z19kb2N1bWVudF90aXRsZSI+VW50aXRsZWQuc3ZnPC90aXRsZT4KICAgIDxkZWZzIGlkPSJzdmdfZG9jdW1lbnRfZGVmcyI+PC9kZWZzPgogICAgPGcgaWQ9Im1haW5fZ3JvdXAiPgogICAgICAgIDxyZWN0IGlkPSJiYWNrZ3JvdW5kX3JlY3QiIGZpbGw9IiMxNjZERTAiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iMXB4IiBoZWlnaHQ9IjFweCI+PC9yZWN0PgogICAgPC9nPgo8L3N2Zz4=) repeat; - background-size: 30px 30px; - opacity: 0.16; - transform: matrix(0.71, 0.64, -0.78, 0.71, 0, 0); - display: block; - content: ""; - - height: 50vh; - width: 50vh; - position: absolute; - left: 83%; - bottom: 3%; - } } .NextStepsView__body-graphic { @@ -65,4 +49,4 @@ background-position-y: 33%; flex: 1 0 25%; z-index: 2; -} +} \ No newline at end of file diff --git a/components/next_steps_view/next_steps_view.tsx b/components/next_steps_view/next_steps_view.tsx index c970d02aa150..baf6f9b4be6e 100644 --- a/components/next_steps_view/next_steps_view.tsx +++ b/components/next_steps_view/next_steps_view.tsx @@ -4,14 +4,15 @@ import React from "react"; import {FormattedMessage} from "react-intl"; +import professionalLogo from 'images/cloud-logos/professional.svg'; + import './next_steps_view.scss' type Props = { - + skuName: string; }; export default class NextStepsView extends React.PureComponent { - getBottomText = () => { // lmao const {isFinished} = {isFinished: false}; @@ -33,11 +34,20 @@ export default class NextStepsView extends React.PureComponent { ) } + getLogo = () => { + // TODO: Switch logos based on edition once we have the other logos + + switch (this.props.skuName) { + default: + return professionalLogo; + } + } + render() { return (
@@ -52,12 +62,11 @@ export default class NextStepsView extends React.PureComponent {
- +
-
diff --git a/images/cloud-logos/professional.svg b/images/cloud-logos/professional.svg new file mode 100644 index 000000000000..1dc55f32f30e --- /dev/null +++ b/images/cloud-logos/professional.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/onboarding-bg.svg b/images/onboarding-bg.svg new file mode 100644 index 000000000000..c6b036cd3fc7 --- /dev/null +++ b/images/onboarding-bg.svg @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/waves.svg b/images/waves.svg deleted file mode 100644 index 3483369b094d..000000000000 --- a/images/waves.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - From 776823e71b6d87c2e6ce89bea478b441fb04d794 Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Tue, 30 Jun 2020 10:59:48 -0400 Subject: [PATCH 04/46] Mobile view, lint and type fixes, added a test file for later use --- components/channel_view/channel_view.jsx | 3 ++- .../next_steps_view/next_steps_view.scss | 27 +++++++++++++++++++ .../next_steps_view/next_steps_view.test.tsx | 21 +++++++++++++++ .../next_steps_view/next_steps_view.tsx | 17 ++++++------ 4 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 components/next_steps_view/next_steps_view.test.tsx diff --git a/components/channel_view/channel_view.jsx b/components/channel_view/channel_view.jsx index be5432490f83..5c9ee6c4fb1c 100644 --- a/components/channel_view/channel_view.jsx +++ b/components/channel_view/channel_view.jsx @@ -28,6 +28,7 @@ export default class ChannelView extends React.PureComponent { }).isRequired, }).isRequired, showTutorial: PropTypes.bool.isRequired, + showNextSteps: PropTypes.bool.isRequired, channelIsArchived: PropTypes.bool.isRequired, viewArchivedChannels: PropTypes.bool.isRequired, actions: PropTypes.shape({ @@ -126,7 +127,7 @@ export default class ChannelView extends React.PureComponent { if (this.props.showNextSteps) { return ( - + ); } diff --git a/components/next_steps_view/next_steps_view.scss b/components/next_steps_view/next_steps_view.scss index dafc3e4738ee..42d578e7adff 100644 --- a/components/next_steps_view/next_steps_view.scss +++ b/components/next_steps_view/next_steps_view.scss @@ -49,4 +49,31 @@ background-position-y: 33%; flex: 1 0 25%; z-index: 2; +} + +@media screen and (max-width: 768px) { + #app-content.NextStepsView { + background-size: auto; + } + + .NextStepsView__header { + padding: 24px 28px; + } + + .NextStepsView__header-headerTopText { + font-size: 24px; + line-height: 32px; + } + + .NextStepsView__header-logo { + display: none; + } + + .NextStepsView__body-main { + flex: 1 1 100%; + } + + .NextStepsView__body-graphic { + flex: 0 0 0%; + } } \ No newline at end of file diff --git a/components/next_steps_view/next_steps_view.test.tsx b/components/next_steps_view/next_steps_view.test.tsx new file mode 100644 index 000000000000..e0a470f32218 --- /dev/null +++ b/components/next_steps_view/next_steps_view.test.tsx @@ -0,0 +1,21 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {shallow} from 'enzyme'; + +import NextStepsView from 'components/next_steps_view/next_steps_view'; + +describe('components/next_steps_view', () => { + const baseProps = { + skuName: '', + }; + + test('should match snapshot', () => { + const wrapper = shallow( + , + ); + + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/components/next_steps_view/next_steps_view.tsx b/components/next_steps_view/next_steps_view.tsx index baf6f9b4be6e..0f98db33d33e 100644 --- a/components/next_steps_view/next_steps_view.tsx +++ b/components/next_steps_view/next_steps_view.tsx @@ -1,12 +1,12 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React from "react"; -import {FormattedMessage} from "react-intl"; +import React from 'react'; +import {FormattedMessage} from 'react-intl'; import professionalLogo from 'images/cloud-logos/professional.svg'; -import './next_steps_view.scss' +import './next_steps_view.scss'; type Props = { skuName: string; @@ -21,7 +21,7 @@ export default class NextStepsView extends React.PureComponent { return ( ); } @@ -31,15 +31,15 @@ export default class NextStepsView extends React.PureComponent { id='next_steps_view.hereAreSomeNextSteps' defaultMessage='Here are some recommended next steps to help you collaborate' /> - ) + ); } getLogo = () => { // TODO: Switch logos based on edition once we have the other logos switch (this.props.skuName) { - default: - return professionalLogo; + default: + return professionalLogo; } } @@ -66,8 +66,7 @@ export default class NextStepsView extends React.PureComponent {
-
-
+
From 630cc1901525d2130a7bb03d09f489ec98d71773 Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Tue, 30 Jun 2020 11:07:56 -0400 Subject: [PATCH 05/46] More test fixes --- components/channel_view/channel_view.test.jsx | 1 + .../next_steps_view.test.tsx.snap | 52 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 components/next_steps_view/__snapshots__/next_steps_view.test.tsx.snap diff --git a/components/channel_view/channel_view.test.jsx b/components/channel_view/channel_view.test.jsx index 040da8a36775..21d372eee4cd 100644 --- a/components/channel_view/channel_view.test.jsx +++ b/components/channel_view/channel_view.test.jsx @@ -16,6 +16,7 @@ describe('components/channel_view', () => { params: {}, }, showTutorial: false, + showNextSteps: false, channelIsArchived: false, viewArchivedChannels: false, actions: { diff --git a/components/next_steps_view/__snapshots__/next_steps_view.test.tsx.snap b/components/next_steps_view/__snapshots__/next_steps_view.test.tsx.snap new file mode 100644 index 000000000000..1624bcbf9e72 --- /dev/null +++ b/components/next_steps_view/__snapshots__/next_steps_view.test.tsx.snap @@ -0,0 +1,52 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`components/next_steps_view should match snapshot 1`] = ` +
+
+
+
+ +
+
+ +
+
+
+ +
+
+
+
+
+
+
+`; From afa668e26032a5c255d663d042bea901b3705cb2 Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Thu, 2 Jul 2020 09:58:55 -0400 Subject: [PATCH 06/46] UX feedback --- components/next_steps_view/next_steps_view.scss | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/components/next_steps_view/next_steps_view.scss b/components/next_steps_view/next_steps_view.scss index 42d578e7adff..577aa16faabc 100644 --- a/components/next_steps_view/next_steps_view.scss +++ b/components/next_steps_view/next_steps_view.scss @@ -49,11 +49,13 @@ background-position-y: 33%; flex: 1 0 25%; z-index: 2; + min-width: 192px; } @media screen and (max-width: 768px) { #app-content.NextStepsView { background-size: auto; + background-position-y: top; } .NextStepsView__header { @@ -65,10 +67,6 @@ line-height: 32px; } - .NextStepsView__header-logo { - display: none; - } - .NextStepsView__body-main { flex: 1 1 100%; } @@ -76,4 +74,14 @@ .NextStepsView__body-graphic { flex: 0 0 0%; } +} + +@media screen and (max-width: 1020px) { + .NextStepsView__header-logo { + display: none; + } + + .NextStepsView__body-main { + min-width: 536px; + } } \ No newline at end of file From 7f57467c4fd2298aa81dab794c00e35d1e0cbe2d Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Fri, 3 Jul 2020 09:19:39 -0400 Subject: [PATCH 07/46] Replaced dumb comment with useful one --- components/next_steps_view/next_steps_view.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/next_steps_view/next_steps_view.tsx b/components/next_steps_view/next_steps_view.tsx index 0f98db33d33e..6fa8e274e3bd 100644 --- a/components/next_steps_view/next_steps_view.tsx +++ b/components/next_steps_view/next_steps_view.tsx @@ -9,12 +9,12 @@ import professionalLogo from 'images/cloud-logos/professional.svg'; import './next_steps_view.scss'; type Props = { - skuName: string; + skuName: string; }; export default class NextStepsView extends React.PureComponent { getBottomText = () => { - // lmao + // TODO: will be stored in user prefs at a later date const {isFinished} = {isFinished: false}; if (isFinished) { From 05521d800f2c00314bbd4e0d1c2b573da28cae46 Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Fri, 3 Jul 2020 09:22:08 -0400 Subject: [PATCH 08/46] Turn off graphic at 1020px --- components/next_steps_view/next_steps_view.scss | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/components/next_steps_view/next_steps_view.scss b/components/next_steps_view/next_steps_view.scss index 577aa16faabc..30a47ee6820a 100644 --- a/components/next_steps_view/next_steps_view.scss +++ b/components/next_steps_view/next_steps_view.scss @@ -70,10 +70,6 @@ .NextStepsView__body-main { flex: 1 1 100%; } - - .NextStepsView__body-graphic { - flex: 0 0 0%; - } } @media screen and (max-width: 1020px) { @@ -81,6 +77,11 @@ display: none; } + .NextStepsView__body-graphic { + flex: 0 0 0%; + min-width: 0; + } + .NextStepsView__body-main { min-width: 536px; } From b7d78f1054ed0d2ae4ffebec12b86f4b73b2ab83 Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Thu, 2 Jul 2020 09:40:21 -0400 Subject: [PATCH 09/46] WIP --- components/card.scss | 0 components/card.tsx | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 components/card.scss create mode 100644 components/card.tsx diff --git a/components/card.scss b/components/card.scss new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/components/card.tsx b/components/card.tsx new file mode 100644 index 000000000000..06c00d443782 --- /dev/null +++ b/components/card.tsx @@ -0,0 +1,39 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; + +import './card.scss'; + +const CardHeader: React.FC<{children: JSX.Element}> = ({children}) => { + return ( +
+ {children} +
+ ); +} + +const CardBody: React.FC<{children: JSX.Element}> = ({children}) => { + return ( +
+ {children} +
+ ); +} + +type Props = { + +} + +export default class Card extends React.PureComponent { + public static Header = CardHeader; + public static Body = CardBody; + + render() { + return ( +
+ {this.props.children} +
+ ); + } +} \ No newline at end of file From 88df376d9c07ee477c56a9bf070600bd8156278b Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Thu, 2 Jul 2020 14:26:44 -0400 Subject: [PATCH 10/46] Initial card style --- components/card.scss | 16 ++++++++++++++++ components/next_steps_view/next_steps_view.scss | 1 + components/next_steps_view/next_steps_view.tsx | 14 +++++++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/components/card.scss b/components/card.scss index e69de29bb2d1..40905d94c4ec 100644 --- a/components/card.scss +++ b/components/card.scss @@ -0,0 +1,16 @@ +.Card { + border: 1px solid var(--center-channel-color-08); + box-shadow: 0px 8px 24px rgba(0, 0, 0, 0.12); + border-radius: 4px; + background-color: var(--center-channel-bg); +} + +.Card__header { + margin: 12px; +} + +.Card__body { + margin: 12px; + padding-top: 12px; + border-top: 1px solid var(--center-channel-color-08); +} \ No newline at end of file diff --git a/components/next_steps_view/next_steps_view.scss b/components/next_steps_view/next_steps_view.scss index 30a47ee6820a..da0122792a10 100644 --- a/components/next_steps_view/next_steps_view.scss +++ b/components/next_steps_view/next_steps_view.scss @@ -40,6 +40,7 @@ .NextStepsView__body-main { flex: 3 0 75%; + padding: 0 86px 0 40px; } .NextStepsView__body-graphic { diff --git a/components/next_steps_view/next_steps_view.tsx b/components/next_steps_view/next_steps_view.tsx index 6fa8e274e3bd..6356f50e8ae4 100644 --- a/components/next_steps_view/next_steps_view.tsx +++ b/components/next_steps_view/next_steps_view.tsx @@ -7,6 +7,7 @@ import {FormattedMessage} from 'react-intl'; import professionalLogo from 'images/cloud-logos/professional.svg'; import './next_steps_view.scss'; +import Card from 'components/card'; type Props = { skuName: string; @@ -66,7 +67,18 @@ export default class NextStepsView extends React.PureComponent {
-
+
+ + + {'Card Header'} + + +
+ {'Card Body'} +
+
+
+
From b8e7ea827baaea4926340830c54b4ec36316ec0f Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Thu, 2 Jul 2020 16:49:41 -0400 Subject: [PATCH 11/46] Collapse functionality (no animation) --- components/card.scss | 8 +++++++ components/card.tsx | 19 ++++++++++----- .../next_steps_view/next_steps_view.tsx | 23 ++++++++++++++++--- 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/components/card.scss b/components/card.scss index 40905d94c4ec..1d44ca0ae8c9 100644 --- a/components/card.scss +++ b/components/card.scss @@ -13,4 +13,12 @@ margin: 12px; padding-top: 12px; border-top: 1px solid var(--center-channel-color-08); + overflow: hidden; +} + +.Card.collapsed .Card__body { + height: 0; + padding-top: 0; + margin: 0 12px; + border-top: none; } \ No newline at end of file diff --git a/components/card.tsx b/components/card.tsx index 06c00d443782..05a08c7893c7 100644 --- a/components/card.tsx +++ b/components/card.tsx @@ -4,6 +4,11 @@ import React from 'react'; import './card.scss'; +import classNames from 'classnames'; + +type Props = { + collapsed: boolean; +} const CardHeader: React.FC<{children: JSX.Element}> = ({children}) => { return ( @@ -21,18 +26,20 @@ const CardBody: React.FC<{children: JSX.Element}> = ({children}) => { ); } -type Props = { - -} - export default class Card extends React.PureComponent { public static Header = CardHeader; public static Body = CardBody; render() { + const {collapsed, children} = this.props; + return ( -
- {this.props.children} +
+ {children}
); } diff --git a/components/next_steps_view/next_steps_view.tsx b/components/next_steps_view/next_steps_view.tsx index 6356f50e8ae4..a6c598eadbe2 100644 --- a/components/next_steps_view/next_steps_view.tsx +++ b/components/next_steps_view/next_steps_view.tsx @@ -4,16 +4,28 @@ import React from 'react'; import {FormattedMessage} from 'react-intl'; +import Card from 'components/card'; import professionalLogo from 'images/cloud-logos/professional.svg'; import './next_steps_view.scss'; -import Card from 'components/card'; type Props = { skuName: string; }; -export default class NextStepsView extends React.PureComponent { +type State = { + collapsed: boolean; +} + +export default class NextStepsView extends React.PureComponent { + constructor(props: Props) { + super(props); + + this.state = { + collapsed: false, + }; + } + getBottomText = () => { // TODO: will be stored in user prefs at a later date const {isFinished} = {isFinished: false}; @@ -44,6 +56,10 @@ export default class NextStepsView extends React.PureComponent { } } + handleButton = () => { + this.setState({collapsed: !this.state.collapsed}); + } + render() { return (
{
- + + {'Card Header'} From d62b7ef34534f0253e25fddc39ac919c7fd20261 Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Fri, 3 Jul 2020 13:26:35 -0400 Subject: [PATCH 12/46] WIP --- components/accordion.scss | 0 components/accordion.tsx | 36 +++++++++++ components/card.scss | 12 ++-- components/card.tsx | 22 ++++--- .../next_steps_view/next_steps_view.tsx | 63 ++++++++++++++----- 5 files changed, 105 insertions(+), 28 deletions(-) create mode 100644 components/accordion.scss create mode 100644 components/accordion.tsx diff --git a/components/accordion.scss b/components/accordion.scss new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/components/accordion.tsx b/components/accordion.tsx new file mode 100644 index 000000000000..d8258344264c --- /dev/null +++ b/components/accordion.tsx @@ -0,0 +1,36 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React, { Children, isValidElement, cloneElement } from 'react'; + +import Card from './card'; + +import './accordion.scss'; + +type Props = { + expandedKey: string; + children: Card[]; +} + +export default class Accordion extends React.PureComponent { + render() { + const {children} = this.props; + + const childrenWithProps = Children.map(children, child => { + // Checking isValidElement is the safe way and avoids a TS error too. + if (isValidElement(child)) { + return cloneElement(child, {collapsed: child.props.key !== this.props.expandedKey}); + } + + return child; + }); + + return ( +
+ {childrenWithProps} +
+ ); + } +} \ No newline at end of file diff --git a/components/card.scss b/components/card.scss index 1d44ca0ae8c9..d6e6188cdeea 100644 --- a/components/card.scss +++ b/components/card.scss @@ -14,11 +14,11 @@ padding-top: 12px; border-top: 1px solid var(--center-channel-color-08); overflow: hidden; -} -.Card.collapsed .Card__body { - height: 0; - padding-top: 0; - margin: 0 12px; - border-top: none; + &.collapsed { + height: 0; + padding-top: 0; + margin: 0 12px; + border-top: none; + } } \ No newline at end of file diff --git a/components/card.tsx b/components/card.tsx index 05a08c7893c7..fa58e225adbd 100644 --- a/components/card.tsx +++ b/components/card.tsx @@ -1,12 +1,13 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React from 'react'; +import React, { Children, isValidElement, cloneElement } from 'react'; import './card.scss'; import classNames from 'classnames'; type Props = { + key: string; collapsed: boolean; } @@ -18,9 +19,9 @@ const CardHeader: React.FC<{children: JSX.Element}> = ({children}) => { ); } -const CardBody: React.FC<{children: JSX.Element}> = ({children}) => { +const CardBody: React.FC<{children: JSX.Element, collapsed?: boolean}> = ({children, collapsed}) => { return ( -
+
{children}
); @@ -33,13 +34,20 @@ export default class Card extends React.PureComponent { render() { const {collapsed, children} = this.props; + const childrenWithProps = Children.map(children, child => { + // Checking isValidElement is the safe way and avoids a TS error too. + if (isValidElement(child)) { + return cloneElement(child, {collapsed}); + } + + return child; + }); + return (
- {children} + {childrenWithProps}
); } diff --git a/components/next_steps_view/next_steps_view.tsx b/components/next_steps_view/next_steps_view.tsx index a6c598eadbe2..8d5b138849f4 100644 --- a/components/next_steps_view/next_steps_view.tsx +++ b/components/next_steps_view/next_steps_view.tsx @@ -8,13 +8,14 @@ import Card from 'components/card'; import professionalLogo from 'images/cloud-logos/professional.svg'; import './next_steps_view.scss'; +import Accordion from 'components/accordion'; type Props = { skuName: string; }; type State = { - collapsed: boolean; + expandedKey: string; } export default class NextStepsView extends React.PureComponent { @@ -22,7 +23,7 @@ export default class NextStepsView extends React.PureComponent { super(props); this.state = { - collapsed: false, + expandedKey: 'Card_1', }; } @@ -56,8 +57,8 @@ export default class NextStepsView extends React.PureComponent { } } - handleButton = () => { - this.setState({collapsed: !this.state.collapsed}); + handleButton = (expandedKey: string) => { + this.setState({expandedKey}); } render() { @@ -84,17 +85,49 @@ export default class NextStepsView extends React.PureComponent {
- - - - {'Card Header'} - - -
- {'Card Body'} -
-
-
+ + + + {'Card Header 1'} + + + +
+ {'Card Body 1'} +
+
+
+ + + {'Card Header 2'} + + + +
+ {'Card Body 2'} +
+
+
+ + + {'Card Header 3'} + + + +
+ {'Card Body 3'} +
+
+
+
From 6b16a0300b5a6fae3a44d5a238a5e0aa4c1a2883 Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Mon, 6 Jul 2020 14:20:36 -0400 Subject: [PATCH 13/46] Rest of accordion common component and some animation --- components/accordion.tsx | 37 ++++--- components/card.scss | 27 +++-- components/card.tsx | 54 +++++++--- .../next_steps_view/next_steps_view.tsx | 102 ++++++++---------- 4 files changed, 122 insertions(+), 98 deletions(-) diff --git a/components/accordion.tsx b/components/accordion.tsx index d8258344264c..cf59baa04173 100644 --- a/components/accordion.tsx +++ b/components/accordion.tsx @@ -1,35 +1,38 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, { Children, isValidElement, cloneElement } from 'react'; - -import Card from './card'; +import React from 'react'; import './accordion.scss'; type Props = { + defaultExpandedKey: string; + children: (setExpanded: (expandedKey: string) => void, expandedKey: string) => React.ReactNode; +}; + +type State = { expandedKey: string; - children: Card[]; -} +}; -export default class Accordion extends React.PureComponent { - render() { - const {children} = this.props; +export default class Accordion extends React.PureComponent { + constructor(props: Props) { + super(props); - const childrenWithProps = Children.map(children, child => { - // Checking isValidElement is the safe way and avoids a TS error too. - if (isValidElement(child)) { - return cloneElement(child, {collapsed: child.props.key !== this.props.expandedKey}); - } - - return child; - }); + this.state = { + expandedKey: props.defaultExpandedKey, + }; + } + setExpanded = (expandedKey: string) => { + this.setState({expandedKey}); + } + + render() { return (
- {childrenWithProps} + {this.props.children(this.setExpanded, this.state.expandedKey)}
); } diff --git a/components/card.scss b/components/card.scss index d6e6188cdeea..6ce20e0ef105 100644 --- a/components/card.scss +++ b/components/card.scss @@ -3,22 +3,31 @@ box-shadow: 0px 8px 24px rgba(0, 0, 0, 0.12); border-radius: 4px; background-color: var(--center-channel-bg); + + & + .Card { + margin-top: 16px; + } } .Card__header { - margin: 12px; + margin: 12px 12px 0 12px; + padding-bottom: 12px; + + &.expanded { + border-bottom: 1px solid var(--center-channel-color-08); + } } .Card__body { - margin: 12px; - padding-top: 12px; - border-top: 1px solid var(--center-channel-color-08); overflow: hidden; + height: 0; + margin: 0 12px; + + transition-duration: 0.3s; + transition-timing-function: ease-in-out; + transition-property: height, margin; - &.collapsed { - height: 0; - padding-top: 0; - margin: 0 12px; - border-top: none; + &.expanded { + margin: 12px; } } \ No newline at end of file diff --git a/components/card.tsx b/components/card.tsx index fa58e225adbd..0ad40b95e9bf 100644 --- a/components/card.tsx +++ b/components/card.tsx @@ -2,29 +2,57 @@ // See LICENSE.txt for license information. import React, { Children, isValidElement, cloneElement } from 'react'; +import classNames from 'classnames'; import './card.scss'; -import classNames from 'classnames'; type Props = { - key: string; - collapsed: boolean; + expanded?: boolean; } -const CardHeader: React.FC<{children: JSX.Element}> = ({children}) => { +const CardHeader: React.FC<{children: React.ReactNode, expanded?: boolean}> = ({children, expanded}) => { return ( -
+
{children}
); } -const CardBody: React.FC<{children: JSX.Element, collapsed?: boolean}> = ({children, collapsed}) => { - return ( -
- {children} -
- ); +class CardBody extends React.PureComponent<{expanded?: boolean}> { + card: React.RefObject; + + constructor(props: Props) { + super(props); + + this.card = React.createRef(); + } + + componentDidMount() { + if (this.card.current && this.props.expanded) { + this.card.current.style.height = `${this.card.current.scrollHeight}px`; + } + } + + componentDidUpdate(prevProps: Props) { + if (this.card.current) { + if (this.props.expanded !== prevProps.expanded && this.props.expanded) { + this.card.current.style.height = `${this.card.current.scrollHeight}px`; + } else { + this.card.current.style.height = ''; + } + } + } + + render() { + return ( +
+ {this.props.children} +
+ ); + } } export default class Card extends React.PureComponent { @@ -32,12 +60,12 @@ export default class Card extends React.PureComponent { public static Body = CardBody; render() { - const {collapsed, children} = this.props; + const {expanded, children} = this.props; const childrenWithProps = Children.map(children, child => { // Checking isValidElement is the safe way and avoids a TS error too. if (isValidElement(child)) { - return cloneElement(child, {collapsed}); + return cloneElement(child, {expanded}); } return child; diff --git a/components/next_steps_view/next_steps_view.tsx b/components/next_steps_view/next_steps_view.tsx index 8d5b138849f4..0fb3b7d1e662 100644 --- a/components/next_steps_view/next_steps_view.tsx +++ b/components/next_steps_view/next_steps_view.tsx @@ -14,19 +14,7 @@ type Props = { skuName: string; }; -type State = { - expandedKey: string; -} - -export default class NextStepsView extends React.PureComponent { - constructor(props: Props) { - super(props); - - this.state = { - expandedKey: 'Card_1', - }; - } - +export default class NextStepsView extends React.PureComponent { getBottomText = () => { // TODO: will be stored in user prefs at a later date const {isFinished} = {isFinished: false}; @@ -57,10 +45,6 @@ export default class NextStepsView extends React.PureComponent { } } - handleButton = (expandedKey: string) => { - this.setState({expandedKey}); - } - render() { return (
{
- - - - {'Card Header 1'} - - - -
- {'Card Body 1'} -
-
-
- - - {'Card Header 2'} - - - -
- {'Card Body 2'} -
-
-
- - - {'Card Header 3'} - - - -
- {'Card Body 3'} -
-
-
+ + {(setExpanded, expandedKey) => { + return ( + <> + + + {'Card Header 1'} + + + +
+ {'Card Body 1'} +
+
+
+ + + {'Card Header 2'} + + + +
+ {'Card Body 2'} +
+
+
+ + + {'Card Header 3'} + + + +
+ {'Card Body 3'} +
+ {'Bigger Card Body'} +
+
+
+ + ) + }}
From 60ad53a4a22c3b8f78f92a3185530f0450b2cc00 Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Mon, 6 Jul 2020 15:25:47 -0400 Subject: [PATCH 14/46] Lint, type and test fixes --- .../card/__snapshots__/card.test.tsx.snap | 32 ++++++++++++++ components/{ => card}/card.scss | 0 components/card/card.test.tsx | 42 +++++++++++++++++++ components/card/card.tsx | 38 +++++++++++++++++ components/{card.tsx => card/card_body.tsx} | 40 ++---------------- components/card/card_header.tsx | 20 +++++++++ .../next_steps_view/next_steps_view.tsx | 12 +++--- 7 files changed, 141 insertions(+), 43 deletions(-) create mode 100644 components/card/__snapshots__/card.test.tsx.snap rename components/{ => card}/card.scss (100%) create mode 100644 components/card/card.test.tsx create mode 100644 components/card/card.tsx rename components/{card.tsx => card/card_body.tsx} (52%) create mode 100644 components/card/card_header.tsx diff --git a/components/card/__snapshots__/card.test.tsx.snap b/components/card/__snapshots__/card.test.tsx.snap new file mode 100644 index 000000000000..5557100ae73f --- /dev/null +++ b/components/card/__snapshots__/card.test.tsx.snap @@ -0,0 +1,32 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`components/card/card should match snapshot 1`] = ` + +
+ +
+ Header Test +
+
+ +
+ Body Test +
+
+
+
+`; diff --git a/components/card.scss b/components/card/card.scss similarity index 100% rename from components/card.scss rename to components/card/card.scss diff --git a/components/card/card.test.tsx b/components/card/card.test.tsx new file mode 100644 index 000000000000..2081f5de8a03 --- /dev/null +++ b/components/card/card.test.tsx @@ -0,0 +1,42 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {mount, ReactWrapper} from 'enzyme'; + +import Card from './card'; +import CardBody from './card_body'; + +describe('components/card/card', () => { + const baseProps = { + expanded: false, + }; + + test('should match snapshot', () => { + const wrapper = mount( + + {'Header Test'} + {'Body Test'} + + ); + + expect(wrapper).toMatchSnapshot(); + }); + + test('should have a height based on content when expanded', () => { + const wrapper: ReactWrapper = mount( + + {'Body Test'} + {'Slightly Larger Body Text'} + + ); + + expect(wrapper.instance().card.current?.style.height).toBe(''); + + wrapper.setProps({expanded: true}); + expect(wrapper.instance().card.current?.style.height).not.toBe(''); + + wrapper.setProps({expanded: false}); + expect(wrapper.instance().card.current?.style.height).toBe(''); + }); +}); diff --git a/components/card/card.tsx b/components/card/card.tsx new file mode 100644 index 000000000000..7f205237b69d --- /dev/null +++ b/components/card/card.tsx @@ -0,0 +1,38 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React, {Children, isValidElement, cloneElement} from 'react'; + +import CardHeader from './card_header'; +import CardBody from './card_body'; + +import './card.scss'; + +type Props = { + expanded?: boolean; +} + +export default class Card extends React.PureComponent { + public static Header = CardHeader; + public static Body = CardBody; + + render() { + const {expanded, children} = this.props; + + const childrenWithProps = Children.map(children, (child) => { + // Checking isValidElement is the safe way and avoids a TS error too. + if (isValidElement(child)) { + return cloneElement(child, {expanded}); + } + return child; + }); + + return ( +
+ {childrenWithProps} +
+ ); + } +} \ No newline at end of file diff --git a/components/card.tsx b/components/card/card_body.tsx similarity index 52% rename from components/card.tsx rename to components/card/card_body.tsx index 0ad40b95e9bf..d21348b92ab9 100644 --- a/components/card.tsx +++ b/components/card/card_body.tsx @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, { Children, isValidElement, cloneElement } from 'react'; +import React from 'react'; import classNames from 'classnames'; import './card.scss'; @@ -10,15 +10,7 @@ type Props = { expanded?: boolean; } -const CardHeader: React.FC<{children: React.ReactNode, expanded?: boolean}> = ({children, expanded}) => { - return ( -
- {children} -
- ); -} - -class CardBody extends React.PureComponent<{expanded?: boolean}> { +export default class CardBody extends React.PureComponent { card: React.RefObject; constructor(props: Props) { @@ -45,38 +37,12 @@ class CardBody extends React.PureComponent<{expanded?: boolean}> { render() { return ( -
{this.props.children}
- ); - } -} - -export default class Card extends React.PureComponent { - public static Header = CardHeader; - public static Body = CardBody; - - render() { - const {expanded, children} = this.props; - - const childrenWithProps = Children.map(children, child => { - // Checking isValidElement is the safe way and avoids a TS error too. - if (isValidElement(child)) { - return cloneElement(child, {expanded}); - } - - return child; - }); - - return ( -
- {childrenWithProps} -
); } } \ No newline at end of file diff --git a/components/card/card_header.tsx b/components/card/card_header.tsx new file mode 100644 index 000000000000..ab3e1d8e006a --- /dev/null +++ b/components/card/card_header.tsx @@ -0,0 +1,20 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import classNames from 'classnames'; + +type Props = { + children: React.ReactNode; + expanded?: boolean; +}; + +const CardHeader: React.FC = (props: Props) => { + return ( +
+ {props.children} +
+ ); +}; + +export default CardHeader; \ No newline at end of file diff --git a/components/next_steps_view/next_steps_view.tsx b/components/next_steps_view/next_steps_view.tsx index 0fb3b7d1e662..78c1ad4a1c1c 100644 --- a/components/next_steps_view/next_steps_view.tsx +++ b/components/next_steps_view/next_steps_view.tsx @@ -4,14 +4,14 @@ import React from 'react'; import {FormattedMessage} from 'react-intl'; -import Card from 'components/card'; +import Card from 'components/card/card'; import professionalLogo from 'images/cloud-logos/professional.svg'; import './next_steps_view.scss'; import Accordion from 'components/accordion'; type Props = { - skuName: string; + skuName: string; }; export default class NextStepsView extends React.PureComponent { @@ -76,7 +76,7 @@ export default class NextStepsView extends React.PureComponent { {'Card Header 1'} - +
@@ -87,7 +87,7 @@ export default class NextStepsView extends React.PureComponent { {'Card Header 2'} - +
@@ -98,7 +98,7 @@ export default class NextStepsView extends React.PureComponent { {'Card Header 3'} - +
@@ -109,7 +109,7 @@ export default class NextStepsView extends React.PureComponent { - ) + ); }}
From d56417c4f197aed8b6d6f73f5309e828633f542c Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Tue, 7 Jul 2020 12:22:39 -0400 Subject: [PATCH 15/46] Updated snapshot --- .../__snapshots__/next_steps_view.test.tsx.snap | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/components/next_steps_view/__snapshots__/next_steps_view.test.tsx.snap b/components/next_steps_view/__snapshots__/next_steps_view.test.tsx.snap index 1624bcbf9e72..33bcdd6199bb 100644 --- a/components/next_steps_view/__snapshots__/next_steps_view.test.tsx.snap +++ b/components/next_steps_view/__snapshots__/next_steps_view.test.tsx.snap @@ -43,7 +43,13 @@ exports[`components/next_steps_view should match snapshot 1`] = ` >
+ > + + + +
From 0027735225a3c8beff184def95142d7fcb675164 Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Tue, 7 Jul 2020 15:30:36 -0400 Subject: [PATCH 16/46] Reduce nesting --- components/card/card_body.tsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/components/card/card_body.tsx b/components/card/card_body.tsx index d21348b92ab9..a9bd3603389f 100644 --- a/components/card/card_body.tsx +++ b/components/card/card_body.tsx @@ -26,12 +26,14 @@ export default class CardBody extends React.PureComponent { } componentDidUpdate(prevProps: Props) { - if (this.card.current) { - if (this.props.expanded !== prevProps.expanded && this.props.expanded) { - this.card.current.style.height = `${this.card.current.scrollHeight}px`; - } else { - this.card.current.style.height = ''; - } + if (!this.card.current) { + return; + } + + if (this.props.expanded !== prevProps.expanded && this.props.expanded) { + this.card.current.style.height = `${this.card.current.scrollHeight}px`; + } else { + this.card.current.style.height = ''; } } @@ -45,4 +47,4 @@ export default class CardBody extends React.PureComponent {
); } -} \ No newline at end of file +} From 839f962c052295b463e1a7813f5ab546767d5cf6 Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Wed, 8 Jul 2020 17:05:06 -0400 Subject: [PATCH 17/46] WIP - Wiring for step wizard --- components/card/card.tsx | 3 +- components/card/card_body.tsx | 2 +- components/next_steps_view/index.ts | 25 ++- .../next_steps_view/next_steps_view.scss | 78 +++++++++ .../next_steps_view/next_steps_view.tsx | 150 +++++++++++++----- .../steps/complete_profile_step.tsx | 24 +++ 6 files changed, 235 insertions(+), 47 deletions(-) create mode 100644 components/next_steps_view/steps/complete_profile_step.tsx diff --git a/components/card/card.tsx b/components/card/card.tsx index 7f205237b69d..26d5f5d16e60 100644 --- a/components/card/card.tsx +++ b/components/card/card.tsx @@ -10,6 +10,7 @@ import './card.scss'; type Props = { expanded?: boolean; + className?: string; } export default class Card extends React.PureComponent { @@ -29,7 +30,7 @@ export default class Card extends React.PureComponent { return (
{childrenWithProps}
diff --git a/components/card/card_body.tsx b/components/card/card_body.tsx index a9bd3603389f..b3269a005dd2 100644 --- a/components/card/card_body.tsx +++ b/components/card/card_body.tsx @@ -32,7 +32,7 @@ export default class CardBody extends React.PureComponent { if (this.props.expanded !== prevProps.expanded && this.props.expanded) { this.card.current.style.height = `${this.card.current.scrollHeight}px`; - } else { + } else if (!this.props.expanded) { this.card.current.style.height = ''; } } diff --git a/components/next_steps_view/index.ts b/components/next_steps_view/index.ts index 1271c79bdff2..fb6dc828aeb8 100644 --- a/components/next_steps_view/index.ts +++ b/components/next_steps_view/index.ts @@ -1,31 +1,40 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - import {connect} from 'react-redux'; import {bindActionCreators, Dispatch} from 'redux'; +import {savePreferences} from 'mattermost-redux/actions/preferences'; import {getLicense} from 'mattermost-redux/selectors/entities/general'; +import {makeGetCategory} from 'mattermost-redux/selectors/entities/preferences'; +import {getCurrentUser} from 'mattermost-redux/selectors/entities/users'; import {GlobalState} from 'types/store'; +import {Preferences} from 'utils/constants'; import NextStepsView from './next_steps_view'; -function mapStateToProps(state: GlobalState) { - const license = getLicense(state); +function makeMapStateToProps() { + const getCategory = makeGetCategory(); - return { - skuName: license.SkuShortName, + return (state: GlobalState) => { + const license = getLicense(state); + const currentUser = getCurrentUser(state); + + return { + currentUserId: currentUser.id, + preferences: getCategory(state, Preferences.RECOMMENDED_NEXT_STEPS), + skuName: license.SkuShortName, + }; }; } function mapDispatchToProps(dispatch: Dispatch) { return { actions: bindActionCreators({ + savePreferences, }, dispatch), }; } -export default connect(mapStateToProps, mapDispatchToProps)(NextStepsView); +export default connect(makeMapStateToProps, mapDispatchToProps)(NextStepsView); diff --git a/components/next_steps_view/next_steps_view.scss b/components/next_steps_view/next_steps_view.scss index da0122792a10..6ebd2889474a 100644 --- a/components/next_steps_view/next_steps_view.scss +++ b/components/next_steps_view/next_steps_view.scss @@ -53,6 +53,84 @@ min-width: 192px; } +.NextStepsView__body .Card__header { + margin: 0 12px; + padding: 0; +} + +.NextStepsView__cardHeader { + display: flex; + border: none; + background: none; + padding: 12px 0; + width: 100%; + + > span { + margin-left: 14px; + font-size: 14px; + line-height: 20px; + font-weight: 600; + color: var(--center-channel-color); + align-self: center; + } +} + +.NextStepsView__cardHeaderBadge { + width: 26px; + height: 26px; + background-color: var(--center-channel-color-16); + color: var(--center-channel-color); + border-radius: 14px; + display: flex; + justify-content: center; + + span { + align-self: center; + font-size: 14px; + line-height: 14px; + font-weight: 600; + } +} + +.Card__header.expanded .NextStepsView__cardHeaderBadge { + color: var(--button-color); + background-color: var(--sidebar-text-active-border); +} + +.Card.complete { + border: none; + box-shadow: none; + + & + .Card { + margin-top: 12px; + } + + .Card__header { + margin: 0; + border: none; + } + + .Card__body { + display: none; + } + + .NextStepsView__cardHeader { + padding: 0; + + > span { + color: var(--online-indicator); + margin-left: 8px; + } + + i { + align-self: center; + font-size: 16px; + line-height: 16px; + color: var(--online-indicator); + } + } +} + @media screen and (max-width: 768px) { #app-content.NextStepsView { background-size: auto; diff --git a/components/next_steps_view/next_steps_view.tsx b/components/next_steps_view/next_steps_view.tsx index 78c1ad4a1c1c..6ec261b333b0 100644 --- a/components/next_steps_view/next_steps_view.tsx +++ b/components/next_steps_view/next_steps_view.tsx @@ -2,16 +2,57 @@ // See LICENSE.txt for license information. import React from 'react'; +import classNames from 'classnames'; import {FormattedMessage} from 'react-intl'; +import {PreferenceType} from 'mattermost-redux/types/preferences'; + +import Accordion from 'components/accordion'; import Card from 'components/card/card'; import professionalLogo from 'images/cloud-logos/professional.svg'; +import {RecommendedNextSteps, Preferences} from 'utils/constants'; +import {localizeMessage} from 'utils/utils'; +import CompleteProfileStep from './steps/complete_profile_step'; import './next_steps_view.scss'; -import Accordion from 'components/accordion'; + +export type StepComponentProps = { + id: string; + onSkip: (id: string) => void; + onFinish: (id: string) => void; +} + +type StepType = { + id: string; + title: string; + component: React.ComponentType; +} + +const steps: StepType[] = [ + { + id: RecommendedNextSteps.COMPLETE_PROFILE, + title: localizeMessage('next_steps_view.titles.completeProfile', 'Complete your profile'), + component: CompleteProfileStep, + }, + { + id: RecommendedNextSteps.TEAM_SETUP, + title: localizeMessage('next_steps_view.titles.teamSetup', 'Name your team'), + component: CompleteProfileStep, + }, + { + id: RecommendedNextSteps.INVITE_MEMBERS, + title: localizeMessage('next_steps_view.titles.inviteMembers', 'Invite members to the team'), + component: CompleteProfileStep, + }, +]; type Props = { + currentUserId: string; + preferences: PreferenceType[]; skuName: string; + actions: { + savePreferences: (userId: string, preferences: PreferenceType[]) => Promise<{data: boolean}>; + }; }; export default class NextStepsView extends React.PureComponent { @@ -45,7 +86,76 @@ export default class NextStepsView extends React.PureComponent { } } + onSkip = (setExpanded: (expandedKey: string) => void) => { + return (id: string) => { + setExpanded(this.getNextStep(id)); + }; + } + + onFinish = (setExpanded: (expandedKey: string) => void) => { + return (id: string) => { + this.props.actions.savePreferences(this.props.currentUserId, [{ + category: Preferences.RECOMMENDED_NEXT_STEPS, + user_id: this.props.currentUserId, + name: id, + value: 'true', + }]); + + setExpanded(this.getNextStep(id)); + }; + } + + getNextStep = (id: string) => { + const currentIndex = steps.findIndex((step) => step.id === id); + + // TODO: Logic to stop once you've hit the final step + return steps[(currentIndex + 1) % steps.length].id; + } + + isStepComplete = (id: string) => { + return this.props.preferences.some((pref) => pref.name === id && Boolean(pref.value)); + } + + renderStep = (step: StepType, index: number) => { + let icon = ( +
+ {index + 1} +
+ ); + if (this.isStepComplete(step.id)) { + icon = ( + + ); + } + + return (setExpanded: (expandedKey: string) => void, expandedKey: string) => ( + + + + + + + + + ); + } + render() { + const renderedSteps = steps.map(this.renderStep); + return (
{
- + {(setExpanded, expandedKey) => { return ( <> - - - {'Card Header 1'} - - - -
- {'Card Body 1'} -
-
-
- - - {'Card Header 2'} - - - -
- {'Card Body 2'} -
-
-
- - - {'Card Header 3'} - - - -
- {'Card Body 3'} -
- {'Bigger Card Body'} -
-
-
+ {renderedSteps.map((step) => step(setExpanded, expandedKey))} ); }} diff --git a/components/next_steps_view/steps/complete_profile_step.tsx b/components/next_steps_view/steps/complete_profile_step.tsx new file mode 100644 index 000000000000..706a80ba6a2e --- /dev/null +++ b/components/next_steps_view/steps/complete_profile_step.tsx @@ -0,0 +1,24 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; + +import {StepComponentProps} from '../next_steps_view'; + +type Props = StepComponentProps & { +}; + +type State = { +}; + +export default class CompleteProfileStep extends React.PureComponent { + render() { + return ( +
+ {'AAAAAAAA'} + + +
+ ); + } +} \ No newline at end of file From e6e10cdf6ef12208ed844a63495660b1001c88f5 Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Thu, 9 Jul 2020 13:33:13 -0400 Subject: [PATCH 18/46] Skip getting started link, hook for final page --- .../next_steps_view/next_steps_view.scss | 16 ++++ .../next_steps_view/next_steps_view.tsx | 82 +++++++++++++++---- 2 files changed, 81 insertions(+), 17 deletions(-) diff --git a/components/next_steps_view/next_steps_view.scss b/components/next_steps_view/next_steps_view.scss index 6ebd2889474a..8b58cb640c5f 100644 --- a/components/next_steps_view/next_steps_view.scss +++ b/components/next_steps_view/next_steps_view.scss @@ -92,6 +92,22 @@ } } +.NextStepsView__skipGettingStarted { + display: flex; + justify-content: flex-end; + margin-top: 12px; + + button { + border: none; + background: none; + padding: 10px 16px; + font-weight: 600; + font-size: 12px; + line-height: 9px; + color: var(--button-bg); + } +} + .Card__header.expanded .NextStepsView__cardHeaderBadge { color: var(--button-color); background-color: var(--sidebar-text-active-border); diff --git a/components/next_steps_view/next_steps_view.tsx b/components/next_steps_view/next_steps_view.tsx index 6ec261b333b0..24df56c5e986 100644 --- a/components/next_steps_view/next_steps_view.tsx +++ b/components/next_steps_view/next_steps_view.tsx @@ -55,7 +55,19 @@ type Props = { }; }; -export default class NextStepsView extends React.PureComponent { +type State = { + showFinalScreen: boolean; +} + +export default class NextStepsView extends React.PureComponent { + constructor(props: Props) { + super(props); + + this.state = { + showFinalScreen: false, + }; + } + getBottomText = () => { // TODO: will be stored in user prefs at a later date const {isFinished} = {isFinished: false}; @@ -88,7 +100,7 @@ export default class NextStepsView extends React.PureComponent { onSkip = (setExpanded: (expandedKey: string) => void) => { return (id: string) => { - setExpanded(this.getNextStep(id)); + this.nextStep(setExpanded, id); }; } @@ -101,15 +113,21 @@ export default class NextStepsView extends React.PureComponent { value: 'true', }]); - setExpanded(this.getNextStep(id)); + this.nextStep(setExpanded, id); }; } - getNextStep = (id: string) => { - const currentIndex = steps.findIndex((step) => step.id === id); + skipAll = () => { + this.setState({showFinalScreen: true}); + } - // TODO: Logic to stop once you've hit the final step - return steps[(currentIndex + 1) % steps.length].id; + nextStep = (setExpanded: (expandedKey: string) => void, id: string) => { + const currentIndex = steps.findIndex((step) => step.id === id); + if ((currentIndex + 1) > (steps.length - 1)) { + this.setState({showFinalScreen: true}); + } else { + setExpanded(steps[currentIndex + 1].id); + } } isStepComplete = (id: string) => { @@ -153,9 +171,47 @@ export default class NextStepsView extends React.PureComponent { ); } - render() { + renderFinalScreen = () => { + // TODO + return ( +
+ ); + } + + renderMainBody = () => { const renderedSteps = steps.map(this.renderStep); + return ( + <> + + {(setExpanded, expandedKey) => { + return ( + <> + {renderedSteps.map((step) => step(setExpanded, expandedKey))} + + ); + }} + +
+ +
+ + ); + } + + render() { + let mainBody = this.renderMainBody(); + if (this.state.showFinalScreen) { + mainBody = this.renderFinalScreen(); + } + return (
{
- - {(setExpanded, expandedKey) => { - return ( - <> - {renderedSteps.map((step) => step(setExpanded, expandedKey))} - - ); - }} - + {mainBody}
From 737669813a9d5e9873f5b3f7595ab6811504a21b Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Thu, 9 Jul 2020 13:47:36 -0400 Subject: [PATCH 19/46] Moved steps into its own constants file, type and test fixes --- components/card/card.tsx | 2 +- .../next_steps_view.test.tsx.snap | 15 +++++- .../next_steps_view/next_steps_view.test.tsx | 5 ++ .../next_steps_view/next_steps_view.tsx | 47 ++++--------------- components/next_steps_view/steps.ts | 37 +++++++++++++++ .../steps/complete_profile_step.tsx | 2 +- 6 files changed, 66 insertions(+), 42 deletions(-) create mode 100644 components/next_steps_view/steps.ts diff --git a/components/card/card.tsx b/components/card/card.tsx index 26d5f5d16e60..651238aef4ee 100644 --- a/components/card/card.tsx +++ b/components/card/card.tsx @@ -30,7 +30,7 @@ export default class Card extends React.PureComponent { return (
{childrenWithProps}
diff --git a/components/next_steps_view/__snapshots__/next_steps_view.test.tsx.snap b/components/next_steps_view/__snapshots__/next_steps_view.test.tsx.snap index 33bcdd6199bb..7f43a9ef6f4a 100644 --- a/components/next_steps_view/__snapshots__/next_steps_view.test.tsx.snap +++ b/components/next_steps_view/__snapshots__/next_steps_view.test.tsx.snap @@ -45,10 +45,23 @@ exports[`components/next_steps_view should match snapshot 1`] = ` className="NextStepsView__body-main" > +
+ +
{ const baseProps = { + currentUserId: 'user_id', + preferences: [], skuName: '', + actions: { + savePreferences: jest.fn(), + }, }; test('should match snapshot', () => { diff --git a/components/next_steps_view/next_steps_view.tsx b/components/next_steps_view/next_steps_view.tsx index 24df56c5e986..d7edef432c71 100644 --- a/components/next_steps_view/next_steps_view.tsx +++ b/components/next_steps_view/next_steps_view.tsx @@ -10,48 +10,17 @@ import {PreferenceType} from 'mattermost-redux/types/preferences'; import Accordion from 'components/accordion'; import Card from 'components/card/card'; import professionalLogo from 'images/cloud-logos/professional.svg'; -import {RecommendedNextSteps, Preferences} from 'utils/constants'; -import {localizeMessage} from 'utils/utils'; +import {Preferences} from 'utils/constants'; -import CompleteProfileStep from './steps/complete_profile_step'; +import {Steps, StepType} from './steps'; import './next_steps_view.scss'; -export type StepComponentProps = { - id: string; - onSkip: (id: string) => void; - onFinish: (id: string) => void; -} - -type StepType = { - id: string; - title: string; - component: React.ComponentType; -} - -const steps: StepType[] = [ - { - id: RecommendedNextSteps.COMPLETE_PROFILE, - title: localizeMessage('next_steps_view.titles.completeProfile', 'Complete your profile'), - component: CompleteProfileStep, - }, - { - id: RecommendedNextSteps.TEAM_SETUP, - title: localizeMessage('next_steps_view.titles.teamSetup', 'Name your team'), - component: CompleteProfileStep, - }, - { - id: RecommendedNextSteps.INVITE_MEMBERS, - title: localizeMessage('next_steps_view.titles.inviteMembers', 'Invite members to the team'), - component: CompleteProfileStep, - }, -]; - type Props = { currentUserId: string; preferences: PreferenceType[]; skuName: string; actions: { - savePreferences: (userId: string, preferences: PreferenceType[]) => Promise<{data: boolean}>; + savePreferences: (userId: string, preferences: PreferenceType[]) => void; }; }; @@ -122,11 +91,11 @@ export default class NextStepsView extends React.PureComponent { } nextStep = (setExpanded: (expandedKey: string) => void, id: string) => { - const currentIndex = steps.findIndex((step) => step.id === id); - if ((currentIndex + 1) > (steps.length - 1)) { + const currentIndex = Steps.findIndex((step) => step.id === id); + if ((currentIndex + 1) > (Steps.length - 1)) { this.setState({showFinalScreen: true}); } else { - setExpanded(steps[currentIndex + 1].id); + setExpanded(Steps[currentIndex + 1].id); } } @@ -179,11 +148,11 @@ export default class NextStepsView extends React.PureComponent { } renderMainBody = () => { - const renderedSteps = steps.map(this.renderStep); + const renderedSteps = Steps.map(this.renderStep); return ( <> - + {(setExpanded, expandedKey) => { return ( <> diff --git a/components/next_steps_view/steps.ts b/components/next_steps_view/steps.ts new file mode 100644 index 000000000000..3551ece726a0 --- /dev/null +++ b/components/next_steps_view/steps.ts @@ -0,0 +1,37 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {RecommendedNextSteps} from 'utils/constants'; +import {localizeMessage} from 'utils/utils'; + +import CompleteProfileStep from './steps/complete_profile_step'; + +export type StepComponentProps = { + id: string; + onSkip: (id: string) => void; + onFinish: (id: string) => void; +} + +export type StepType = { + id: string; + title: string; + component: React.ComponentType; +} + +export const Steps: StepType[] = [ + { + id: RecommendedNextSteps.COMPLETE_PROFILE, + title: localizeMessage('next_steps_view.titles.completeProfile', 'Complete your profile'), + component: CompleteProfileStep, + }, + { + id: RecommendedNextSteps.TEAM_SETUP, + title: localizeMessage('next_steps_view.titles.teamSetup', 'Name your team'), + component: CompleteProfileStep, + }, + { + id: RecommendedNextSteps.INVITE_MEMBERS, + title: localizeMessage('next_steps_view.titles.inviteMembers', 'Invite members to the team'), + component: CompleteProfileStep, + }, +]; \ No newline at end of file diff --git a/components/next_steps_view/steps/complete_profile_step.tsx b/components/next_steps_view/steps/complete_profile_step.tsx index 706a80ba6a2e..7e20e60e6239 100644 --- a/components/next_steps_view/steps/complete_profile_step.tsx +++ b/components/next_steps_view/steps/complete_profile_step.tsx @@ -3,7 +3,7 @@ import React from 'react'; -import {StepComponentProps} from '../next_steps_view'; +import {StepComponentProps} from '../steps'; type Props = StepComponentProps & { }; From c604880214f978933ebfcdd62976fccdc159b4bc Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Thu, 9 Jul 2020 13:52:38 -0400 Subject: [PATCH 20/46] Shifted around the screen changing and added final screen placeholder --- .../next_steps_view/next_steps_view.scss | 1 + .../next_steps_view/next_steps_view.tsx | 74 ++++++++++--------- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/components/next_steps_view/next_steps_view.scss b/components/next_steps_view/next_steps_view.scss index 8b58cb640c5f..ecf2cd3c62dc 100644 --- a/components/next_steps_view/next_steps_view.scss +++ b/components/next_steps_view/next_steps_view.scss @@ -4,6 +4,7 @@ background-position-y: bottom; background-position-x: -305px; background-size: calc(100% + 305px); + height: 100%; } .NextStepsView__header { diff --git a/components/next_steps_view/next_steps_view.tsx b/components/next_steps_view/next_steps_view.tsx index d7edef432c71..8fcab3787823 100644 --- a/components/next_steps_view/next_steps_view.tsx +++ b/components/next_steps_view/next_steps_view.tsx @@ -143,7 +143,9 @@ export default class NextStepsView extends React.PureComponent { renderFinalScreen = () => { // TODO return ( -
+
+ {'Placeholder for Final Screen'} +
); } @@ -152,40 +154,6 @@ export default class NextStepsView extends React.PureComponent { return ( <> - - {(setExpanded, expandedKey) => { - return ( - <> - {renderedSteps.map((step) => step(setExpanded, expandedKey))} - - ); - }} - -
- -
- - ); - } - - render() { - let mainBody = this.renderMainBody(); - if (this.state.showFinalScreen) { - mainBody = this.renderFinalScreen(); - } - - return ( -
@@ -204,10 +172,44 @@ export default class NextStepsView extends React.PureComponent {
- {mainBody} + + {(setExpanded, expandedKey) => { + return ( + <> + {renderedSteps.map((step) => step(setExpanded, expandedKey))} + + ); + }} + +
+ +
+ + ); + } + + render() { + let mainBody = this.renderMainBody(); + if (this.state.showFinalScreen) { + mainBody = this.renderFinalScreen(); + } + + return ( +
+ {mainBody}
); } From b2dc9c383207cd331882e28e2e3979923f24feca Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Fri, 10 Jul 2020 12:08:56 -0400 Subject: [PATCH 21/46] Translations and wizard navigation button styling --- .../next_steps_view/next_steps_view.scss | 39 ++++++++++++++++++- .../steps/complete_profile_step.tsx | 33 ++++++++++++++-- i18n/en.json | 6 +++ 3 files changed, 73 insertions(+), 5 deletions(-) diff --git a/components/next_steps_view/next_steps_view.scss b/components/next_steps_view/next_steps_view.scss index ecf2cd3c62dc..3e40f180118f 100644 --- a/components/next_steps_view/next_steps_view.scss +++ b/components/next_steps_view/next_steps_view.scss @@ -109,6 +109,41 @@ } } +.Card { + .NextStepsView__button { + padding: 12px 16px; + line-height: 12px; + font-size: 14px; + font-weight: 600; + border: none; + + &.cancel { + background: var(--center-channel-bg); + color: var(--button-bg); + } + + &.confirm { + background: var(--button-bg); + border-radius: 4px; + color: var(--button-color); + + &.disabled { + color: rgba(var(--center-channel-color-rgb), 0.64); + background: rgba(var(--center-channel-color-rgb), 0.08); + } + } + } + .NextStepsView__wizardButtons { + display: flex; + justify-content: flex-end; + margin: 4px; + + .NextStepsView__button + .NextStepsView__button { + margin-left: 8px; + } + } +} + .Card__header.expanded .NextStepsView__cardHeaderBadge { color: var(--button-color); background-color: var(--sidebar-text-active-border); @@ -157,7 +192,7 @@ .NextStepsView__header { padding: 24px 28px; } - + .NextStepsView__header-headerTopText { font-size: 24px; line-height: 32px; @@ -181,4 +216,4 @@ .NextStepsView__body-main { min-width: 536px; } -} \ No newline at end of file +} diff --git a/components/next_steps_view/steps/complete_profile_step.tsx b/components/next_steps_view/steps/complete_profile_step.tsx index 7e20e60e6239..dde27e260cd4 100644 --- a/components/next_steps_view/steps/complete_profile_step.tsx +++ b/components/next_steps_view/steps/complete_profile_step.tsx @@ -2,6 +2,7 @@ // See LICENSE.txt for license information. import React from 'react'; +import {FormattedMessage} from 'react-intl'; import {StepComponentProps} from '../steps'; @@ -12,13 +13,39 @@ type State = { }; export default class CompleteProfileStep extends React.PureComponent { + onSkip = () => { + this.props.onSkip(this.props.id); + } + + onFinish = () => { + this.props.onFinish(this.props.id); + } + render() { return (
{'AAAAAAAA'} - - +
+ + +
); } -} \ No newline at end of file +} diff --git a/i18n/en.json b/i18n/en.json index 868f4a307325..cbb58a9c66cd 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -3057,7 +3057,13 @@ "navbar.viewInfo": "View Info", "navbar.viewPinnedPosts": "View Pinned Posts", "next_steps_view.allSetToGo": "You're all set to go!", + "next_steps_view.complete_profile_step.saveProfile": "Save profile", "next_steps_view.hereAreSomeNextSteps": "Here are some recommended next steps to help you collaborate", + "next_steps_view.skipForNow": "Skip for now", + "next_steps_view.skipGettingStarted": "Skip Getting Started", + "next_steps_view.titles.completeProfile": "Complete your profile", + "next_steps_view.titles.inviteMembers": "Invite members to the team", + "next_steps_view.titles.teamSetup": "Name your team", "next_steps_view.welcomeToMattermost": "Welcome to Mattermost", "no_results.channel_search.subtitle": "Check the spelling or try another search.", "no_results.channel_search.title": "No results for {channelName}", From 6d4aaefaeb0c2ff9df4648af7243e71550c37b4f Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Fri, 10 Jul 2020 12:47:06 -0400 Subject: [PATCH 22/46] Pick starting step based on which are finished, button styling fixes --- components/next_steps_view/next_steps_view.scss | 6 +++--- components/next_steps_view/next_steps_view.tsx | 12 +++++++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/components/next_steps_view/next_steps_view.scss b/components/next_steps_view/next_steps_view.scss index 3e40f180118f..e97b2e51008c 100644 --- a/components/next_steps_view/next_steps_view.scss +++ b/components/next_steps_view/next_steps_view.scss @@ -111,8 +111,8 @@ .Card { .NextStepsView__button { - padding: 12px 16px; - line-height: 12px; + padding: 12px 20px; + line-height: 14px; font-size: 14px; font-weight: 600; border: none; @@ -128,7 +128,7 @@ color: var(--button-color); &.disabled { - color: rgba(var(--center-channel-color-rgb), 0.64); + color: rgba(var(--center-channel-color-rgb), 0.32); background: rgba(var(--center-channel-color-rgb), 0.08); } } diff --git a/components/next_steps_view/next_steps_view.tsx b/components/next_steps_view/next_steps_view.tsx index 8fcab3787823..79dd7e372013 100644 --- a/components/next_steps_view/next_steps_view.tsx +++ b/components/next_steps_view/next_steps_view.tsx @@ -67,6 +67,16 @@ export default class NextStepsView extends React.PureComponent { } } + getStartingStep = () => { + for (let i = 0; i < Steps.length; i++) { + if (!this.props.preferences.some((pref) => pref.name === Steps[i].id && pref.value)) { + return Steps[i].id; + } + } + + return Steps[0].id; + } + onSkip = (setExpanded: (expandedKey: string) => void) => { return (id: string) => { this.nextStep(setExpanded, id); @@ -172,7 +182,7 @@ export default class NextStepsView extends React.PureComponent {
- + {(setExpanded, expandedKey) => { return ( <> From d140a63e5ee2131166003cb6b46dcdb449e3eb60 Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Fri, 10 Jul 2020 15:49:44 -0400 Subject: [PATCH 23/46] Allow for getting out of next steps view by switching channels --- components/channel_view/channel_view.jsx | 5 +++-- components/channel_view/index.js | 3 ++- components/next_steps_view/steps.ts | 17 +++++++++++++++-- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/components/channel_view/channel_view.jsx b/components/channel_view/channel_view.jsx index 5c9ee6c4fb1c..ebf97b177745 100644 --- a/components/channel_view/channel_view.jsx +++ b/components/channel_view/channel_view.jsx @@ -54,7 +54,7 @@ export default class ChannelView extends React.PureComponent { const focusedPostId = props.match.params.postid; if (props.match.url !== state.url && props.channelId !== state.channelId) { - updatedState = {deferredPostView: ChannelView.createDeferredPostView(), url: props.match.url, focusedPostId}; + updatedState = {deferredPostView: ChannelView.createDeferredPostView(), url: props.match.url, focusedPostId, showNextSteps: false}; } if (props.channelId !== state.channelId) { @@ -79,6 +79,7 @@ export default class ChannelView extends React.PureComponent { url: props.match.url, channelId: props.channelId, deferredPostView: ChannelView.createDeferredPostView(), + showNextSteps: props.showNextSteps, }; } @@ -125,7 +126,7 @@ export default class ChannelView extends React.PureComponent { ); } - if (this.props.showNextSteps) { + if (this.state.showNextSteps) { return ( ); diff --git a/components/channel_view/index.js b/components/channel_view/index.js index b5969d26f08c..89438c8c4655 100644 --- a/components/channel_view/index.js +++ b/components/channel_view/index.js @@ -16,6 +16,7 @@ import {getDirectTeammate} from 'utils/utils.jsx'; import {TutorialSteps, Preferences} from 'utils/constants'; import {goToLastViewedChannel} from 'actions/views/channel'; +import {showNextSteps} from 'components/next_steps_view/steps'; import ChannelView from './channel_view.jsx'; @@ -57,7 +58,7 @@ function mapStateToProps(state) { channelRolesLoading, deactivatedChannel: channel ? getDeactivatedChannel(state, channel.id) : false, showTutorial: enableTutorial && tutorialStep <= TutorialSteps.INTRO_SCREENS, - showNextSteps: true, + showNextSteps: showNextSteps(state), channelIsArchived: channel ? channel.delete_at !== 0 : false, viewArchivedChannels, }; diff --git a/components/next_steps_view/steps.ts b/components/next_steps_view/steps.ts index 3551ece726a0..4cfe0750ead4 100644 --- a/components/next_steps_view/steps.ts +++ b/components/next_steps_view/steps.ts @@ -1,7 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import {createSelector} from 'reselect'; -import {RecommendedNextSteps} from 'utils/constants'; +import {makeGetCategory} from 'mattermost-redux/selectors/entities/preferences'; +import {GlobalState} from 'mattermost-redux/types/store'; + +import {RecommendedNextSteps, Preferences} from 'utils/constants'; import {localizeMessage} from 'utils/utils'; import CompleteProfileStep from './steps/complete_profile_step'; @@ -34,4 +38,13 @@ export const Steps: StepType[] = [ title: localizeMessage('next_steps_view.titles.inviteMembers', 'Invite members to the team'), component: CompleteProfileStep, }, -]; \ No newline at end of file +]; + +const getCategory = makeGetCategory(); +export const showNextSteps = createSelector( + (state: GlobalState) => getCategory(state, Preferences.RECOMMENDED_NEXT_STEPS), + (stepPreferences) => { + const checkPref = (step: StepType) => stepPreferences.some((pref) => pref.name === step.id && pref.value); + return !Steps.every(checkPref); + } +); From c63851c4aa3a48226464e12854f73ebc4eaff31a Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Mon, 13 Jul 2020 14:00:57 -0400 Subject: [PATCH 24/46] PR feedback --- .../next_steps_view/next_steps_view.scss | 32 ++++++++++--------- .../next_steps_view/next_steps_view.tsx | 3 ++ 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/components/next_steps_view/next_steps_view.scss b/components/next_steps_view/next_steps_view.scss index e97b2e51008c..b94e473764e3 100644 --- a/components/next_steps_view/next_steps_view.scss +++ b/components/next_steps_view/next_steps_view.scss @@ -84,6 +84,7 @@ border-radius: 14px; display: flex; justify-content: center; + transition: all 200ms ease-in-out; span { align-self: center; @@ -183,6 +184,21 @@ } } +@media screen and (max-width: 1020px) { + .NextStepsView__header-logo { + display: none; + } + + .NextStepsView__body-graphic { + flex: 0 0 0%; + min-width: 0; + } + + .NextStepsView__body-main { + padding: 0 40px; + } +} + @media screen and (max-width: 768px) { #app-content.NextStepsView { background-size: auto; @@ -200,20 +216,6 @@ .NextStepsView__body-main { flex: 1 1 100%; - } -} - -@media screen and (max-width: 1020px) { - .NextStepsView__header-logo { - display: none; - } - - .NextStepsView__body-graphic { - flex: 0 0 0%; - min-width: 0; - } - - .NextStepsView__body-main { - min-width: 536px; + padding: 0 12px; } } diff --git a/components/next_steps_view/next_steps_view.tsx b/components/next_steps_view/next_steps_view.tsx index 79dd7e372013..e59864a33d93 100644 --- a/components/next_steps_view/next_steps_view.tsx +++ b/components/next_steps_view/next_steps_view.tsx @@ -104,6 +104,8 @@ export default class NextStepsView extends React.PureComponent { const currentIndex = Steps.findIndex((step) => step.id === id); if ((currentIndex + 1) > (Steps.length - 1)) { this.setState({showFinalScreen: true}); + } else if (this.isStepComplete(Steps[currentIndex + 1].id)) { + this.nextStep(setExpanded, Steps[currentIndex + 1].id); } else { setExpanded(Steps[currentIndex + 1].id); } @@ -133,6 +135,7 @@ export default class NextStepsView extends React.PureComponent { +
Date: Thu, 16 Jul 2020 13:52:48 -0400 Subject: [PATCH 30/46] PR feedback --- components/next_steps_view/next_steps_view.scss | 6 +++--- components/next_steps_view/steps/complete_profile_step.tsx | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/components/next_steps_view/next_steps_view.scss b/components/next_steps_view/next_steps_view.scss index ee7f73d53b6c..d98761166b19 100644 --- a/components/next_steps_view/next_steps_view.scss +++ b/components/next_steps_view/next_steps_view.scss @@ -8,7 +8,7 @@ } .NextStepsView__header { - padding: 32px 40px; + padding: 32px 40px 24px 40px; display: flex; } @@ -32,7 +32,7 @@ .NextStepsView__header-logo { margin-left: auto; - align-self: center; + margin-top: 8px; } .NextStepsView__body { @@ -174,7 +174,7 @@ } .NextStepsView__cardHeader { - //padding: 0; + padding: 0; transition: padding 0.3s ease-in-out; > span { diff --git a/components/next_steps_view/steps/complete_profile_step.tsx b/components/next_steps_view/steps/complete_profile_step.tsx index dde27e260cd4..659fcaca415b 100644 --- a/components/next_steps_view/steps/complete_profile_step.tsx +++ b/components/next_steps_view/steps/complete_profile_step.tsx @@ -26,7 +26,7 @@ export default class CompleteProfileStep extends React.PureComponent {'AAAAAAAA'}
- + */} +
+ +
); }; -export default PictureSelector; \ No newline at end of file +export default PictureSelector; From 7e2e31aabd36a61d18d3efd0c44b563aeb3c6a45 Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Wed, 22 Jul 2020 16:50:16 -0400 Subject: [PATCH 37/46] Styling and a couple tweaks --- components/picture_selector.scss | 54 ++++++++++++++++++++++++++++++++ components/picture_selector.tsx | 37 +++++++++++++++------- 2 files changed, 79 insertions(+), 12 deletions(-) create mode 100644 components/picture_selector.scss diff --git a/components/picture_selector.scss b/components/picture_selector.scss new file mode 100644 index 000000000000..87d9f72640ba --- /dev/null +++ b/components/picture_selector.scss @@ -0,0 +1,54 @@ +.PictureSelector { + display: flex; + flex-direction: column; + width: 96px; +} + +.PictureSelector__imageContainer { + height: 96px; + position: relative; +} + +.PictureSelector__image { + height: 100%; + border-radius: 100%; + background-position: 50% 50%; + background-size: cover; + border: 1px solid rgba(var(--center-channel-color-rgb), 0.08); +} + +.PictureSelector__selectButton { + position: absolute; + top: 0; + right: 0; + border-radius: 100%; + background-color: var(--center-channel-bg); + border: 1px solid rgba(var(--center-channel-color-rgb), 0.08); + box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.12); + padding: 4px; + width: 28px; + height: 28px; + display: flex; + justify-content: center; + + i { + font-size: 16px; + line-height: 18px; + color: rgba(var(--center-channel-color-rgb), 0.56); + align-self: center; + + &::before { + margin: 0; + } + } +} + +.PictureSelector__removeButton { + padding: 0; + border: none; + font-size: 12px; + font-weight: 600; + color: var(--button-bg); + margin-top: 12px; + background-color: transparent; +} diff --git a/components/picture_selector.tsx b/components/picture_selector.tsx index c73a4a313177..c4df19e0893a 100644 --- a/components/picture_selector.tsx +++ b/components/picture_selector.tsx @@ -7,6 +7,8 @@ import {FormattedMessage} from 'react-intl'; import {localizeMessage} from 'utils/utils'; import * as FileUtils from 'utils/file_utils'; +import './picture_selector.scss'; + type Props = { src?: string; defaultSrc?: string; @@ -49,7 +51,11 @@ const PictureSelector: React.FC = (props: Props) => { const handleRemove = () => { props.onRemove(); - setImage(undefined); + if (props.defaultSrc) { + setImage(props.defaultSrc); + } else { + setImage(undefined); + } }; useEffect(() => { @@ -62,6 +68,23 @@ const PictureSelector: React.FC = (props: Props) => { } }); + let removeButton; + if (image && image !== props.defaultSrc) { + removeButton = ( + + ); + } + return (
= (props: Props) => {
- + {removeButton}
); }; From b76aacb5892aeb7c804053a3c8fc74f2b8a290cb Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Wed, 22 Jul 2020 16:57:54 -0400 Subject: [PATCH 38/46] A few tests --- components/picture_selector.test.tsx | 49 ++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 components/picture_selector.test.tsx diff --git a/components/picture_selector.test.tsx b/components/picture_selector.test.tsx new file mode 100644 index 000000000000..1bd9b9e9a9b9 --- /dev/null +++ b/components/picture_selector.test.tsx @@ -0,0 +1,49 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {shallow} from 'enzyme'; + +import PictureSelector from 'components/picture_selector'; + +describe('components/picture_selector', () => { + const baseProps = { + onSelect: jest.fn(), + onRemove: jest.fn(), + }; + + test('should match snapshot, no picture selected', () => { + const wrapper = shallow( + , + ); + + expect(wrapper).toMatchSnapshot(); + }); + + test('should match snapshot, existing picture provided', () => { + const props = { + ...baseProps, + src: 'http:///url.com/picture.jpg', + defaultSrc: 'http:///url.com/default-picture.jpg', + }; + + const wrapper = shallow( + , + ); + + expect(wrapper).toMatchSnapshot(); + }); + + test('should match snapshot, default picture provided', () => { + const props = { + ...baseProps, + defaultSrc: 'http:///url.com/default-picture.jpg', + }; + + const wrapper = shallow( + , + ); + + expect(wrapper).toMatchSnapshot(); + }); +}); From 9ba4b8092f80cc163e041b1fc33f377f226389c9 Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Wed, 22 Jul 2020 17:05:31 -0400 Subject: [PATCH 39/46] Snapshots --- .../picture_selector.test.tsx.snap | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 components/__snapshots__/picture_selector.test.tsx.snap diff --git a/components/__snapshots__/picture_selector.test.tsx.snap b/components/__snapshots__/picture_selector.test.tsx.snap new file mode 100644 index 000000000000..97f16e8ce1bc --- /dev/null +++ b/components/__snapshots__/picture_selector.test.tsx.snap @@ -0,0 +1,115 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`components/picture_selector should match snapshot, default picture provided 1`] = ` +
+ +
+
+ +
+
+`; + +exports[`components/picture_selector should match snapshot, existing picture provided 1`] = ` +
+ +
+
+ +
+
+`; + +exports[`components/picture_selector should match snapshot, no picture selected 1`] = ` +
+ +
+
+ +
+
+`; From 9e3507c3d52db91ee6a3d74fe1a44823f7844b96 Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Wed, 22 Jul 2020 20:21:18 -0400 Subject: [PATCH 40/46] Type and i18n fixes --- components/next_steps_view/next_steps_view.test.tsx | 3 ++- i18n/en.json | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/components/next_steps_view/next_steps_view.test.tsx b/components/next_steps_view/next_steps_view.test.tsx index 532eb36bcd97..da37c5df7b8a 100644 --- a/components/next_steps_view/next_steps_view.test.tsx +++ b/components/next_steps_view/next_steps_view.test.tsx @@ -5,10 +5,11 @@ import React from 'react'; import {shallow} from 'enzyme'; import NextStepsView from 'components/next_steps_view/next_steps_view'; +import {TestHelper} from 'utils/test_helper'; describe('components/next_steps_view', () => { const baseProps = { - currentUserId: 'user_id', + currentUser: TestHelper.getUserMock(), preferences: [], skuName: '', actions: { diff --git a/i18n/en.json b/i18n/en.json index c98d8b0583c9..c2d32ceaaba6 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -3115,6 +3115,8 @@ "pending_post_actions.retry": "Retry", "permalink.error.access": "Permalink belongs to a deleted message or to a channel to which you do not have access.", "permalink.error.title": "Message Not Found", + "picture_selector.remove_picture": "Remove picture", + "picture_selector.select_button.ariaLabel": "Select picture", "post_body.check_for_out_of_channel_groups_mentions.message": "did not get notified by this mention because they are not in the channel. They cannot be added to the channel because they are not a member of the linked groups. To add them to this channel, they must be added to the linked groups.", "post_body.check_for_out_of_channel_mentions.link.and": " and ", "post_body.check_for_out_of_channel_mentions.link.private": "add them to this private channel", From cb02d04dd4ccde8c3e9fb05e1d4290e09f45ca7b Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Thu, 23 Jul 2020 09:49:31 -0400 Subject: [PATCH 41/46] PR feedback and test fixes --- .../picture_selector.test.tsx.snap | 118 ++++++++++++++++++ components/picture_selector.tsx | 29 +++-- i18n/en.json | 1 + 3 files changed, 135 insertions(+), 13 deletions(-) create mode 100644 components/__snapshots__/picture_selector.test.tsx.snap diff --git a/components/__snapshots__/picture_selector.test.tsx.snap b/components/__snapshots__/picture_selector.test.tsx.snap new file mode 100644 index 000000000000..f4221101ef6f --- /dev/null +++ b/components/__snapshots__/picture_selector.test.tsx.snap @@ -0,0 +1,118 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`components/picture_selector should match snapshot, default picture provided 1`] = ` +
+ +
+
+ +
+
+`; + +exports[`components/picture_selector should match snapshot, existing picture provided 1`] = ` +
+ +
+
+ +
+
+`; + +exports[`components/picture_selector should match snapshot, no picture selected 1`] = ` +
+ +
+
+ +
+
+`; diff --git a/components/picture_selector.tsx b/components/picture_selector.tsx index c4df19e0893a..ffd65fded83f 100644 --- a/components/picture_selector.tsx +++ b/components/picture_selector.tsx @@ -18,6 +18,8 @@ type Props = { }; const PictureSelector: React.FC = (props: Props) => { + const {src, defaultSrc, loadingPicture, onSelect, onRemove} = props; + const [image, setImage] = useState(); const [orientationStyles, setOrientationStyles] = useState<{transform: any; transformOrigin: any}>(); @@ -36,7 +38,7 @@ const PictureSelector: React.FC = (props: Props) => { }; reader.readAsArrayBuffer(file); - props.onSelect(file); + onSelect(file); } }; @@ -50,9 +52,9 @@ const PictureSelector: React.FC = (props: Props) => { }; const handleRemove = () => { - props.onRemove(); - if (props.defaultSrc) { - setImage(props.defaultSrc); + onRemove(); + if (defaultSrc) { + setImage(defaultSrc); } else { setImage(undefined); } @@ -60,21 +62,21 @@ const PictureSelector: React.FC = (props: Props) => { useEffect(() => { if (!image) { - if (props.src) { - setImage(props.src); - } else if (props.defaultSrc) { - setImage(props.defaultSrc); + if (src) { + setImage(src); + } else if (defaultSrc) { + setImage(defaultSrc); } } - }); + }, [src, setImage]); let removeButton; - if (image && image !== props.defaultSrc) { + if (image && image !== defaultSrc) { removeButton = (
From 44600ddcf38e8147c79141d1d7119f0cb85b8a7e Mon Sep 17 00:00:00 2001 From: Devin Binnie Date: Mon, 27 Jul 2020 16:20:14 -0400 Subject: [PATCH 44/46] Blur select button on select image --- components/picture_selector.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/picture_selector.tsx b/components/picture_selector.tsx index ffd65fded83f..7429dfa6fb25 100644 --- a/components/picture_selector.tsx +++ b/components/picture_selector.tsx @@ -24,8 +24,11 @@ const PictureSelector: React.FC = (props: Props) => { const [orientationStyles, setOrientationStyles] = useState<{transform: any; transformOrigin: any}>(); const inputRef: React.RefObject = React.createRef(); + const selectButton: React.RefObject = React.createRef(); const handleFileChange = (e: React.ChangeEvent) => { + selectButton.current?.blur(); + if (e.target.files && e.target.files[0]) { const file = e.target.files[0]; @@ -110,6 +113,7 @@ const PictureSelector: React.FC = (props: Props) => { }} />