Skip to content
This repository has been archived by the owner on Mar 13, 2024. It is now read-only.

[MM-27526] Onboarding Flow for End Users #6296

Merged
merged 57 commits into from
Aug 31, 2020
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
cbdfc57
[MM-26468][MM-26483] Progress Bar Common Component/Sidebar Next Steps…
devinbinnie Jul 13, 2020
7cf611c
[MM-26465] Background and general layout for cloud onboarding (#5823)
devinbinnie Jul 13, 2020
25cacae
[MM-26480] Card/Accordion Common Component (#5861)
devinbinnie Jul 16, 2020
a93932e
[MM-26470] Base Next Step Wizard Controller and Styling (#5893)
devinbinnie Jul 23, 2020
d461828
Merge remote-tracking branch 'upstream/master' into feature/cloud-onb…
devinbinnie Jul 24, 2020
77f56af
[MM-26466] Close Next Steps Modal and functionality (#5995)
devinbinnie Jul 28, 2020
0db4c7e
Screen transitions for loading screen and final screen
devinbinnie Jul 28, 2020
b523bd5
Transition screen
devinbinnie Jul 28, 2020
f997f8a
Fixed APNG issue
devinbinnie Jul 28, 2020
28de81d
Style for desktop and mobile
devinbinnie Jul 29, 2020
4b99a39
Fixed styling
devinbinnie Jul 30, 2020
e549f19
More fixes
devinbinnie Jul 30, 2020
c4fe1ca
Functionality and test fixes
devinbinnie Jul 30, 2020
f3b43cf
Dev PR feedback
devinbinnie Aug 4, 2020
79d18c9
UX PR feedback
devinbinnie Aug 4, 2020
fbd0184
[MM-27164] Picture Selector Common Component (#5973)
devinbinnie Aug 4, 2020
18a3ba3
[MM-26482] Textbox Common Component for Cloud Onboarding (#5904)
devinbinnie Aug 4, 2020
dea01e0
Merge branch 'feature/cloud-onboarding' into MM-26473
mattermod Aug 4, 2020
681f8a5
Merge branch 'master' into feature/cloud-onboarding
devinbinnie Aug 5, 2020
3a929f5
Adding a card for opening and setting preferences
nickmisasi Aug 5, 2020
73466bc
Merge branch 'feature/cloud-onboarding' into MM-27526
nickmisasi Aug 5, 2020
90e181e
Adding files
nickmisasi Aug 6, 2020
cf33468
[MM-26469] Complete Profile Step (#6077)
devinbinnie Aug 10, 2020
26701b5
[MM-26473] Tips and Next Steps screen (#6020)
devinbinnie Aug 10, 2020
ad19b71
[MM-26471] Team Profile Setup step (#6083)
devinbinnie Aug 11, 2020
014b369
Merge remote-tracking branch 'upstream/master' into feature/cloud-onb…
devinbinnie Aug 14, 2020
a131923
[MM-26472] Invite Members step (#6143)
devinbinnie Aug 17, 2020
2529aa0
[MM-27908] Updated Cloud Logo (#6190)
devinbinnie Aug 19, 2020
aba0ef5
[MM-27907] Fixed input focus colour and fixed placeholder focus on mu…
devinbinnie Aug 19, 2020
200fd7c
[MM-27159] Button States for Cloud Onboarding (#6189)
devinbinnie Aug 19, 2020
396ab95
[MM-27305] Card Component Refactor for smoothness (#6172)
devinbinnie Aug 19, 2020
1bdd166
[MM-27974] Unit Tests for Cloud Onboarding (#6208)
devinbinnie Aug 20, 2020
79ebba2
[MM-27923] Turn on Cloud Onboarding for Cloud-only instances (#6225)
devinbinnie Aug 21, 2020
97a2eca
Additions for notification step, merging Devin's branch into mine
nickmisasi Aug 25, 2020
742529f
Flow working, pre cleanup, pre merge of Devin's work
nickmisasi Aug 26, 2020
cd20015
Merged master into MM-27526, resolved a bunch of conflicts
nickmisasi Aug 27, 2020
0e6f0a1
Code clean up
nickmisasi Aug 27, 2020
a057113
Adding tests for my added components
nickmisasi Aug 27, 2020
26ec777
Put package-lock back to master
nickmisasi Aug 27, 2020
b51bb25
Fix bug that always redirects you to the next steps page
nickmisasi Aug 27, 2020
9e840e5
Change function name to avoid confusion
nickmisasi Aug 27, 2020
daa05d2
Code clean up, and this dang package-lock
nickmisasi Aug 27, 2020
f37cd6e
Fix one of the test issues
nickmisasi Aug 27, 2020
ede4e65
Fix other tests
nickmisasi Aug 27, 2020
0ce0841
fix alignment of body text in text_card_with_action component
nickmisasi Aug 28, 2020
7a845f4
Fix a few issues in PR review
nickmisasi Aug 28, 2020
f2b90ff
pushing to get help
nickmisasi Aug 28, 2020
eb885bf
Fixes and updating tests
nickmisasi Aug 28, 2020
7c6aaa4
Fix package-lock
nickmisasi Aug 28, 2020
b07a2a5
Fix package-lock
nickmisasi Aug 28, 2020
2cd637b
Remove package-lock changes
nickmisasi Aug 31, 2020
56b6c73
Increase transition time from 1 second to 3 seconds
nickmisasi Aug 31, 2020
1c115ab
Some fixes for UX
nickmisasi Aug 31, 2020
01babfb
Merge branch 'master' into MM-27526
nickmisasi Aug 31, 2020
6c781ab
add 2 tests to be sure that step filter logic is working based on use…
nickmisasi Aug 31, 2020
302a169
Fix tests
nickmisasi Aug 31, 2020
0621034
Fix test
nickmisasi Aug 31, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ exports[`components/next_steps_view should match snapshot 1`] = `
</div>
<NextStepsTips
animating={false}
isAdmin={true}
showFinalScreen={false}
stopAnimating={[Function]}
/>
Expand Down
3 changes: 2 additions & 1 deletion components/next_steps_view/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {bindActionCreators, Dispatch} from 'redux';

import {savePreferences} from 'mattermost-redux/actions/preferences';
import {makeGetCategory} from 'mattermost-redux/selectors/entities/preferences';
import {getCurrentUser} from 'mattermost-redux/selectors/entities/users';
import {getCurrentUser, isCurrentUserSystemAdmin} from 'mattermost-redux/selectors/entities/users';

import {setShowNextStepsView} from 'actions/views/next_steps';
import {GlobalState} from 'types/store';
Expand All @@ -20,6 +20,7 @@ function makeMapStateToProps() {
return (state: GlobalState) => {
return {
currentUser: getCurrentUser(state),
isAdmin: isCurrentUserSystemAdmin(state),
preferences: getCategory(state, Preferences.RECOMMENDED_NEXT_STEPS),
};
};
Expand Down
62 changes: 60 additions & 2 deletions components/next_steps_view/next_steps_tips.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import * as UserAgent from 'utils/user_agent';
import {ModalIdentifiers} from 'utils/constants';
import * as Utils from 'utils/utils';

import TeamMembersModal from 'components/team_members_modal';
import {toggleShortcutsModal} from 'actions/global_actions.jsx';

const seeAllApps = () => {
window.open('https://mattermost.com/download/#mattermostApps', '_blank');
};
Expand Down Expand Up @@ -73,13 +76,17 @@ const openAuthPage = (page: string) => {
browserHistory.push(`/admin_console/authentication/${page}`);
};

export default function NextStepsTips(props: {showFinalScreen: boolean; animating: boolean; stopAnimating: () => void}) {
export default function NextStepsTips(props: { showFinalScreen: boolean; animating: boolean; stopAnimating: () => void; isAdmin: boolean}) {
const dispatch = useDispatch();
const openPluginMarketplace = openModal({modalId: ModalIdentifiers.PLUGIN_MARKETPLACE, dialogType: MarketplaceModal});
const openMoreChannels = openModal({modalId: ModalIdentifiers.MORE_CHANNELS, dialogType: MoreChannels});
const openViewMembersModal = openModal({
modalId: ModalIdentifiers.TEAM_MEMBERS,
dialogType: TeamMembersModal,
});

let nonMobileTips;
if (!Utils.isMobile()) {
if (!Utils.isMobile() && props.isAdmin) {
nonMobileTips = (
<>
<Card expanded={true}>
Expand Down Expand Up @@ -146,6 +153,57 @@ export default function NextStepsTips(props: {showFinalScreen: boolean; animatin
</Card>
</>
);
} else if (!Utils.isMobile() && !props.isAdmin) {
nonMobileTips = (
<>
<Card expanded={true}>
<div className='Card__body'>
<h3>
<FormattedMessage
id='next_steps_view.tips.configureLogins'
defaultMessage='See who else is here'
/>
</h3>
<FormattedMessage
id='next_steps_view.tips.configureLogin.texts'
defaultMessage='Browse or search through the team members directory'
/>
<button
className='NextStepsView__button NextStepsView__finishButton primary'
onClick={() => openViewMembersModal(dispatch)}
>
<FormattedMessage
id='next_steps_view.tips.addPlugins.buttons'
defaultMessage='View team members'
/>
</button>
</div>
</Card>
<Card expanded={true}>
<div className='Card__body'>
<h3>
<FormattedMessage
id='next_steps_view.tips.addPluginss'
defaultMessage='Learn Keyboard Shortcuts'
/>
</h3>
<FormattedMessage
id='next_steps_view.tips.addPlugins.texts'
defaultMessage='Work more efficiently with Keyboard Shortcuts in Mattermost.'
/>
<button
className='NextStepsView__button NextStepsView__finishButton primary'
onClick={toggleShortcutsModal}
>
<FormattedMessage
id='next_steps_view.tips.addPlugins.buttons'
defaultMessage='See shortcuts'
/>
</button>
</div>
</Card>
</>
);
}

let downloadSection;
Expand Down
2 changes: 2 additions & 0 deletions components/next_steps_view/next_steps_view.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ jest.mock('components/next_steps_view/steps', () => ({
component: jest.fn(),
},
],
isStepForUser: () => true,
}));

describe('components/next_steps_view', () => {
const baseProps = {
currentUser: TestHelper.getUserMock(),
preferences: [],
isAdmin: true,
actions: {
setShowNextStepsView: jest.fn(),
savePreferences: jest.fn(),
Expand Down
31 changes: 20 additions & 11 deletions components/next_steps_view/next_steps_view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import onboardingSuccess from 'images/onboarding-success.svg';
import loadingIcon from 'images/spinner-48x48-blue.apng';
import {Preferences} from 'utils/constants';

import {Steps, StepType} from './steps';
import {Steps, StepType, isStepForUser} from './steps';
import './next_steps_view.scss';
import NextStepsTips from './next_steps_tips';

Expand All @@ -25,6 +25,7 @@ const TRANSITION_SCREEN_TIMEOUT = 1000;
type Props = {
currentUser: UserProfile;
preferences: PreferenceType[];
isAdmin: boolean;
actions: {
savePreferences: (userId: string, preferences: PreferenceType[]) => void;
setShowNextStepsView: (show: boolean) => void;
Expand All @@ -49,13 +50,13 @@ export default class NextStepsView extends React.PureComponent<Props, State> {
}

getStartingStep = () => {
for (let i = 0; i < Steps.length; i++) {
if (!this.isStepComplete(Steps[i].id)) {
return Steps[i].id;
for (let i = 0; i < this.steps.length; i++) {
if (!this.isStepComplete(this.steps[i].id)) {
return this.steps[i].id;
}
}

return Steps[0].id;
return this.steps[0].id;
}

onSkip = (setExpanded: (expandedKey: string) => void) => {
Expand Down Expand Up @@ -98,13 +99,13 @@ export default class NextStepsView extends React.PureComponent<Props, State> {
}

nextStep = (setExpanded: (expandedKey: string) => void, id: string) => {
const currentIndex = Steps.findIndex((step) => step.id === id);
if ((currentIndex + 1) > (Steps.length - 1)) {
const currentIndex = this.steps.findIndex((step) => step.id === id);
if (currentIndex + 1 > this.steps.length - 1) {
this.transitionToFinalScreen();
} else if (this.isStepComplete(Steps[currentIndex + 1].id)) {
this.nextStep(setExpanded, Steps[currentIndex + 1].id);
} else if (this.isStepComplete(this.steps[currentIndex + 1].id)) {
this.nextStep(setExpanded, this.steps[currentIndex + 1].id);
} else {
setExpanded(Steps[currentIndex + 1].id);
setExpanded(this.steps[currentIndex + 1].id);
}
}

Expand Down Expand Up @@ -183,8 +184,15 @@ export default class NextStepsView extends React.PureComponent<Props, State> {
);
}

// Filter the steps shown by checking if our user has any of the required roles for that step
filterSteps = (step: StepType) => {
return isStepForUser(step, this.props.currentUser.roles);
}

steps = Steps.filter(this.filterSteps);
nickmisasi marked this conversation as resolved.
Show resolved Hide resolved

renderMainBody = () => {
const renderedSteps = Steps.map(this.renderStep);
const renderedSteps = this.steps.map(this.renderStep);

return (
<div
Expand Down Expand Up @@ -253,6 +261,7 @@ export default class NextStepsView extends React.PureComponent<Props, State> {
showFinalScreen={this.state.showFinalScreen}
animating={this.state.animating}
stopAnimating={this.stopAnimating}
isAdmin={this.props.isAdmin}
/>
</section>
);
Expand Down
33 changes: 30 additions & 3 deletions components/next_steps_view/steps.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ describe('components/next_steps_view/steps', () => {
preferences: {
myPreferences: {},
},
users: {
currentUserId: 'current_user_id',
profiles: {
current_user_id: {roles: 'system_role'},
},
},
nickmisasi marked this conversation as resolved.
Show resolved Hide resolved
},
};

Expand Down Expand Up @@ -49,6 +55,12 @@ describe('components/next_steps_view/steps', () => {
'recommended_next_steps--hide': {name: 'hide', value: 'true'},
},
},
users: {
currentUserId: 'current_user_id',
profiles: {
current_user_id: {roles: 'system_role'},
},
},
},
};

Expand All @@ -65,9 +77,24 @@ describe('components/next_steps_view/steps', () => {
},
preferences: {
myPreferences: {
'recommended_next_steps--complete_profile': {name: 'complete_profile', value: 'true'},
'recommended_next_steps--team_setup': {name: 'team_setup', value: 'true'},
'recommended_next_steps--invite_members': {name: 'invite_members', value: 'true'},
'recommended_next_steps--complete_profile': {
name: 'complete_profile',
value: 'true',
},
'recommended_next_steps--team_setup': {
name: 'team_setup',
value: 'true',
},
'recommended_next_steps--invite_members': {
name: 'invite_members',
value: 'true',
},
},
},
users: {
currentUserId: 'current_user_id',
profiles: {
current_user_id: {roles: 'system_role'},
},
},
},
Expand Down
63 changes: 56 additions & 7 deletions components/next_steps_view/steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,93 @@ import {getLicense} from 'mattermost-redux/selectors/entities/general';
import {makeGetCategory} from 'mattermost-redux/selectors/entities/preferences';
import {UserProfile} from 'mattermost-redux/types/users';

import {getCurrentUser} from 'mattermost-redux/selectors/entities/users';
import {isEqual} from 'lodash';

import {GlobalState} from 'types/store';
import {RecommendedNextSteps, Preferences} from 'utils/constants';
import {localizeMessage} from 'utils/utils';

import CompleteProfileStep from './steps/complete_profile_step';
import SetupPreferencesStep from './steps/setup_preferences_step/setup_preferences_step';
import InviteMembersStep from './steps/invite_members_step';
import TeamProfileStep from './steps/team_profile_step';
import EnableNotificationsStep from './steps/enable_notifications_step/enable_notifications_step';

export type StepComponentProps = {
id: string;
currentUser: UserProfile;
onSkip: (id: string) => void;
onFinish: (id: string) => void;
}

export type StepType = {
id: string;
title: string;
component: React.ComponentType<StepComponentProps>;
}

// An array of all roles a user must have in order to see the step e.g. admins are both system_admin and system_user
// so you would require ['system_admin','system_user'] to match.
// to show step for all roles, leave the roles array blank.
roles: Array<string>;
};

export const Steps: StepType[] = [
{
id: RecommendedNextSteps.COMPLETE_PROFILE,
title: localizeMessage('next_steps_view.titles.completeProfile', 'Complete your profile'),
title: localizeMessage(
'next_steps_view.titles.completeProfile',
'Complete your profile'
),
component: CompleteProfileStep,
roles: [],
},
{
id: RecommendedNextSteps.TEAM_SETUP,
title: localizeMessage('next_steps_view.titles.teamSetup', 'Name your team'),
title: localizeMessage(
'next_steps_view.titles.teamSetup',
'Name your team'
),
roles: ['system_admin', 'system_user'],
devinbinnie marked this conversation as resolved.
Show resolved Hide resolved
component: TeamProfileStep,
},
{
id: RecommendedNextSteps.INVITE_MEMBERS,
title: localizeMessage('next_steps_view.titles.inviteMembers', 'Invite members to the team'),
title: localizeMessage(
'next_steps_view.titles.inviteMembers',
'Invite members to the team'
),
roles: ['system_admin', 'system_user'],
component: InviteMembersStep,
},
{
id: RecommendedNextSteps.NOTIFICATION_SETUP,
title: localizeMessage(
'next_steps_view.notificationSetup.setNotifications',
'Turn on notifications'
),
roles: ['system_user'],
component: EnableNotificationsStep,
},
{
id: RecommendedNextSteps.PREFERENCES_SETUP,
title: localizeMessage(
'next_steps_view.titles.preferenceSetup',
'Set your preferences'
),
roles: ['system_user'],
component: SetupPreferencesStep,
},
];

// Filter the steps shown by checking if our user has any of the required roles for that step
export function isStepForUser(step: StepType, roles: string): boolean {
const userRoles = roles.split(' ');
return (
isEqual(userRoles.sort(), step.roles.sort()) ||
step.roles.length === 0
);
}

const getCategory = makeGetCategory();
export const showNextSteps = createSelector(
(state: GlobalState) => getCategory(state, Preferences.RECOMMENDED_NEXT_STEPS),
Expand All @@ -65,8 +113,9 @@ export const showNextSteps = createSelector(

export const nextStepsNotFinished = createSelector(
(state: GlobalState) => getCategory(state, Preferences.RECOMMENDED_NEXT_STEPS),
(stepPreferences) => {
const checkPref = (step: StepType) => stepPreferences.some((pref) => pref.name === step.id && pref.value === 'true');
(state: GlobalState) => getCurrentUser(state),
(stepPreferences, currentUser) => {
const checkPref = (step: StepType) => stepPreferences.some((pref) => (pref.name === step.id && pref.value === 'true') || !isStepForUser(step, currentUser.roles));
return !Steps.every(checkPref);
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`components/next_steps_view/steps/setup_preferences_step should match snapshot 1`] = `
<TextCardWithAction
buttonDefaultMessage="Enable notifications"
buttonMessageId="next_steps_view.notificationSetup.enableNotifications"
cardBodyDefaultMessage="We recommend enabling desktop notifications so you don’t miss any important communications."
cardBodyMessageId="next_steps_view.notificationSetup"
onClick={[Function]}
/>
`;
Loading