mirror of https://github.com/vector-im/riot-web
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.pull/21833/head
parent
a7c37733b8
commit
1cbb4be6f7
|
@ -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.');
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue