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

[MM-27164] Picture Selector Common Component #5973

Merged
merged 51 commits into from
Aug 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
6308348
WIP
devinbinnie Jun 25, 2020
a2450a8
WIP
devinbinnie Jun 30, 2020
d2bdbac
Merge remote-tracking branch 'upstream/master' into MM-26465
devinbinnie Jun 30, 2020
6f376e3
[MM-26465] Background and general layout for cloud onboarding
devinbinnie Jun 30, 2020
776823e
Mobile view, lint and type fixes, added a test file for later use
devinbinnie Jun 30, 2020
630cc19
More test fixes
devinbinnie Jun 30, 2020
afa668e
UX feedback
devinbinnie Jul 2, 2020
7f57467
Replaced dumb comment with useful one
devinbinnie Jul 3, 2020
05521d8
Turn off graphic at 1020px
devinbinnie Jul 3, 2020
b7d78f1
WIP
devinbinnie Jul 2, 2020
88df376
Initial card style
devinbinnie Jul 2, 2020
b8e7ea8
Collapse functionality (no animation)
devinbinnie Jul 2, 2020
d62b7ef
WIP
devinbinnie Jul 3, 2020
6b16a03
Rest of accordion common component and some animation
devinbinnie Jul 6, 2020
60ad53a
Lint, type and test fixes
devinbinnie Jul 6, 2020
d56417c
Updated snapshot
devinbinnie Jul 7, 2020
0027735
Reduce nesting
devinbinnie Jul 7, 2020
839f962
WIP - Wiring for step wizard
devinbinnie Jul 8, 2020
e6e10cd
Skip getting started link, hook for final page
devinbinnie Jul 9, 2020
7376698
Moved steps into its own constants file, type and test fixes
devinbinnie Jul 9, 2020
c604880
Shifted around the screen changing and added final screen placeholder
devinbinnie Jul 9, 2020
b2dc9c3
Translations and wizard navigation button styling
devinbinnie Jul 10, 2020
6d4aaef
Pick starting step based on which are finished, button styling fixes
devinbinnie Jul 10, 2020
d140a63
Allow for getting out of next steps view by switching channels
devinbinnie Jul 10, 2020
c63851c
PR feedback
devinbinnie Jul 13, 2020
ba4f642
PR feedback
devinbinnie Jul 15, 2020
677dfd2
blank
devinbinnie Jul 15, 2020
e47ddf4
Change style of complete card header to be more like the regular one
devinbinnie Jul 16, 2020
70dce26
Fixed background on complete step
devinbinnie Jul 16, 2020
58fcc44
Merge branch 'feature/cloud-onboarding' into MM-26470
devinbinnie Jul 16, 2020
98628b1
Merge'd
devinbinnie Jul 16, 2020
202f215
PR feedback
devinbinnie Jul 16, 2020
2904373
PR feedback
devinbinnie Jul 16, 2020
f429567
Removed translation
devinbinnie Jul 16, 2020
1d8c6c0
Fixed box shadow transition on card
devinbinnie Jul 21, 2020
0c856a5
Removed duplicate logic
devinbinnie Jul 21, 2020
8615269
WIP
devinbinnie Jul 21, 2020
05bfd36
Functional component that works
devinbinnie Jul 22, 2020
7e2e31a
Styling and a couple tweaks
devinbinnie Jul 22, 2020
b76aacb
A few tests
devinbinnie Jul 22, 2020
9ba4b80
Snapshots
devinbinnie Jul 22, 2020
9e3507c
Type and i18n fixes
devinbinnie Jul 23, 2020
cb02d04
PR feedback and test fixes
devinbinnie Jul 23, 2020
3b49a13
Merge branch 'MM-27164' of https://github.com/devinbinnie/mattermost-…
devinbinnie Jul 23, 2020
999d90e
Merge branch 'feature/cloud-onboarding' into MM-27164
devinbinnie Jul 23, 2020
4e81a1b
Added button hover states
devinbinnie Jul 27, 2020
9eb0fcc
PR feedback
devinbinnie Jul 27, 2020
44600dd
Blur select button on select image
devinbinnie Jul 27, 2020
bdf625d
Blur on click, not on select image
devinbinnie Jul 28, 2020
c78b9cb
Update components/picture_selector.tsx
devinbinnie Jul 28, 2020
9896a18
Merge branch 'feature/cloud-onboarding' into MM-27164
devinbinnie Jul 30, 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
118 changes: 118 additions & 0 deletions components/__snapshots__/picture_selector.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`components/picture_selector should match snapshot, default picture provided 1`] = `
<div
className="PictureSelector"
>
<input
accept=".jpg,.png,.bmp"
aria-hidden={true}
className="PictureSelector__input"
data-testid="PictureSelector__input"
onChange={[Function]}
tabIndex={-1}
type="file"
/>
<div
className="PictureSelector__imageContainer"
>
<div
aria-label="Picture selector image"
className="PictureSelector__image"
style={
Object {
"backgroundImage": "url(undefined)",
}
}
/>
<button
aria-label="Select picture"
className="PictureSelector__selectButton"
data-testid="PictureSelector__selectButton"
onClick={[Function]}
>
<i
className="icon icon-pencil-outline"
/>
</button>
</div>
</div>
`;

exports[`components/picture_selector should match snapshot, existing picture provided 1`] = `
<div
className="PictureSelector"
>
<input
accept=".jpg,.png,.bmp"
aria-hidden={true}
className="PictureSelector__input"
data-testid="PictureSelector__input"
onChange={[Function]}
tabIndex={-1}
type="file"
/>
<div
className="PictureSelector__imageContainer"
>
<div
aria-label="Picture selector image"
className="PictureSelector__image"
style={
Object {
"backgroundImage": "url(undefined)",
}
}
/>
<button
aria-label="Select picture"
className="PictureSelector__selectButton"
data-testid="PictureSelector__selectButton"
onClick={[Function]}
>
<i
className="icon icon-pencil-outline"
/>
</button>
</div>
</div>
`;

exports[`components/picture_selector should match snapshot, no picture selected 1`] = `
<div
className="PictureSelector"
>
<input
accept=".jpg,.png,.bmp"
aria-hidden={true}
className="PictureSelector__input"
data-testid="PictureSelector__input"
onChange={[Function]}
tabIndex={-1}
type="file"
/>
<div
className="PictureSelector__imageContainer"
>
<div
aria-label="Picture selector image"
className="PictureSelector__image"
style={
Object {
"backgroundImage": "url(undefined)",
}
}
/>
<button
aria-label="Select picture"
className="PictureSelector__selectButton"
data-testid="PictureSelector__selectButton"
onClick={[Function]}
>
<i
className="icon icon-pencil-outline"
/>
</button>
</div>
</div>
`;
3 changes: 1 addition & 2 deletions components/next_steps_view/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@ function makeMapStateToProps() {

return (state: GlobalState) => {
const license = getLicense(state);
const currentUser = getCurrentUser(state);

return {
currentUserId: currentUser.id,
currentUser: getCurrentUser(state),
preferences: getCategory(state, Preferences.RECOMMENDED_NEXT_STEPS),
skuName: license.SkuShortName,
};
Expand Down
3 changes: 2 additions & 1 deletion components/next_steps_view/next_steps_view.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
24 changes: 14 additions & 10 deletions components/next_steps_view/next_steps_view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import classNames from 'classnames';
import {FormattedMessage} from 'react-intl';

import {PreferenceType} from 'mattermost-redux/types/preferences';
import {UserProfile} from 'mattermost-redux/types/users';

import Accordion from 'components/accordion';
import Card from 'components/card/card';
Expand All @@ -16,7 +17,7 @@ import {Steps, StepType} from './steps';
import './next_steps_view.scss';

type Props = {
currentUserId: string;
currentUser: UserProfile;
preferences: PreferenceType[];
skuName: string;
actions: {
Expand Down Expand Up @@ -86,9 +87,9 @@ export default class NextStepsView extends React.PureComponent<Props, State> {

onFinish = (setExpanded: (expandedKey: string) => void) => {
return (id: string) => {
this.props.actions.savePreferences(this.props.currentUserId, [{
this.props.actions.savePreferences(this.props.currentUser.id, [{
category: Preferences.RECOMMENDED_NEXT_STEPS,
user_id: this.props.currentUserId,
user_id: this.props.currentUser.id,
name: id,
value: 'true',
}]);
Expand Down Expand Up @@ -117,35 +118,38 @@ export default class NextStepsView extends React.PureComponent<Props, State> {
}

renderStep = (step: StepType, index: number) => {
const {id, title} = step;

let icon = (
<div className='NextStepsView__cardHeaderBadge'>
<span>{index + 1}</span>
</div>
);
if (this.isStepComplete(step.id)) {
if (this.isStepComplete(id)) {
icon = (
<i className='icon icon-check-circle'/>
);
}

return (setExpanded: (expandedKey: string) => void, expandedKey: string) => (
<Card
className={classNames({complete: this.isStepComplete(step.id)})}
expanded={expandedKey === step.id}
className={classNames({complete: this.isStepComplete(id)})}
expanded={expandedKey === id}
>
<Card.Header>
<button
onClick={() => setExpanded(step.id)}
disabled={this.isStepComplete(step.id)}
onClick={() => setExpanded(id)}
disabled={this.isStepComplete(id)}
className='NextStepsView__cardHeader'
>
{icon}
<span>{step.title}</span>
<span>{title}</span>
</button>
</Card.Header>
<Card.Body>
<step.component
id={step.id}
id={id}
currentUser={this.props.currentUser}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think we can destructure props

onFinish={this.onFinish(setExpanded)}
onSkip={this.onSkip(setExpanded)}
/>
Expand Down
2 changes: 2 additions & 0 deletions components/next_steps_view/steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import {createSelector} from 'reselect';

import {makeGetCategory} from 'mattermost-redux/selectors/entities/preferences';
import {UserProfile} from 'mattermost-redux/types/users';

import {GlobalState} from 'types/store';
import {RecommendedNextSteps, Preferences} from 'utils/constants';
Expand All @@ -12,6 +13,7 @@ import CompleteProfileStep from './steps/complete_profile_step';

export type StepComponentProps = {
id: string;
currentUser: UserProfile;
onSkip: (id: string) => void;
onFinish: (id: string) => void;
}
Expand Down
40 changes: 39 additions & 1 deletion components/next_steps_view/steps/complete_profile_step.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,25 @@
import React from 'react';
import {FormattedMessage} from 'react-intl';

import PictureSelector from 'components/picture_selector';
import * as Utils from 'utils/utils';

import {StepComponentProps} from '../steps';

type Props = StepComponentProps & {
};

type State = {
profilePicture?: File;
};

export default class CompleteProfileStep extends React.PureComponent<Props, State> {
constructor(props: Props) {
super(props);

this.state = {};
}

onSkip = () => {
this.props.onSkip(this.props.id);
}
Expand All @@ -21,10 +31,38 @@ export default class CompleteProfileStep extends React.PureComponent<Props, Stat
this.props.onFinish(this.props.id);
}

onSelectPicture = (profilePicture: File) => {
this.setState({profilePicture});
}

onRemovePicture = () => {
this.setState({profilePicture: undefined});
}

render() {
const {currentUser} = this.props;

// Make sure picture has been set
const pictureSrc = currentUser.last_picture_update ? Utils.imageURLForUser(currentUser.id, currentUser.last_picture_update) : undefined;
const defaultSrc = Utils.defaultImageURLForUser(currentUser.id);

return (
<div>
{'AAAAAAAA'}
<div
style={{

// TODO temp for textbox demo
margin: '24px',
minHeight: '200px',
}}
>
<PictureSelector
onSelect={this.onSelectPicture}
onRemove={this.onRemovePicture}
src={pictureSrc}
defaultSrc={defaultSrc}
/>
</div>
<div className='NextStepsView__wizardButtons'>
{/* <button
className='NextStepsView__button cancel'
Expand Down
80 changes: 80 additions & 0 deletions components/picture_selector.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
.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: 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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we make this more specific with a class? We might end up battling it later with another icon. 🤷‍♀️

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already wrapped around the PictureSelector__selectButton class, meaning it only affects icons in that scope, and I can't see us using this class and an icon within it for something else.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HTML elements do have more specificity and it can still clash if we are using a sibling component. I have had this issue multiple times when trying to add a new element to the RHS for example. I think we should strive to target specificity only with classes.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, yes. But I don't think we need to be so specific with this case. There's not going to be any sibling components of PictureSelector__selectButton, it's pretty self-contained and I don't see any need to change to allow consumers to add their own select button.

font-size: 16px;
line-height: 18px;
color: rgba(var(--center-channel-color-rgb), 0.56);
align-self: center;

&::before {
margin: 0;
}
}

&:hover {
border: 1px solid rgba(var(--center-channel-color-rgb), 0.16);
box-shadow: 0px 6px 14px rgba(0, 0, 0, 0.12);

i {
color: rgba(var(--center-channel-color-rgb), 0.72);
}
}

&:active {
background: linear-gradient(0deg, rgba(22, 109, 224, 0.08), rgba(22, 109, 224, 0.08)), #FFFFFF;
border: 1px solid rgba(22, 109, 224, 0.16);

i {
color: var(--button-bg);
}
}

&:focus {
border: 2px solid #166DE0;
}
}

.PictureSelector__removeButton {
padding: 0;
border: none;
font-size: 12px;
font-weight: 600;
color: var(--button-bg);
margin-top: 12px;
background-color: transparent;

&:focus {
text-decoration-line: underline;
}
}
Loading