Visitar URL original
USWDS - Bug: Fix invalid aria role in validator list items by amyleadem · Pull Request #4914 · uswds/uswds · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0684b17
Pull in changes from cm-validation-a11y
Aug 8, 2022
f077275
Remove aria-checked attribute
Aug 8, 2022
0a19319
Add tabindex to usa-checklist
Aug 8, 2022
14f06bc
Remove tabindex from template
Aug 11, 2022
29c2b61
Add tabindex to validation list items
Aug 11, 2022
07ad7de
Add aria-label to input element
Sep 20, 2022
8b0bff8
Change span to i
Sep 20, 2022
abc6fb4
Remove comma
Sep 20, 2022
45cdf3d
Add aria label on init
Sep 21, 2022
511d666
Clean up code
Sep 21, 2022
6e89868
Change innerText to textContent
Sep 21, 2022
2a56879
Replace generated span with aria-label
Sep 22, 2022
8be3392
Create container for status summary; add status updates
Sep 26, 2022
34c5be9
Replace 'item' with 'input'
Sep 26, 2022
eeb2e18
Create data-validation-complete and data-validation-incomplete
Sep 26, 2022
7b53b94
Create enhanceValidation function
Sep 27, 2022
c284450
Rename data-checklist-label to data-validation-status
Sep 27, 2022
36a1de6
Update package-lock
Oct 5, 2022
393d235
Add unique id to summary element
Oct 5, 2022
fb807ba
Rename change to handleChange
Oct 5, 2022
73b954f
Remove input aria-describedby from twig
Oct 5, 2022
a57aff1
Move selectOrMatches to init
Oct 5, 2022
853fb7d
Separate functions into createStatusElement and createInitialStatus
Oct 6, 2022
9162036
Add debounce
Oct 6, 2022
aa38ecb
Update handleChange
Oct 6, 2022
545a7d9
Pull in develop
Oct 6, 2022
ab76749
Update statusSummaryID
Oct 6, 2022
aea87ce
Simplify statusComplete and statusIncomplete data check
Oct 7, 2022
bfbfd52
Add readability with srUpdateStatus()
Oct 7, 2022
e41d2fe
Consolidate handleChange function
Oct 9, 2022
ef0c9db
Run prettier
Oct 9, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 34 additions & 4 deletions packages/usa-validation/src/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,44 @@
const behavior = require("../../uswds-core/src/js/utils/behavior");
const validate = require("../../uswds-core/src/js/utils/validate-input");
const { prefix: PREFIX } = require("../../uswds-core/src/js/config");
const selectOrMatches = require("../../uswds-core/src/js/utils/select-or-matches");

const VALIDATE_INPUT = "input[data-validation-element]";
const CHECKLIST_ITEM = `.${PREFIX}-checklist__item`;

function change() {
validate(this);
}

