Add support for validating more strictly at submit time

When submitting a form, we want to validate more strictly to check for empty
values that might be required. A separate mode is used since we want to ignore
this issue when visiting a field one by one to enter data.

As an example, we convert the pre-existing logic for the username requirement
using this new support.
This commit is contained in:
J. Ryan Stinnett 2019-04-18 21:33:37 +01:00
parent a7c37733b8
commit 1cbb4be6f7
5 changed files with 36 additions and 38 deletions

View file

@ -344,12 +344,6 @@ module.exports = React.createClass({
case "RegistrationForm.ERR_MISSING_PHONE_NUMBER":
errMsg = _t('A phone number is required to register on this homeserver.');
break;
case "RegistrationForm.ERR_USERNAME_INVALID":
errMsg = _t("A username can only contain lower case letters, numbers and '=_-./'");
break;
case "RegistrationForm.ERR_USERNAME_BLANK":
errMsg = _t('You need to enter a username.');
break;
default:
console.error("Unknown error code: %s", errCode);
errMsg = _t('An unknown error occurred.');

View file

@ -94,7 +94,6 @@ module.exports = React.createClass({
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) {
@ -142,23 +141,38 @@ module.exports = React.createClass({
},
verifyFieldsBeforeSubmit() {
if (this.allFieldsValid()) {
return true;
}
const invalidField = this.findFirstInvalidField([
const fieldIDsInDisplayOrder = [
FIELD_USERNAME,
FIELD_PASSWORD,
FIELD_PASSWORD_CONFIRM,
FIELD_EMAIL,
FIELD_PHONE_NUMBER,
]);
];
// Run all fields with stricter validation that no longer allows empty
// values for required fields.
for (const fieldID of fieldIDsInDisplayOrder) {
const field = this[fieldID];
if (!field) {
continue;
}
field.validate({ allowEmpty: false });
}
if (this.allFieldsValid()) {
return true;
}
const invalidField = this.findFirstInvalidField(fieldIDsInDisplayOrder);
if (!invalidField) {
return true;
}
// Focus the first invalid field and show feedback in the stricter mode
// that no longer allows empty values for required fields.
invalidField.focus();
invalidField.validate({ allowEmpty: false, focused: true });
return false;
},
@ -215,21 +229,6 @@ module.exports = React.createClass({
} else this.markFieldError(fieldID, phoneNumberValid, "RegistrationForm.ERR_PHONE_NUMBER_INVALID");
break;
}
case FIELD_USERNAME: {
const username = this.state.username;
if (allowEmpty && username === '') {
this.markFieldError(fieldID, true);
} else if (username == '') {
this.markFieldError(
fieldID,
false,
"RegistrationForm.ERR_USERNAME_BLANK",
);
} else {
this.markFieldError(fieldID, true);
}
break;
}
case FIELD_PASSWORD:
if (allowEmpty && pwd1 === "") {
this.markFieldError(fieldID, true);
@ -358,9 +357,14 @@ module.exports = React.createClass({
validateUsernameRules: withValidation({
description: () => _t("Use letters, numbers, dashes and underscores only"),
rules: [
{
key: "required",
test: ({ value, allowEmpty }) => allowEmpty || !!value,
invalid: () => _t("Enter username"),
},
{
key: "safeLocalpart",
regex: SAFE_LOCALPART_REGEX,
test: ({ value }) => !value || SAFE_LOCALPART_REGEX.test(value),
invalid: () => _t("Some characters not allowed"),
},
],
@ -393,7 +397,6 @@ module.exports = React.createClass({
renderUsername() {
const Field = sdk.getComponent('elements.Field');
return <Field
className={this._classForField(FIELD_USERNAME)}
id="mx_RegistrationForm_username"
ref={field => this[FIELD_USERNAME] = field}
type="text"

View file

@ -87,14 +87,15 @@ export default class Field extends React.PureComponent {
this.input.focus();
}
validate({ focused }) {
validate({ focused, allowEmpty = true }) {
if (!this.props.onValidate) {
return;
}
const { value } = this.input;
const value = this.input ? this.input.value : null;
const { valid, feedback } = this.props.onValidate({
value,
focused,
allowEmpty,
});
this.setState({
valid,

View file

@ -26,7 +26,7 @@ import classNames from 'classnames';
* An array of rules describing how to check to input value. Each rule in an object
* and may have the following properties:
* - `key`: A unique ID for the rule. Required.
* - `regex`: A regex used to determine the rule's current validity. Required.
* - `test`: A function used to determine the rule's current validity. Required.
* - `valid`: Function returning text to show when the rule is valid. Only shown if set.
* - `invalid`: Function returning text to show when the rule is invalid. Only shown if set.
* @returns {Function}
@ -34,9 +34,9 @@ import classNames from 'classnames';
* the overall validity and a feedback UI that can be rendered for more detail.
*/
export default function withValidation({ description, rules }) {
return function onValidate({ value, focused }) {
return function onValidate({ value, focused, allowEmpty = true }) {
// TODO: Re-run only after ~200ms of inactivity
if (!value) {
if (!value && allowEmpty) {
return {
valid: null,
feedback: null,
@ -47,10 +47,10 @@ export default function withValidation({ description, rules }) {
let valid = true;
if (rules && rules.length) {
for (const rule of rules) {
if (!rule.key || !rule.regex) {
if (!rule.key || !rule.test) {
continue;
}
const ruleValid = rule.regex.test(value);
const ruleValid = rule.test({ value, allowEmpty });
valid = valid && ruleValid;
if (ruleValid && rule.valid) {
// If the rule's result is valid and has text to show for

View file

@ -1324,6 +1324,7 @@
"If you don't specify an email address, you won't be able to reset your password. Are you sure?": "If you don't specify an email address, you won't be able to reset your password. Are you sure?",
"Use letters, numbers, dashes and underscores only": "Use letters, numbers, dashes and underscores only",
"Some characters not allowed": "Some characters not allowed",
"Enter username": "Enter username",
"Create your Matrix account": "Create your Matrix account",
"Create your Matrix account on %(serverName)s": "Create your Matrix account on %(serverName)s",
"Email (optional)": "Email (optional)",
@ -1524,7 +1525,6 @@
"This doesn't look like a valid phone number.": "This doesn't look like a valid phone number.",
"An email address is required to register on this homeserver.": "An email address is required to register on this homeserver.",
"A phone number is required to register on this homeserver.": "A phone number is required to register on this homeserver.",
"You need to enter a username.": "You need to enter a username.",
"An unknown error occurred.": "An unknown error occurred.",
"Create your account": "Create your account",
"Commands": "Commands",