diff --git a/res/css/views/auth/_AuthBody.scss b/res/css/views/auth/_AuthBody.scss index 6216bdd4b8..778f5f6a4d 100644 --- a/res/css/views/auth/_AuthBody.scss +++ b/res/css/views/auth/_AuthBody.scss @@ -58,6 +58,10 @@ limitations under the License. background-color: $authpage-body-bg-color; } +.mx_AuthBody input.error { + color: $warning-color; +} + .mx_AuthBody_editServerDetails { padding-left: 1em; font-size: 12px; diff --git a/src/components/structures/auth/Registration.js b/src/components/structures/auth/Registration.js index 711ec3c1d7..1fcaa77d54 100644 --- a/src/components/structures/auth/Registration.js +++ b/src/components/structures/auth/Registration.js @@ -308,7 +308,19 @@ module.exports = React.createClass({ }); }, - onFormValidationFailed: function(errCode) { + onFormValidationChange: function(fieldErrors) { + // `fieldErrors` is an object mapping field IDs to error codes when there is an + // error or `null` for no error, so the values array will be something like: + // `[ null, "RegistrationForm.ERR_PASSWORD_MISSING", null]` + // Find the first non-null error code and show that. + const errCode = Object.values(fieldErrors).find(value => !!value); + if (!errCode) { + this.setState({ + errorText: null, + }); + return; + } + let errMsg; switch (errCode) { case "RegistrationForm.ERR_PASSWORD_MISSING": @@ -510,7 +522,7 @@ module.exports = React.createClass({ defaultPhoneNumber={this.state.formVals.phoneNumber} defaultPassword={this.state.formVals.password} minPasswordLength={MIN_PASSWORD_LENGTH} - onError={this.onFormValidationFailed} + onValidationChange={this.onFormValidationChange} onRegisterClick={this.onFormSubmit} onEditServerDetailsClick={onEditServerDetailsClick} flows={this.state.flows} diff --git a/src/components/views/auth/RegistrationForm.js b/src/components/views/auth/RegistrationForm.js index eabdcd0dd2..d031dc7bdd 100644 --- a/src/components/views/auth/RegistrationForm.js +++ b/src/components/views/auth/RegistrationForm.js @@ -46,7 +46,7 @@ module.exports = React.createClass({ defaultUsername: PropTypes.string, defaultPassword: PropTypes.string, minPasswordLength: PropTypes.number, - onError: PropTypes.func, + onValidationChange: PropTypes.func, onRegisterClick: PropTypes.func.isRequired, // onRegisterClick(Object) => ?Promise onEditServerDetailsClick: PropTypes.func, flows: PropTypes.arrayOf(PropTypes.object).isRequired, @@ -60,15 +60,14 @@ module.exports = React.createClass({ getDefaultProps: function() { return { minPasswordLength: 6, - onError: function(e) { - console.error(e); - }, + onValidationChange: console.error, }; }, getInitialState: function() { return { - fieldValid: {}, + // Field error codes by field ID + fieldErrors: {}, // The ISO2 country code selected in the phone number entry phoneCountry: this.props.defaultPhoneCountry, }; @@ -81,12 +80,12 @@ module.exports = React.createClass({ // the error that ends up being displayed // is the one from the first invalid field. // It's not super ideal that this just calls - // onError once for each invalid field. + // onValidationChange once for each invalid field. + 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); - this.validateField(FIELD_PHONE_NUMBER, ev.type); - this.validateField(FIELD_EMAIL, ev.type); const self = this; if (this.allFieldsValid()) { @@ -134,9 +133,9 @@ module.exports = React.createClass({ * @returns {boolean} true if all fields were valid last time they were validated. */ allFieldsValid: function() { - const keys = Object.keys(this.state.fieldValid); + const keys = Object.keys(this.state.fieldErrors); for (let i = 0; i < keys.length; ++i) { - if (this.state.fieldValid[keys[i]] == false) { + if (this.state.fieldErrors[keys[i]]) { return false; } } @@ -206,21 +205,29 @@ module.exports = React.createClass({ } break; case FIELD_PASSWORD_CONFIRM: - this.markFieldValid( - fieldID, pwd1 == pwd2, - "RegistrationForm.ERR_PASSWORD_MISMATCH", - ); + if (allowEmpty && pwd2 === "") { + this.markFieldValid(fieldID, true); + } else { + this.markFieldValid( + fieldID, pwd1 == pwd2, + "RegistrationForm.ERR_PASSWORD_MISMATCH", + ); + } break; } }, - markFieldValid: function(fieldID, val, errorCode) { - const fieldValid = this.state.fieldValid; - fieldValid[fieldID] = val; - this.setState({fieldValid: fieldValid}); - if (!val) { - this.props.onError(errorCode); + markFieldValid: function(fieldID, valid, errorCode) { + const { fieldErrors } = this.state; + if (valid) { + fieldErrors[fieldID] = null; + } else { + fieldErrors[fieldID] = errorCode; } + this.setState({ + fieldErrors, + }); + this.props.onValidationChange(fieldErrors); }, fieldElementById(fieldID) { @@ -240,7 +247,7 @@ module.exports = React.createClass({ _classForField: function(fieldID, ...baseClasses) { let cls = baseClasses.join(' '); - if (this.state.fieldValid[fieldID] === false) { + if (this.state.fieldErrors[fieldID]) { if (cls) cls += ' '; cls += 'error'; }