const validator = behavior({
"keyup change": {
"input[data-validation-element]": change,
const validator = behavior(
{
"input change": {
[VALIDATE_INPUT]: change,
Comment thread
amyleadem marked this conversation as resolved.
Resolved
},
},
});
{
init(root) {
selectOrMatches(VALIDATE_INPUT, root).forEach((item) => {
Comment thread
amyleadem marked this conversation as resolved.
Resolved
const validationParent = item.parentNode;
const checklistItems =
validationParent.querySelectorAll(CHECKLIST_ITEM);

item.setAttribute("aria-controls", "validate-code");
item.setAttribute("aria-live", "polite");
Comment thread
amyleadem marked this conversation as resolved.
Resolved
item.setAttribute("aria-atomic", true);
Comment thread
amyleadem marked this conversation as resolved.
Resolved
Comment thread
amyleadem marked this conversation as resolved.
Resolved

let inputLabel = "";
Comment thread
amyleadem marked this conversation as resolved.
Resolved
checklistItems.forEach((listItem) => {
const itemStatus = `${listItem.textContent} status incomplete`;
Comment thread
amyleadem marked this conversation as resolved.
Resolved
listItem.setAttribute("tabindex", "0");
listItem.setAttribute("aria-label", itemStatus);
inputLabel += `${itemStatus}. `;
});

item.setAttribute("aria-label", inputLabel);
Comment thread
amyleadem marked this conversation as resolved.
Resolved
Comment thread
amyleadem marked this conversation as resolved.
Resolved
});
},
Comment thread
amyleadem marked this conversation as resolved.
Resolved
}
);

module.exports = validator;
19 changes: 14 additions & 5 deletions packages/usa-validation/src/test/template.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
<div>
<ul class="usa-checklist" id="validation_list">
<li class="usa-checklist__item" data-validator="length">Be at least 8 characters</li>
<li class="usa-checklist__item" data-validator="uppercase">Have at least 1 uppercase character</li>
<li class="usa-checklist__item" data-validator="numerical">Have at least 1 numerical character</li>
<li class="usa-checklist__item" data-validator="length">
Be at least 8 characters
</li>
<li class="usa-checklist__item" data-validator="uppercase">
Have at least 1 uppercase character
</li>
<li class="usa-checklist__item" data-validator="numerical">
Have at least 1 numerical character
</li>
</ul>
<input
class="usa-input"
id="password" name="password" type="password"
id="password"
name="password"
type="password"
aria-describedby="validation_list"
data-validate-length=".{8,}"
data-validate-uppercase="[A-Z]"
data-validate-numerical="\d"
data-validation-element="validation_list">
data-validation-element="validation_list"
/>
</div>
30 changes: 22 additions & 8 deletions packages/usa-validation/src/test/validator.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,61 @@ const TEMPLATE = fs.readFileSync(path.join(__dirname, "/template.html"));

const INPUT_SELECTOR = "[data-validation-element]";
const VALIDATORS = "[data-validator]";
const VALIDATOR_LABEL = "[data-checklist-label]";
const CHECKED_CLASS = "usa-checklist__item--checked";

const inputSelector = () => document.querySelector(INPUT_SELECTOR);

const keyup = (el) => {
el.dispatchEvent(new KeyboardEvent("keyup", { bubbles: true }));
el.dispatchEvent(new Event("change", { bubbles: true }));
};

const tests = [
{ name: "document.body", selector: () => document.body },
{ name: "input", selector: () => document.querySelector(INPUT_SELECTOR) }
{ name: "input", selector: inputSelector },
];

tests.forEach(({name, selector: containerSelector}) => {
tests.forEach(({ name, selector: containerSelector }) => {
describe(`validator component initialized at ${name}`, () => {
const { body } = document;

let root;
let validated;
let validators;

beforeEach(() => {
body.innerHTML = TEMPLATE;

validated = document.querySelector(INPUT_SELECTOR);
validators = Array.from(document.querySelectorAll(VALIDATORS));

validator.on(containerSelector());

root = inputSelector();
validated = inputSelector();
validators = root.querySelectorAll(VALIDATORS);
});

afterEach(() => {
validator.off(containerSelector());
body.textContent = "";
});

describe("initialization", () => {
it("creates a hidden span element in each list item", () => {
validators.forEach((checkbox) => {
assert.notStrictEqual(checkbox.queryElement(VALIDATOR_LABEL), null);
});
});
});

describe("validation state", () => {
it("adds ." + CHECKED_CLASS + " for all successful validations", () => {
it(`adds .${CHECKED_CLASS} for all successful validations`, () => {
validated.value = "GreatPassword1";
keyup(validated);
validators.forEach((checkbox) => {
assert.strictEqual(checkbox.classList.contains(CHECKED_CLASS), true);
});
});

it("removes ." + CHECKED_CLASS + " for failed validations", () => {
it(`removes .${CHECKED_CLASS} for failed validations`, () => {
validated.value = "GreatPassword";
keyup(validated);
validators.forEach((checkbox) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/usa-validation/src/usa-validation.twig
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<div class="usa-alert__body">
<h3 class="usa-alert__heading">Code requirements</h3>
<ul class="usa-checklist" id="validate-code">
<li class="usa-checklist__item usa-checklist__item--checked" data-validator="uppercase">Use at least one uppercase character</li>
<li class="usa-checklist__item" data-validator="uppercase">Use at least one uppercase character</li>
<li class="usa-checklist__item" data-validator="numerical">Use at least one number</li>
</ul>
</div>
Expand Down
15 changes: 13 additions & 2 deletions packages/uswds-core/src/js/utils/validate-input.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const { prefix: PREFIX } = require("../config");

const CHECKED = "aria-checked";
const CHECKED_CLASS = `${PREFIX}-checklist__item--checked`;

module.exports = function validate(el) {
Expand All @@ -14,6 +13,8 @@ module.exports = function validate(el) {
throw new Error(`No validation element found with id: "${id}"`);
}

// create element to hold aria-label content for input element
let inputLabel = "";
Object.entries(el.dataset).forEach(([key, value]) => {
if (key.startsWith("validate")) {
const validatorName = key.substr("validate".length).toLowerCase();
Expand All @@ -27,7 +28,17 @@ module.exports = function validate(el) {

const checked = validatorPattern.test(el.value);
validatorCheckbox.classList.toggle(CHECKED_CLASS, checked);
validatorCheckbox.setAttribute(CHECKED, checked);

let validatorStatus = `${validatorCheckbox.textContent} status `;
if(validatorCheckbox.classList.contains(CHECKED_CLASS)){
validatorStatus += "complete";
} else{
validatorStatus += "incomplete";
};

validatorCheckbox.setAttribute("aria-label", validatorStatus);
inputLabel += `${validatorStatus}. `;
el.setAttribute("aria-label", inputLabel);
}
});
};