mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-22 15:17:53 -05:00
Merge pull request #3236 from benjiwheeler/join-flow-highlighting
handle username validation errors states better
This commit is contained in:
commit
46de5a23e1
5 changed files with 81 additions and 25 deletions
|
@ -5,9 +5,8 @@ import {Field} from 'formik';
|
|||
|
||||
const ValidationMessage = require('../forms/validation-message.jsx');
|
||||
|
||||
require('./input.scss');
|
||||
require('../forms/input.scss');
|
||||
require('../forms/row.scss');
|
||||
require('./formik-input.scss');
|
||||
|
||||
const FormikInput = ({
|
||||
className,
|
||||
|
@ -27,6 +26,7 @@ const FormikInput = ({
|
|||
<Field
|
||||
className={classNames(
|
||||
'input',
|
||||
{fail: error},
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
28
src/components/formik-forms/formik-input.scss
Normal file
28
src/components/formik-forms/formik-input.scss
Normal file
|
@ -0,0 +1,28 @@
|
|||
@import "../../colors";
|
||||
|
||||
.input {
|
||||
height: 2.75rem;
|
||||
border-radius: .5rem;
|
||||
background-color: $ui-white;
|
||||
margin-bottom: .5rem;
|
||||
transition: all .5s ease;
|
||||
border: 1px solid $active-gray;
|
||||
padding: 0 1rem;
|
||||
color: $type-gray;
|
||||
font-size: .875rem;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 .25rem $ui-blue-25percent;
|
||||
outline: none;
|
||||
border: 1px solid $ui-blue;
|
||||
}
|
||||
|
||||
&.fail {
|
||||
border: 1px solid $ui-orange;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 .25rem $ui-orange-25percent;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,20 +36,26 @@ class UsernameStep extends React.Component {
|
|||
// we allow username to be empty on blur, since you might not have typed anything yet
|
||||
validateUsernameIfPresent (username) {
|
||||
if (!username) return null; // skip validation if username is blank; null indicates valid
|
||||
// if username is not blank, run both local and remote validations
|
||||
const localResult = validate.validateUsernameLocally(username);
|
||||
if (localResult.valid) {
|
||||
return validate.validateUsernameRemotely(username).then(
|
||||
remoteResult => {
|
||||
if (remoteResult.valid) return null;
|
||||
return validate.validateUsernameRemotely(username).then(
|
||||
remoteResult => {
|
||||
// there may be multiple validation errors. Prioritize vulgarity, then
|
||||
// length, then having invalid chars, then all other remote reports
|
||||
if (remoteResult.valid === false && remoteResult.errMsgId === 'registration.validationUsernameVulgar') {
|
||||
return this.props.intl.formatMessage({id: remoteResult.errMsgId});
|
||||
} else if (localResult.valid === false) {
|
||||
return this.props.intl.formatMessage({id: localResult.errMsgId});
|
||||
} else if (remoteResult.valid === false) {
|
||||
return this.props.intl.formatMessage({id: remoteResult.errMsgId});
|
||||
}
|
||||
);
|
||||
}
|
||||
return this.props.intl.formatMessage({id: localResult.errMsgId});
|
||||
return null;
|
||||
}
|
||||
);
|
||||
}
|
||||
validatePasswordIfPresent (password) {
|
||||
validatePasswordIfPresent (password, username) {
|
||||
if (!password) return null; // skip validation if password is blank; null indicates valid
|
||||
const localResult = validate.validatePassword(password);
|
||||
const localResult = validate.validatePassword(password, username);
|
||||
if (localResult.valid) return null;
|
||||
return this.props.intl.formatMessage({id: localResult.errMsgId});
|
||||
}
|
||||
|
@ -69,13 +75,10 @@ class UsernameStep extends React.Component {
|
|||
if (!usernameResult.valid) {
|
||||
errors.username = this.props.intl.formatMessage({id: usernameResult.errMsgId});
|
||||
}
|
||||
const passwordResult = validate.validatePassword(values.password);
|
||||
const passwordResult = validate.validatePassword(values.password, values.username);
|
||||
if (!passwordResult.valid) {
|
||||
errors.password = this.props.intl.formatMessage({id: passwordResult.errMsgId});
|
||||
}
|
||||
if (values.password === values.username) {
|
||||
errors.password = this.props.intl.formatMessage({id: 'registration.validationPasswordNotUsername'});
|
||||
}
|
||||
const passwordConfirmResult = validate.validatePasswordConfirm(values.password, values.passwordConfirm);
|
||||
if (!passwordConfirmResult.valid) {
|
||||
errors.passwordConfirm = this.props.intl.formatMessage({id: passwordConfirmResult.errMsgId});
|
||||
|
@ -105,6 +108,8 @@ class UsernameStep extends React.Component {
|
|||
errors,
|
||||
handleSubmit,
|
||||
isSubmitting,
|
||||
setFieldError,
|
||||
setFieldValue,
|
||||
validateField,
|
||||
values
|
||||
} = props;
|
||||
|
@ -123,15 +128,20 @@ class UsernameStep extends React.Component {
|
|||
</div>
|
||||
<FormikInput
|
||||
className={classNames(
|
||||
'join-flow-input',
|
||||
{fail: errors.username}
|
||||
'join-flow-input'
|
||||
)}
|
||||
error={errors.username}
|
||||
id="username"
|
||||
name="username"
|
||||
validate={this.validateUsernameIfPresent}
|
||||
validationClassName="validation-full-width-input"
|
||||
onBlur={() => validateField('username')} // eslint-disable-line react/jsx-no-bind
|
||||
/* eslint-disable react/jsx-no-bind */
|
||||
onBlur={() => validateField('username')}
|
||||
onChange={e => {
|
||||
setFieldValue('username', e.target.value);
|
||||
setFieldError('username', null);
|
||||
}}
|
||||
/* eslint-enable react/jsx-no-bind */
|
||||
/>
|
||||
<div className="join-flow-password-section">
|
||||
<div className="join-flow-input-title">
|
||||
|
@ -139,23 +149,25 @@ class UsernameStep extends React.Component {
|
|||
</div>
|
||||
<FormikInput
|
||||
className={classNames(
|
||||
'join-flow-input',
|
||||
{fail: errors.password}
|
||||
'join-flow-input'
|
||||
)}
|
||||
error={errors.password}
|
||||
id="password"
|
||||
name="password"
|
||||
type={this.state.showPassword ? 'text' : 'password'}
|
||||
validate={this.validatePasswordIfPresent}
|
||||
validationClassName="validation-full-width-input"
|
||||
/* eslint-disable react/jsx-no-bind */
|
||||
validate={password => this.validatePasswordIfPresent(password, values.username)}
|
||||
validationClassName="validation-full-width-input"
|
||||
onBlur={() => validateField('password')}
|
||||
onChange={e => {
|
||||
setFieldValue('password', e.target.value);
|
||||
setFieldError('password', null);
|
||||
}}
|
||||
/* eslint-enable react/jsx-no-bind */
|
||||
/>
|
||||
<FormikInput
|
||||
className={classNames(
|
||||
'join-flow-input',
|
||||
{fail: errors.passwordConfirm}
|
||||
'join-flow-input'
|
||||
)}
|
||||
error={errors.passwordConfirm}
|
||||
id="passwordConfirm"
|
||||
|
@ -170,6 +182,10 @@ class UsernameStep extends React.Component {
|
|||
onBlur={() =>
|
||||
validateField('passwordConfirm')
|
||||
}
|
||||
onChange={e => {
|
||||
setFieldValue('passwordConfirm', e.target.value);
|
||||
setFieldError('passwordConfirm', null);
|
||||
}}
|
||||
/* eslint-enable react/jsx-no-bind */
|
||||
/>
|
||||
<div className="join-flow-input-title">
|
||||
|
|
|
@ -40,13 +40,21 @@ module.exports.validateUsernameRemotely = username => (
|
|||
})
|
||||
);
|
||||
|
||||
module.exports.validatePassword = password => {
|
||||
/**
|
||||
* Validate password value, optionally also considering username value
|
||||
* @param {string} password password value to validate
|
||||
* @param {string} username username value to compare
|
||||
* @return {object} {valid: boolean, errMsgId: string}
|
||||
*/
|
||||
module.exports.validatePassword = (password, username) => {
|
||||
if (!password) {
|
||||
return {valid: false, errMsgId: 'general.required'};
|
||||
} else if (password.length < 6) {
|
||||
return {valid: false, errMsgId: 'registration.validationPasswordLength'};
|
||||
} else if (password === 'password') {
|
||||
return {valid: false, errMsgId: 'registration.validationPasswordNotEquals'};
|
||||
} else if (username && password === username) {
|
||||
return {valid: false, errMsgId: 'registration.validationPasswordNotUsername'};
|
||||
}
|
||||
return {valid: true};
|
||||
};
|
||||
|
|
|
@ -39,6 +39,10 @@ describe('unit test lib/validate.js', () => {
|
|||
expect(response).toEqual({valid: false, errMsgId: 'registration.validationPasswordLength'});
|
||||
response = validate.validatePassword('password');
|
||||
expect(response).toEqual({valid: false, errMsgId: 'registration.validationPasswordNotEquals'});
|
||||
response = validate.validatePassword('abcdefg', 'abcdefg');
|
||||
expect(response).toEqual({valid: false, errMsgId: 'registration.validationPasswordNotUsername'});
|
||||
response = validate.validatePassword('abcdefg', 'abcdefG');
|
||||
expect(response).toEqual({valid: true});
|
||||
});
|
||||
|
||||
test('validate password confirm', () => {
|
||||
|
|
Loading…
Reference in a new issue