From f7fac2e41c2d013685bbb9c1483bff9f1053f15e Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Fri, 9 Aug 2019 22:59:01 -0400 Subject: [PATCH 1/4] handle username validation errors states better --- src/components/formik-forms/formik-input.jsx | 4 +-- src/components/formik-forms/formik-input.scss | 33 +++++++++++++++++++ src/components/join-flow/username-step.jsx | 30 ++++++++--------- src/lib/validate.js | 10 +++++- 4 files changed, 59 insertions(+), 18 deletions(-) create mode 100644 src/components/formik-forms/formik-input.scss diff --git a/src/components/formik-forms/formik-input.jsx b/src/components/formik-forms/formik-input.jsx index 3937a0bd5..26bff6a82 100644 --- a/src/components/formik-forms/formik-input.jsx +++ b/src/components/formik-forms/formik-input.jsx @@ -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 = ({ validateField('username')} // eslint-disable-line react/jsx-no-bind + /* eslint-disable react/jsx-no-bind */ + onBlur={() => validateField('username')} + onFocus={() => setFieldError('username', null)} + /* eslint-enable react/jsx-no-bind */ />
@@ -139,23 +139,22 @@ class UsernameStep extends React.Component {
this.validatePasswordIfPresent(password, values.username)} + validationClassName="validation-full-width-input" onBlur={() => validateField('password')} + onFocus={() => setFieldError('password', null)} /* eslint-enable react/jsx-no-bind */ /> validateField('passwordConfirm') } + onFocus={() => setFieldError('passwordConfirm', null)} /* eslint-enable react/jsx-no-bind */ />
diff --git a/src/lib/validate.js b/src/lib/validate.js index f4b4fe9fc..ca3e16b17 100644 --- a/src/lib/validate.js +++ b/src/lib/validate.js @@ -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: 'form.validationRequired'}; } 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}; }; From 11e2b0bf7859a3e38a34fc12e17cb0a3fb66958f Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 12 Aug 2019 14:11:05 -0400 Subject: [PATCH 2/4] removed ie 10 and 11 css case --- src/components/formik-forms/formik-input.scss | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/components/formik-forms/formik-input.scss b/src/components/formik-forms/formik-input.scss index c2b64bb0e..3cf483877 100644 --- a/src/components/formik-forms/formik-input.scss +++ b/src/components/formik-forms/formik-input.scss @@ -25,9 +25,4 @@ outline: none; } } - - /* IE10/11-specific style resets */ - &::-ms-reveal, &::-ms-clear { - display: none; - } } From d5a86a99e59c0fbe814a89329933a6bf717271a9 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 12 Aug 2019 14:11:40 -0400 Subject: [PATCH 3/4] added test for password equaling username --- test/unit/lib/validate.test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/unit/lib/validate.test.js b/test/unit/lib/validate.test.js index 31eca3e36..993ce8c7c 100644 --- a/test/unit/lib/validate.test.js +++ b/test/unit/lib/validate.test.js @@ -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', () => { From aa69de39f32b6eb776af8a135b9822dec95d8a25 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 12 Aug 2019 16:07:11 -0400 Subject: [PATCH 4/4] keep showing validation errors on focus, until first keystroke; prioritize vulgarity --- src/components/join-flow/username-step.jsx | 36 ++++++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/components/join-flow/username-step.jsx b/src/components/join-flow/username-step.jsx index e30f31eb1..9d5199787 100644 --- a/src/components/join-flow/username-step.jsx +++ b/src/components/join-flow/username-step.jsx @@ -36,16 +36,22 @@ 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, username) { if (!password) return null; // skip validation if password is blank; null indicates valid @@ -103,6 +109,7 @@ class UsernameStep extends React.Component { handleSubmit, isSubmitting, setFieldError, + setFieldValue, validateField, values } = props; @@ -130,7 +137,10 @@ class UsernameStep extends React.Component { validationClassName="validation-full-width-input" /* eslint-disable react/jsx-no-bind */ onBlur={() => validateField('username')} - onFocus={() => setFieldError('username', null)} + onChange={e => { + setFieldValue('username', e.target.value); + setFieldError('username', null); + }} /* eslint-enable react/jsx-no-bind */ />
@@ -149,7 +159,10 @@ class UsernameStep extends React.Component { validate={password => this.validatePasswordIfPresent(password, values.username)} validationClassName="validation-full-width-input" onBlur={() => validateField('password')} - onFocus={() => setFieldError('password', null)} + onChange={e => { + setFieldValue('password', e.target.value); + setFieldError('password', null); + }} /* eslint-enable react/jsx-no-bind */ /> validateField('passwordConfirm') } - onFocus={() => setFieldError('passwordConfirm', null)} + onChange={e => { + setFieldValue('passwordConfirm', e.target.value); + setFieldError('passwordConfirm', null); + }} /* eslint-enable react/jsx-no-bind */ />