Skip to content

Commit

Permalink
Add/account recovery/step 5b to 6 (Automattic#13362)
Browse files Browse the repository at this point in the history
* Wrap form components under `<form>` tag

* Move `reset-password-confirm-form` one directory level up and integrate
it into the AR flow.

* Chain step 5b to step 6.

* Fix `account-recovery/validate` data layer handler accordingly.

* Update Account Recovery < New Password as a breadcrumb format.

* Remove the unnecessary `primary`

* Show more details of error accordingly

* Break a long string into two lines.
  • Loading branch information
southp authored Apr 30, 2017
1 parent 35ff17d commit 26efe9e
Show file tree
Hide file tree
Showing 11 changed files with 152 additions and 85 deletions.
2 changes: 1 addition & 1 deletion assets/stylesheets/_components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
@import 'account-recovery/reset-password-form/style';
@import 'account-recovery/reset-password-sms-form/style';
@import 'account-recovery/reset-password-email-form/style';
@import 'account-recovery/reset-password/reset-password-confirm-form/style';
@import 'account-recovery/reset-password-confirm-form/style';
@import 'account-recovery/reset-password/transaction-id-form/style';
@import 'account-recovery/style';
@import 'auth/style';
Expand Down
14 changes: 14 additions & 0 deletions client/account-recovery/account-recovery-root/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ import ForgotUsernameForm from 'account-recovery/forgot-username-form';
import ResetPasswordForm from 'account-recovery/reset-password-form';
import ResetPasswordEmailForm from 'account-recovery/reset-password-email-form';
import ResetPasswordSmsForm from 'account-recovery/reset-password-sms-form';
import ResetPasswordConfirmForm from 'account-recovery/reset-password-confirm-form';
import { ACCOUNT_RECOVERY_STEPS as STEPS } from 'account-recovery/constants';
import {
isAccountRecoveryResetOptionsReady,
isAccountRecoveryUserDataReady,
getAccountRecoveryResetSelectedMethod,
getAccountRecoveryValidationKey,
} from 'state/selectors';

const getPageInfo = ( translate, step ) => {
Expand All @@ -49,6 +51,10 @@ const getPageInfo = ( translate, step ) => {
trackerTitle: 'Account Recovery > Reset Password Sms',
documentHeadTitle: concatHeadTitle( translate( 'Reset Password' ), translate( 'SMS Message' ) ),
},
[ STEPS.RESET_PASSWORD_CONFIRM ]: {
trackerTitle: 'Account Recovery > New Password',
documentHeadTitle: concatHeadTitle( translate( 'Reset Password' ), translate( 'New Password' ) ),
},
};

return pageInfo[ step ];
Expand All @@ -60,8 +66,13 @@ const getCurrentStep = ( props ) => {
isUserDataReady,
isResetOptionsReady,
selectedMethod,
validationKey,
} = props;

if ( validationKey ) {
return STEPS.RESET_PASSWORD_CONFIRM;
}

if ( selectedMethod ) {
if ( includes( [ 'primary_email', 'secondary_email' ], selectedMethod ) ) {
return STEPS.RESET_PASSWORD_EMAIL;
Expand Down Expand Up @@ -89,6 +100,8 @@ const getForm = ( step ) => {
return <ResetPasswordEmailForm />;
case STEPS.RESET_PASSWORD_SMS:
return <ResetPasswordSmsForm />;
case STEPS.RESET_PASSWORD_CONFIRM:
return <ResetPasswordConfirmForm />;
}

return null;
Expand Down Expand Up @@ -118,5 +131,6 @@ export default connect(
isResetOptionsReady: isAccountRecoveryResetOptionsReady( state ),
isUserDataReady: isAccountRecoveryUserDataReady( state ),
selectedMethod: getAccountRecoveryResetSelectedMethod( state ),
validationKey: getAccountRecoveryValidationKey( state ),
} )
)( localize( AccountRecoveryRoot ) );
Original file line number Diff line number Diff line change
@@ -1,20 +1,37 @@
/**
* External dependencies
*/
import React from 'react';
import React, { Component } from 'react';
import { localize } from 'i18n-calypso';
import config from 'config';

const AccountRecoveryErrorMessage = ( props ) => (
<p className="account-recovery-error-message">
{ props.translate(
class AccountRecoveryErrorMessage extends Component {
getErrorMessage = () => {
const {
translate,
error,
} = this.props;

switch ( error.name ) {
case 'RestInvalidKeyError':
return translate( "We've failed to validate with the given code. " +
'Please double check if the code is correct.' );
}

return translate(
"We're having trouble connecting to our servers at the moment. " +
'Please try again later. If the problem persists, please {{a}}contact us{{/a}}.',
{ components: {
a: <a href={ config( 'login_url' ) + '?action=recovery' } target="_blank" rel="noopener noreferrer" />
} }
) }
</p>
);
);
}

render = () => {
const errorMsg = this.getErrorMessage();

return <p className="account-recovery-error-message__text">{ errorMsg }</p>;
}
}

export default localize( AccountRecoveryErrorMessage );
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.account-recovery-error-message {
.account-recovery-error-message__text {
margin-bottom: 4px;
color: $alert-red;
}
1 change: 1 addition & 0 deletions client/account-recovery/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export const ACCOUNT_RECOVERY_STEPS = {
RESET_PASSWORD: 'resetPassword',
RESET_PASSWORD_EMAIL: 'resetPasswordEmail',
RESET_PASSWORD_SMS: 'resetPasswordSms',
RESET_PASSWORD_CONFIRM: 'resetPasswordConfirm',
};
64 changes: 64 additions & 0 deletions client/account-recovery/reset-password-confirm-form/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* External dependencies
*/
import React, { Component } from 'react';
import { localize } from 'i18n-calypso';
import { identity } from 'lodash';

/**
* Internal dependencies
*/
import Card from 'components/card';
import FormPasswordInput from 'components/forms/form-password-input';
import FormLabel from 'components/forms/form-label';
import FormButton from 'components/forms/form-button';
import { STRONG_PASSWORD } from 'lib/url/support';

class ResetPasswordConfirmForm extends Component {
submitNewPassword = ( event ) => {
event.preventDefault();
}

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

return (
<Card>
<h2 className="reset-password-confirm-form__title">{ translate( 'Reset your password' ) }</h2>
<form onSubmit={ this.submitNewPassword }>
<FormLabel className="reset-password-confirm-form__text-input-label" htmlFor="password">
{ translate( 'New password' ) }
</FormLabel>
<FormPasswordInput className="reset-password-confirm-form__password-input-field" id="password" autoFocus />
<FormButton className="reset-password-confirm-form__button generate-password-button" isPrimary={ false }>
{ translate( 'Generate strong password' ) }
</FormButton>
<p className="reset-password-confirm-form__description">
{ translate(
'{{a}}Great passwords{{/a}} use upper and lower case characters, numbers, ' +
'and symbols like {{em}}%(symbols)s{{/em}}.',
{
args: {
symbols: '!/"$%&',
},
components: {
a: <a href={ STRONG_PASSWORD } target="_blank" rel="noopener noreferrer" />,
em: <em />,
}
}
) }
</p>
<FormButton className="reset-password-confirm-form__button submit" type="submit">
{ translate( 'Reset Password' ) }
</FormButton>
</form>
</Card>
);
}
}

ResetPasswordConfirmForm.defaultProps = {
translate: identity,
};

export default localize( ResetPasswordConfirmForm );
21 changes: 13 additions & 8 deletions client/account-recovery/reset-password-sms-form/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
getAccountRecoveryResetUserData,
getAccountRecoveryResetSelectedMethod,
getAccountRecoveryValidationError,
getAccountRecoveryValidationKey,
isValidatingAccountRecoveryKey,
} from 'state/selectors';

