From 5d95c318756d3cf2e72ca7748f48c828eede57f2 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Wed, 17 Apr 2019 14:23:59 +0100 Subject: [PATCH] Focus the first invalid field This adjusts the submission step to focus the first invalid field and redisplay validation. This also rearranges the older style field error handling on registration which is slated for removal once we convert all fields to the new style. --- src/components/views/auth/RegistrationForm.js | 76 ++++++++++++++----- src/components/views/elements/Field.js | 5 ++ 2 files changed, 60 insertions(+), 21 deletions(-) diff --git a/src/components/views/auth/RegistrationForm.js b/src/components/views/auth/RegistrationForm.js index 6e139d7051..c680d058c5 100644 --- a/src/components/views/auth/RegistrationForm.js +++ b/src/components/views/auth/RegistrationForm.js @@ -89,34 +89,37 @@ module.exports = React.createClass({ // is the one from the first invalid field. // It's not super ideal that this just calls // onValidationChange once for each invalid field. - // TODO: Change this to trigger new-style validation for an invalid fields. + // TODO: Remove these calls once converted to new-style validation. this.validateField(FIELD_PHONE_NUMBER, ev.type); this.validateField(FIELD_EMAIL, ev.type); this.validateField(FIELD_PASSWORD_CONFIRM, ev.type); this.validateField(FIELD_PASSWORD, ev.type); this.validateField(FIELD_USERNAME, ev.type); + const allFieldsValid = this.verifyFieldsBeforeSubmit(); + if (!allFieldsValid) { + return; + } + const self = this; - if (this.allFieldsValid()) { - if (this.state.email == '') { - const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); - Modal.createTrackedDialog('If you don\'t specify an email address...', '', QuestionDialog, { - title: _t("Warning!"), - description: -
- { _t("If you don't specify an email address, you won't be able to reset your password. " + - "Are you sure?") } -
, - button: _t("Continue"), - onFinished: function(confirmed) { - if (confirmed) { - self._doSubmit(ev); - } - }, - }); - } else { - self._doSubmit(ev); - } + if (this.state.email == '') { + const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); + Modal.createTrackedDialog('If you don\'t specify an email address...', '', QuestionDialog, { + title: _t("Warning!"), + description: +
+ { _t("If you don't specify an email address, you won't be able to reset your password. " + + "Are you sure?") } +
, + button: _t("Continue"), + onFinished: function(confirmed) { + if (confirmed) { + self._doSubmit(ev); + } + }, + }); + } else { + self._doSubmit(ev); } }, @@ -138,6 +141,27 @@ module.exports = React.createClass({ } }, + verifyFieldsBeforeSubmit() { + if (this.allFieldsValid()) { + return true; + } + + const invalidField = this.findFirstInvalidField([ + FIELD_USERNAME, + FIELD_PASSWORD, + FIELD_PASSWORD_CONFIRM, + FIELD_EMAIL, + FIELD_PHONE_NUMBER, + ]); + + if (!invalidField) { + return true; + } + + invalidField.focus(); + return false; + }, + /** * @returns {boolean} true if all fields were valid last time they were validated. */ @@ -158,6 +182,15 @@ module.exports = React.createClass({ return true; }, + findFirstInvalidField(fieldIDs) { + for (const fieldID of fieldIDs) { + if (!this.state.fieldValid[fieldID] && this[fieldID]) { + return this[fieldID]; + } + } + return null; + }, + validateField: function(fieldID, eventType) { const pwd1 = this.state.password.trim(); const pwd2 = this.state.passwordConfirm.trim(); @@ -362,6 +395,7 @@ module.exports = React.createClass({ return this[FIELD_USERNAME] = field} type="text" autoFocus={true} label={_t("Username")} diff --git a/src/components/views/elements/Field.js b/src/components/views/elements/Field.js index 6d58d29a3d..93d1f1e71d 100644 --- a/src/components/views/elements/Field.js +++ b/src/components/views/elements/Field.js @@ -86,6 +86,10 @@ export default class Field extends React.PureComponent { } }; + focus() { + this.input.focus(); + } + validate({ value, focused }) { if (!this.props.onValidate) { return; @@ -107,6 +111,7 @@ export default class Field extends React.PureComponent { // Set some defaults for the element inputProps.type = inputProps.type || "text"; + inputProps.ref = input => this.input = input; inputProps.placeholder = inputProps.placeholder || inputProps.label; inputProps.onFocus = this.onFocus;