Skip to content

Commit

Permalink
Refactor Change password (#5044)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tishasoumya-02 authored Aug 23, 2024
1 parent 16b6147 commit 913670f
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 172 deletions.
1 change: 1 addition & 0 deletions packages/volto/news/5044.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Refactor Preference/Change Password from class to functional component. @Tishasoumya-02
266 changes: 94 additions & 172 deletions packages/volto/src/components/manage/Preferences/ChangePassword.jsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
/**
* Change password component.
* @module components/manage/Preferences/ChangePassword
*/

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Helmet } from '@plone/volto/helpers';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { Link, withRouter } from 'react-router-dom';
import { useSelector, useDispatch, shallowEqual } from 'react-redux';
import { Link, useHistory, useLocation } from 'react-router-dom';
import { defineMessages, useIntl } from 'react-intl';
import { createPortal } from 'react-dom';
import { defineMessages, injectIntl } from 'react-intl';
import { Container } from 'semantic-ui-react';
import jwtDecode from 'jwt-decode';
import { toast } from 'react-toastify';

import { Icon, Toast, Toolbar } from '@plone/volto/components';
import { Form } from '@plone/volto/components/manage/Form';
import { Helmet } from '@plone/volto/helpers';
import { useClient } from '@plone/volto/hooks';
import { Form, Icon, Toast, Toolbar } from '@plone/volto/components';
import { updatePassword } from '@plone/volto/actions';
import { getBaseUrl } from '@plone/volto/helpers';

import backSVG from '@plone/volto/icons/back.svg';

