This repository has been archived on 2023-03-18. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
osr-discourse-src/app/assets/javascripts/discourse/app/mixins/password-validation.js
T
Martin Brennan c401d6411b A11Y: Improve create account modal for screen readers (#14234)
Improves the create account modal for screen readers by doing the following:

* Making the `modal-alert` section into an `aria-role="alert"` region and making it show and hide using height instead of display:none so screen readers pick it up. Made a change so the field-related error messages are always shown beneath the field.
* Add `aria-invalid` and `aria-describedby` attributes to each field in the modal, so the screen reader will read out the error hint on error. This necessitated an Ember component extension to allow both the `aria-*` attributes to be bound and to render on `{{input}}`.
* Moved the social login buttons to the right in the HTML structure so they are not read out first.
* Added `aria-label` attributes to the login buttons so they can have different content for screen readers.
* In some cases for modals, the title that should be used for the `aria-labelledby` attribute is within the modal content and not the discourse-modal-title title. This introduces a new titleAriaElementId property to the d-modal component that is then used by the create-account modal to read out the title

------

This is the same as e0d2de73d8 but
fixes the Ember-input-component-extension to use the public
Ember components TextField and TextArea instead of the private
TextSupport so the extension works in both normal Ember and
Ember CLI.
2021-09-03 13:04:24 +10:00

112 lines
2.7 KiB
JavaScript

import EmberObject from "@ember/object";
import I18n from "I18n";
import Mixin from "@ember/object/mixin";
import discourseComputed from "discourse-common/utils/decorators";
import { isEmpty } from "@ember/utils";
export default Mixin.create({
rejectedPasswords: null,
init() {
this._super(...arguments);
this.set("rejectedPasswords", []);
this.set("rejectedPasswordsMessages", new Map());
},
@discourseComputed("passwordMinLength")
passwordInstructions() {
return I18n.t("user.password.instructions", {
count: this.passwordMinLength,
});
},
@discourseComputed("isDeveloper", "admin")
passwordMinLength(isDeveloper, admin) {
return isDeveloper || admin
? this.siteSettings.min_admin_password_length
: this.siteSettings.min_password_length;
},
@discourseComputed(
"accountPassword",
"passwordRequired",
"rejectedPasswords.[]",
"accountUsername",
"accountEmail",
"passwordMinLength",
"forceValidationReason"
)
passwordValidation(
password,
passwordRequired,
rejectedPasswords,
accountUsername,
accountEmail,
passwordMinLength,
forceValidationReason
) {
const failedAttrs = {
failed: true,
ok: false,
element: document.querySelector("#new-account-password"),
};
if (!passwordRequired) {
return EmberObject.create({ ok: true });
}
if (rejectedPasswords.includes(password)) {
return EmberObject.create(
Object.assign(failedAttrs, {
reason:
this.rejectedPasswordsMessages.get(password) ||
I18n.t("user.password.common"),
})
);
}
// If blank, fail without a reason
if (isEmpty(password)) {
return EmberObject.create(
Object.assign(failedAttrs, {
message: I18n.t("user.password.required"),
reason: forceValidationReason
? I18n.t("user.password.required")
: null,
})
);
}
// If too short
if (password.length < passwordMinLength) {
return EmberObject.create(
Object.assign(failedAttrs, {
reason: I18n.t("user.password.too_short"),
})
);
}
if (!isEmpty(accountUsername) && password === accountUsername) {
return EmberObject.create(
Object.assign(failedAttrs, {
reason: I18n.t("user.password.same_as_username"),
})
);
}
if (!isEmpty(accountEmail) && password === accountEmail) {
return EmberObject.create(
Object.assign(failedAttrs, {
reason: I18n.t("user.password.same_as_email"),
})
);
}
// Looks good!
return EmberObject.create({
ok: true,
reason: I18n.t("user.password.ok"),
});
},
});