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

Commit

Permalink
MM-12067: Allow to remove the user profile picture (#1785)
Browse files Browse the repository at this point in the history
* MM-12067: Allow to remove the user profile picture

* Upgrade to the last mattermost-redux version

* Simplification of defaultImageURLForUser util function

* Preventing default directly from the SettingPicture component
  • Loading branch information
jespino committed Nov 15, 2018
1 parent cb385d2 commit fc1f115
Show file tree
Hide file tree
Showing 9 changed files with 295 additions and 51 deletions.
137 changes: 137 additions & 0 deletions components/__snapshots__/setting_picture.test.jsx.snap
Expand Up @@ -713,3 +713,140 @@ exports[`components/SettingItemMin should match snapshot, team icon on source 1`
</li>
</ul>
`;

exports[`components/SettingItemMin should match snapshot, user icon on source 1`] = `
<ul
className="section-max form-horizontal"
>
<li
className="col-xs-12 section-title"
>
Profile Picture
</li>
<li
className="col-xs-offset-3 col-xs-8"
>
<ul
className="setting-list"
>
<li
className="setting-list-item"
>
<div
className="profile-img__container"
>
<div
className="img-preview__image"
>
<img
alt="profile image"
className="profile-img"
src="http://localhost:8065/api/v4/users/src_id"
/>
</div>
<OverlayTrigger
defaultOverlayShown={false}
delayShow={400}
overlay={
<Tooltip
bsClass="tooltip"
id="removeIcon"
placement="right"
>
<FormattedMessage
defaultMessage="Remove profile picture"
id="setting_picture.remove_profile_picture"
values={Object {}}
/>
</Tooltip>
}
placement="right"
trigger={
Array [
"hover",
"focus",
]
}
>
<a
className="profile-img__remove"
onClick={[Function]}
>
<span>
×
</span>
</a>
</OverlayTrigger>
</div>
</li>
<li
className="setting-list-item padding-top x2"
>
<FormattedMessage
defaultMessage="Upload a picture in BMP, JPG or PNG format. Maximum file size: {max}"
id="setting_picture.help.profile"
values={
Object {
"max": "200MB",
}
}
/>
</li>
<li
className="setting-list-item"
>
<hr />
<FormError
error={null}
errors={
Array [
"",
"",
]
}
type="modal"
/>
<div
className="btn btn-sm btn-primary btn-file sel-btn"
disabled={false}
>
<FormattedMessage
defaultMessage="Select"
id="setting_picture.select"
values={Object {}}
/>
<input
accept=".jpg,.png,.bmp"
disabled={false}
onChange={[Function]}
type="file"
/>
</div>
<a
className="btn btn-sm btn-inactive disabled"
onClick={[Function]}
>
<FormattedMessage
defaultMessage="Save"
id="setting_picture.save"
values={Object {}}
/>
</a>
<a
className="btn btn-sm theme"
href="#"
onClick={[Function]}
>
<FormattedMessage
defaultMessage="Cancel"
id="setting_picture.cancel"
values={Object {}}
/>
</a>
</li>
</ul>
</li>
</ul>
`;
122 changes: 82 additions & 40 deletions components/setting_picture.jsx
Expand Up @@ -24,10 +24,12 @@ export default class SettingPicture extends Component {
clientError: PropTypes.string,
serverError: PropTypes.string,
src: PropTypes.string,
defaultImageSrc: PropTypes.string,
file: PropTypes.object,
loadingPicture: PropTypes.bool,
submitActive: PropTypes.bool,
onRemove: PropTypes.func,
onSetDefault: PropTypes.func,
onSubmit: PropTypes.func,
title: PropTypes.string,
onFileChange: PropTypes.func,
Expand All @@ -42,6 +44,7 @@ export default class SettingPicture extends Component {
this.state = {
image: null,
removeSrc: false,
setDefaultSrc: false,
};
}

Expand All @@ -60,15 +63,18 @@ export default class SettingPicture extends Component {
}

handleCancel = (e) => {
this.setState({removeSrc: false});
this.setState({removeSrc: false, setDefaultSrc: false});
this.props.updateSection(e);
}

handleSave = (e) => {
e.preventDefault();
if (this.state.removeSrc) {
this.props.onRemove(e);
this.props.onRemove();
} else if (this.state.setDefaultSrc) {
this.props.onSetDefault();
} else {
this.props.onSubmit(e);
this.props.onSubmit();
}
}

Expand All @@ -77,6 +83,11 @@ export default class SettingPicture extends Component {
this.setState({removeSrc: true});
}

handleSetDefaultSrc = (e) => {
e.preventDefault();
this.setState({setDefaultSrc: true});
}

handleFileChange = (e) => {
this.setState({removeSrc: false});
this.props.onFileChange(e);
Expand Down Expand Up @@ -147,18 +158,16 @@ export default class SettingPicture extends Component {
return {transform, transformOrigin};
}

render() {
renderImg = () => {
const imageContext = this.props.imageContext;

let img;

if (this.props.file) {
const imageStyles = {
backgroundImage: 'url(' + this.state.image + ')',
...this.state.orientationStyles,
};

img = (
return (
<div className={`${imageContext}-img-preview`}>
<div className='img-preview__image'>
<div
Expand All @@ -169,49 +178,82 @@ export default class SettingPicture extends Component {
</div>
</div>
);
} else if (this.props.src && !this.state.removeSrc) {
img = (
}

if (this.state.setDefaultSrc) {
return (
<img
className={`${imageContext}-img`}
alt={`${imageContext} image`}
src={this.props.defaultImageSrc}
/>
);
}

if (this.props.src && !this.state.removeSrc) {
const imageElement = (
<img
className={`${imageContext}-img`}
alt={`${imageContext} image`}
src={this.props.src}
/>
);
if (!this.props.onRemove && !this.props.onSetDefault) {
return imageElement;
}

let title;
let handler;
if (this.props.onRemove) {
img = (
<div className={`${imageContext}-img__container`}>
<div className='img-preview__image'>
<img
className={`${imageContext}-img`}
alt={`${imageContext} image`}
src={this.props.src}
/>
</div>
<OverlayTrigger
trigger={['hover', 'focus']}
delayShow={Constants.OVERLAY_TIME_DELAY}
placement='right'
overlay={(
<Tooltip id='removeIcon'>
<FormattedMessage
id='setting_picture.remove'
defaultMessage='Remove this icon'
/>
</Tooltip>
)}
>
<a
className={`${imageContext}-img__remove`}
onClick={this.handleRemoveSrc}
>
<span>{'×'}</span>
</a>
</OverlayTrigger>
</div>
title = (
<FormattedMessage
id='setting_picture.remove'
defaultMessage='Remove this icon'
/>
);
handler = this.handleRemoveSrc;
} else if (this.props.onSetDefault) {
title = (
<FormattedMessage
id='setting_picture.remove_profile_picture'
defaultMessage='Remove profile picture'
/>
);
handler = this.handleSetDefaultSrc;
}

return (
<div className={`${imageContext}-img__container`}>
<div className='img-preview__image'>
{imageElement}
</div>
<OverlayTrigger
trigger={['hover', 'focus']}
delayShow={Constants.OVERLAY_TIME_DELAY}
placement='right'
overlay={(
<Tooltip id='removeIcon'>
{title}
</Tooltip>
)}
>
<a
className={`${imageContext}-img__remove`}
onClick={handler}
>
<span>{'×'}</span>
</a>
</OverlayTrigger>
</div>
);
}
return null;
}

render() {
const imageContext = this.props.imageContext;

const img = this.renderImg();

let confirmButton;
let selectButtonSpinner;
Expand All @@ -232,7 +274,7 @@ export default class SettingPicture extends Component {
fileInputDisabled = true;
} else {
let confirmButtonClass = 'btn btn-sm';
if (this.props.submitActive || this.state.removeSrc) {
if (this.props.submitActive || this.state.removeSrc || this.state.setDefaultSrc) {
confirmButtonClass += ' btn-primary';
} else {
confirmButtonClass += ' btn-inactive disabled';
Expand Down
23 changes: 21 additions & 2 deletions components/setting_picture.test.jsx
Expand Up @@ -37,6 +37,15 @@ describe('components/SettingItemMin', () => {
expect(wrapper).toMatchSnapshot();
});

test('should match snapshot, user icon on source', () => {
const props = {...baseProps, onSetDefault: jest.fn()};
const wrapper = shallow(
<SettingPicture {...props}/>
);

expect(wrapper).toMatchSnapshot();
});

test('should match snapshot, team icon on source', () => {
const props = {...baseProps, onRemove: jest.fn(), imageContext: 'team'};
const wrapper = shallow(
Expand Down Expand Up @@ -105,7 +114,18 @@ describe('components/SettingItemMin', () => {

wrapper.instance().handleSave(evt);
expect(props.onRemove).toHaveBeenCalledTimes(1);
expect(props.onRemove).toHaveBeenCalledWith(evt);
});

test('should call props.onSetDefault on handleSave', () => {
const props = {...baseProps, onSetDefault: jest.fn()};
const wrapper = shallow(
<SettingPicture {...props}/>
);
wrapper.setState({setDefaultSrc: true});
const evt = {preventDefault: jest.fn()};

wrapper.instance().handleSave(evt);
expect(props.onSetDefault).toHaveBeenCalledTimes(1);
});

test('should match state and call props.onSubmit on handleSave', () => {
Expand All @@ -118,7 +138,6 @@ describe('components/SettingItemMin', () => {

wrapper.instance().handleSave(evt);
expect(props.onSubmit).toHaveBeenCalledTimes(1);
expect(props.onSubmit).toHaveBeenCalledWith(evt);

wrapper.update();
expect(wrapper.state('removeSrc')).toEqual(false);
Expand Down

0 comments on commit fc1f115

Please sign in to comment.