Expand All @@ -29,28 +28,34 @@ import {
} from 'state/account-recovery/reset/actions';

class ResetPasswordSmsForm extends Component {
constructor( props ) {
super( props );

this.state = {
candidateKey: '',
};
}

submitValidationKey = ( event ) => {
const {
userData,
selectedMethod,
validationKey,
} = this.props;

this.props.validateRequest( userData, selectedMethod, validationKey );
this.props.validateRequest( userData, selectedMethod, this.state.candidateKey );

event.preventDefault();
}

updateValidationKey = ( event ) => {
this.props.setValidationKey( event.target.value );
this.setState( { candidateKey: event.target.value } );
}

render() {
const {
translate,
isValidating,
error,
validationKey,
} = this.props;

return (
Expand All @@ -68,10 +73,11 @@ class ResetPasswordSmsForm extends Component {
<FormTextInput
className="reset-password-sms-form__validation-code-input"
disabled={ isValidating }
value={ validationKey || '' }
value={ this.state.candidateKey }
onChange={ this.updateValidationKey }
autoFocus
/>
{ error && <ErrorMessage /> }
{ error && <ErrorMessage error={ error } /> }
<FormButton className="reset-password-sms-form__submit-button" type="submit" disabled={ isValidating } >
{ translate( 'Continue' ) }
</FormButton>
Expand All @@ -88,7 +94,6 @@ export default connect(
( state ) => ( {
userData: getAccountRecoveryResetUserData( state ),
selectedMethod: getAccountRecoveryResetSelectedMethod( state ),
validationKey: getAccountRecoveryValidationKey( state ),
isValidating: isValidatingAccountRecoveryKey( state ),
error: getAccountRecoveryValidationError( state ),
} ),
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ACCOUNT_RECOVERY_RESET_VALIDATE_REQUEST } from 'state/action-types';
import {
validateRequestSuccess,
validateRequestError,
setValidationKey,
} from 'state/account-recovery/reset/actions';

export const handleValidateRequest = ( { dispatch }, action, next ) => {
Expand All @@ -18,7 +19,10 @@ export const handleValidateRequest = ( { dispatch }, action, next ) => {
},
apiNamespace: 'wpcom/v2',
path: '/account-recovery/validate',
} ).then( () => dispatch( validateRequestSuccess() ) )
} ).then( () => {
dispatch( validateRequestSuccess() );
dispatch( setValidationKey( key ) );
} )
.catch( ( error ) => dispatch( validateRequestError( error ) ) );

return next( action );
Expand Down
Loading

0 comments on commit 26efe9e

Please sign in to comment.