Skip to content

Commit

Permalink
Align R18 with specification (#1541)
Browse files Browse the repository at this point in the history
* Add updated predicate for checking allowed attributes for input types

* Add comment about change to input type=email

* Add changeset

* Update .changeset/violet-coats-tie.md

Co-authored-by: Jean-Yves Moyen <[email protected]>

---------

Co-authored-by: Jean-Yves Moyen <[email protected]>
  • Loading branch information
rcj-siteimprove and Jym77 authored Dec 20, 2023
1 parent 62ca5ec commit 73d5efb
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changeset/violet-coats-tie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@siteimprove/alfa-rules": patch
---

**Fixed:** SIA-R18 now accepts attributes for `input type=file` and `input type=color` according to the [ARIA in HTML](https://w3c.github.io/html-aria/#el-input-file) specification
1 change: 1 addition & 0 deletions packages/alfa-aria/src/feature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ const Features: Features = {
return element.attribute("list").isSome()
? "combobox"
: "searchbox";
// Note: The specification for email has changed, it now has role textbox. We should look into this if it becomes an issue.
case "email":
case "tel":
case "text":
Expand Down
39 changes: 28 additions & 11 deletions packages/alfa-rules/src/sia-r18/rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,33 @@ export default Rule.Atomic.of<Page, Attribute>({
},
});

function allowedForInputType(
attributeName: aria.Attribute.Name,
): Predicate<Element> {
return hasInputType((inputType) => {
switch (inputType) {
case "color":
return attributeName === "aria-disabled";
case "date":
case "datetime-local":
case "email":
case "month":
case "password":
case "time":
case "week":
return Role.of("textbox").isAttributeSupported(attributeName);
case "file":
return (
attributeName === "aria-disabled" ||
attributeName === "aria-invalid" ||
attributeName === "aria-required"
);
default:
return false;
}
});
}

function ariaHtmlAllowed(target: Attribute): boolean {
const attributeName = target.name as aria.Attribute.Name;
for (const element of target.owner) {
Expand All @@ -78,17 +105,7 @@ function ariaHtmlAllowed(target: Attribute): boolean {
return Role.of("document").isAttributeSupported(attributeName);

case "input":
return (
hasInputType(
"date",
"datetime-local",
"email",
"month",
"password",
"time",
"week",
)(element) && Role.of("textbox").isAttributeSupported(attributeName)
);
return allowedForInputType(attributeName)(element);

case "select":
return (
Expand Down
52 changes: 52 additions & 0 deletions packages/alfa-rules/test/sia-r18/rule.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,58 @@ test(`evaluate() passes input with type password field and aria-required state`,
]);
});

test(`evaluate() passes input with type file field and aria-disabled state`, async (t) => {
const target = <input type="file" aria-disabled="true" />;

const document = h.document([target]);

t.deepEqual(await evaluate(R18, { document }), [
passed(R18, target.attribute("aria-disabled").getUnsafe(), {
1: Outcomes.IsAllowed,
2: Outcomes.IsNotProhibited,
}),
]);
});

test(`evaluate() passes input with type file field and aria-invalid state`, async (t) => {
const target = <input type="file" aria-invalid="true" />;

const document = h.document([target]);

t.deepEqual(await evaluate(R18, { document }), [
passed(R18, target.attribute("aria-invalid").getUnsafe(), {
1: Outcomes.IsAllowed,
2: Outcomes.IsNotProhibited,
}),
]);
});

test(`evaluate() passes input with type file field and aria-required state`, async (t) => {
const target = <input type="file" aria-required="true" />;

const document = h.document([target]);

t.deepEqual(await evaluate(R18, { document }), [
passed(R18, target.attribute("aria-required").getUnsafe(), {
1: Outcomes.IsAllowed,
2: Outcomes.IsNotProhibited,
}),
]);
});

test(`evaluate() passes input with type color field and aria-disabled state`, async (t) => {
const target = <input type="color" aria-disabled="true" />;

const document = h.document([target]);

t.deepEqual(await evaluate(R18, { document }), [
passed(R18, target.attribute("aria-disabled").getUnsafe(), {
1: Outcomes.IsAllowed,
2: Outcomes.IsNotProhibited,
}),
]);
});

test(`evaluate() passes a button with aria-pressed state`, async (t) => {
const target = <button aria-pressed="false">My button</button>;

Expand Down

0 comments on commit 73d5efb

Please sign in to comment.