const messages = defineMessages({
Expand Down Expand Up @@ -70,169 +61,100 @@ const messages = defineMessages({
},
});

/**
* ChangePassword class.
* @class ChangePassword
* @extends Component
*/
class ChangePassword extends Component {
/**
* Property types.
* @property {Object} propTypes Property types.
* @static
*/
static propTypes = {
userId: PropTypes.string.isRequired,
loading: PropTypes.bool.isRequired,
updatePassword: PropTypes.func.isRequired,
pathname: PropTypes.string.isRequired,
};

/**
* Constructor
* @method constructor
* @param {Object} props Component properties
* @constructs ChangePassword
*/
constructor(props) {
super(props);
this.onCancel = this.onCancel.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = { isClient: false };
}
const ChangePassword = () => {
const intl = useIntl();
const dispatch = useDispatch();
const isClient = useClient();

/**
* Component did mount
* @method componentDidMount
* @returns {undefined}
*/
componentDidMount() {
this.setState({ isClient: true });
}
const userId = useSelector(
(state) =>
state.userSession.token ? jwtDecode(state.userSession.token).sub : '',
shallowEqual,
);
const loading = useSelector((state) => state.users.update_password.loading);
const { pathname } = useLocation();
const history = useHistory();

/**
* Submit handler
* @method onSubmit
* @param {object} data Form data.
* @returns {undefined}
*/
onSubmit(data) {
const onSubmit = (data) => {
if (data.newPassword === data.newPasswordRepeat) {
this.props.updatePassword(
this.props.userId,
data.oldPassword,
data.newPassword,
);
toast.success(
<Toast
success
title={this.props.intl.formatMessage(messages.success)}
content={this.props.intl.formatMessage(messages.saved)}
/>,
dispatch(updatePassword(userId, data.oldPassword, data.newPassword)).then(
() => {
toast.success(
<Toast
success
title={intl.formatMessage(messages.success)}
content={intl.formatMessage(messages.saved)}
/>,
);
},
);
}
}
};

/**
* Cancel handler
* @method onCancel
* @returns {undefined}
*/
onCancel() {
this.props.history.goBack();
}
const onCancel = () => {
history.goBack();
};

/**
* Render method.
* @method render
* @returns {string} Markup for the component.
*/
render() {
return (
<Container id="page-change-password">
<Helmet
title={this.props.intl.formatMessage(messages.changePassword)}
/>
<Form
schema={{
fieldsets: [
{
id: 'default',
title: this.props.intl.formatMessage(messages.default),
fields: ['oldPassword', 'newPassword', 'newPasswordRepeat'],
},
],
properties: {
oldPassword: {
description: this.props.intl.formatMessage(
messages.oldPasswordDescription,
),
title: this.props.intl.formatMessage(messages.oldPasswordTitle),
type: 'string',
widget: 'password',
},
newPassword: {
description: this.props.intl.formatMessage(
messages.newPasswordDescription,
),
title: this.props.intl.formatMessage(messages.newPasswordTitle),
type: 'string',
widget: 'password',
},
newPasswordRepeat: {
description: this.props.intl.formatMessage(
messages.newPasswordRepeatDescription,
),
title: this.props.intl.formatMessage(
messages.newPasswordRepeatTitle,
),
type: 'string',
widget: 'password',
},
return (
<Container id="page-change-password">
<Helmet title={intl.formatMessage(messages.changePassword)} />
<Form
schema={{
fieldsets: [
{
id: 'default',
title: intl.formatMessage(messages.default),
fields: ['oldPassword', 'newPassword', 'newPasswordRepeat'],
},
required: ['oldPassword', 'newPassword', 'newPasswordRepeat'],
}}
onSubmit={this.onSubmit}
onCancel={this.onCancel}
loading={this.props.loading}
/>
{this.state.isClient &&
createPortal(
<Toolbar
pathname={this.props.pathname}
hideDefaultViewButtons
inner={
<Link
to={`${getBaseUrl(this.props.pathname)}`}
className="item"
>
<Icon
name={backSVG}
className="contents circled"
size="30px"
title={this.props.intl.formatMessage(messages.back)}
/>
</Link>
}
/>,
document.getElementById('toolbar'),
)}
</Container>
);
}
}
],
properties: {
oldPassword: {
description: intl.formatMessage(messages.oldPasswordDescription),
title: intl.formatMessage(messages.oldPasswordTitle),
type: 'string',
widget: 'password',
},
newPassword: {
description: intl.formatMessage(messages.newPasswordDescription),
title: intl.formatMessage(messages.newPasswordTitle),
type: 'string',
widget: 'password',
},
newPasswordRepeat: {
description: intl.formatMessage(
messages.newPasswordRepeatDescription,
),
title: intl.formatMessage(messages.newPasswordRepeatTitle),
type: 'string',
widget: 'password',
},
},
required: ['oldPassword', 'newPassword', 'newPasswordRepeat'],
}}
onSubmit={onSubmit}
onCancel={onCancel}
loading={loading}
/>
{isClient &&
createPortal(
<Toolbar
pathname={pathname}
hideDefaultViewButtons
inner={
<Link to={`${getBaseUrl(pathname)}`} className="item">
<Icon
name={backSVG}
className="contents circled"
size="30px"
title={intl.formatMessage(messages.back)}
/>
</Link>
}
/>,
document.getElementById('toolbar'),
)}
</Container>
);
};

export default compose(
withRouter,
injectIntl,
connect(
(state, props) => ({
userId: state.userSession.token
? jwtDecode(state.userSession.token).sub
: '',
loading: state.users.update_password.loading,
pathname: props.location.pathname,
}),
{ updatePassword },
),
)(ChangePassword);
export default ChangePassword;
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { injectIntl } from 'react-intl';
import React from 'react';
import ChangePasswordComponent from './ChangePassword';
import { RealStoreWrapper as Wrapper } from '@plone/volto/storybook';
const IntlChangePasswordComponent = injectIntl(ChangePasswordComponent);

function StoryComponent(args) {
return (
<Wrapper
customStore={{
intl: {
locale: 'en',
messages: {},
},
users: {
update_password: {
loading: false,
},
},
}}
>
<div id="toolbar" style={{ display: 'none' }} />
<IntlChangePasswordComponent {...args} location={{ pathname: '/blog' }} />
</Wrapper>
);
}

export const ChangePassword = StoryComponent.bind({});

export default {
title: 'Public components/ChangePassword',
component: ChangePassword,
decorators: [
(Story) => (
<div className="ui segment form attached" style={{ width: '400px' }}>
<Story />
</div>
),
],
argTypes: {},
};

0 comments on commit 913670f

Please sign in to comment.