Skip to content

Commit a593ee0

Browse files
feat: support "none" role as a synonym for the "presentation" role (#933)
* feat: support `"none"` role as a synonym for the `"presentation"` role * Create yellow-poems-cry.md * test: add test coverage for `none` synonym role * Update sources/__tests__/accessible-name.js --------- Co-authored-by: Sebastian Silbermann <[email protected]>
1 parent 7e0a7f1 commit a593ee0

File tree

8 files changed

+35
-3
lines changed

8 files changed

+35
-3
lines changed

.changeset/yellow-poems-cry.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"dom-accessibility-api": patch
3+
---
4+
5+
Support `"none"` role as a synonym for the `"presentation"` role

sources/__tests__/accessible-description.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ describe("wpt copies", () => {
5555
`<img src="foo.jpg" data-test alt="test" aria-describedby="t1"><span id="t1" role="presentation">foo</span>`,
5656
"foo",
5757
],
58+
[
59+
`<img src="foo.jpg" data-test alt="test" aria-describedby="t1"><span id="t1" role="none">foo</span>`,
60+
"foo",
61+
],
5862
[
5963
`<a data-test href="#" aria-label="California" title="San Francisco" >United States</a>`,
6064
"San Francisco",

sources/__tests__/accessible-name.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,13 @@ test.each([
285285
`
286286
<div data-test aria-labelledby="label">I reference my name</div>
287287
<div id="label" role="presentation">I'm prohibited a name</div>
288+
`,
289+
"I'm prohibited a name",
290+
],
291+
[
292+
`
293+
<div data-test aria-labelledby="label">I reference my name</div>
294+
<div id="label" role="none">I'm prohibited a name</div>
288295
`,
289296
"I'm prohibited a name",
290297
],
@@ -406,6 +413,7 @@ test.each([
406413
`<img data-test alt="" aria-label="a logo" role="presentation" /> />`,
407414
"a logo",
408415
],
416+
[`<img data-test alt="" aria-label="a logo" role="none" />`, "a logo"],
409417
[` <input type="radio" data-test title="crazy"/>`, "crazy"],
410418
[
411419
`
@@ -467,6 +475,7 @@ describe("prohibited naming", () => {
467475
["insertion", '<div data-test role="insertion">named?</div>'],
468476
["paragraph", '<div data-test role="paragraph">named?</div>'],
469477
["presentation", '<div data-test role="presentation">named?</div>'],
478+
["none", '<div data-test role="none">named?</div>'],
470479
["strong", '<div data-test role="strong">named?</div>'],
471480
["subscript", '<div data-test role="subscript">named?</div>'],
472481
["superscript", "<div data-test role='supscript'>Hello</div>"],
@@ -518,6 +527,11 @@ describe("prohibited naming", () => {
518527
"<button data-test><span role='presentation'>icon</span></button>",
519528
"icon",
520529
],
530+
[
531+
"presentation",
532+
"<button data-test><span role='none'>icon</span></button>",
533+
"icon",
534+
],
521535
[
522536
"strong",
523537
"<button data-test><span role='strong'>CLICK!</span></button>",

sources/__tests__/getRole.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,10 @@ const cases = [
176176
// https://rawgit.com/w3c/aria/stable/#conflict_resolution_presentation_none
177177
["presentational <img /> with accessible name", "img", createElementFactory("img", {alt: "", 'aria-label': "foo"})],
178178
["presentational <h1 /> global aria attributes", "heading", createElementFactory("h1", {'aria-describedby': "comment-1", role: "presentation"})],
179+
["presentational <h1 /> global aria attributes", "heading", createElementFactory("h1", {'aria-describedby': "comment-1", role: "none"})],
179180
// <div /> isn't mapped to `"generic"` yet so implicit semantics are `No role`
180181
["presentational <div /> with prohibited aria attributes", null, createElementFactory("div", {'aria-label': "hello", role: "presentation"})],
182+
["presentational <div /> with prohibited aria attributes", null, createElementFactory("div", {'aria-label': "hello", role: "none"})],
181183
];
182184

183185
it.each(cases)("%s has the role %s", (name, role, elementFactory) => {

sources/accessible-name-and-description.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
isSVGTitleElement,
2121
queryIdRefs,
2222
getLocalName,
23+
presentationRoles,
2324
} from "./util";
2425

2526
/**
@@ -150,7 +151,7 @@ function querySelectedOptions(listbox: Element): ArrayLike<Element> {
150151
}
151152

152153
function isMarkedPresentational(node: Node): node is Element {
153-
return hasAnyConcreteRoles(node, ["none", "presentation"]);
154+
return hasAnyConcreteRoles(node, presentationRoles);
154155
}
155156

156157
/**

sources/accessible-name.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ function prohibitsNaming(node: Node): boolean {
1515
"emphasis",
1616
"generic",
1717
"insertion",
18+
"none",
1819
"paragraph",
1920
"presentation",
2021
"strong",

sources/getRole.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// https://w3c.github.io/html-aria/#document-conformance-requirements-for-use-of-aria-attributes-in-html
22

3+
import { presentationRoles } from "./util";
4+
35
/**
46
* Safe Element.localName for all supported environments
57
* @param element
@@ -70,6 +72,7 @@ const prohibitedAttributes: Record<string, Set<string>> = {
7072
emphasis: new Set(["aria-label", "aria-labelledby"]),
7173
generic: new Set(["aria-label", "aria-labelledby", "aria-roledescription"]),
7274
insertion: new Set(["aria-label", "aria-labelledby"]),
75+
none: new Set(["aria-label", "aria-labelledby"]),
7376
paragraph: new Set(["aria-label", "aria-labelledby"]),
7477
presentation: new Set(["aria-label", "aria-labelledby"]),
7578
strong: new Set(["aria-label", "aria-labelledby"]),
@@ -126,10 +129,10 @@ function ignorePresentationalRole(
126129

127130
export default function getRole(element: Element): string | null {
128131
const explicitRole = getExplicitRole(element);
129-
if (explicitRole === null || explicitRole === "presentation") {
132+
if (explicitRole === null || presentationRoles.indexOf(explicitRole) !== -1) {
130133
const implicitRole = getImplicitRole(element);
131134
if (
132-
explicitRole !== "presentation" ||
135+
presentationRoles.indexOf(explicitRole || "") === -1 ||
133136
ignorePresentationalRole(element, implicitRole || "")
134137
) {
135138
return implicitRole;

sources/util.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
export { getLocalName } from "./getRole";
22
import getRole, { getLocalName } from "./getRole";
33

4+
export const presentationRoles = ["presentation", "none"];
5+
46
export function isElement(node: Node | null): node is Element {
57
return node !== null && node.nodeType === node.ELEMENT_NODE;
68
}

0 commit comments

Comments
 (0)