From f1ecf6c1ea28b0289525a660bfb7d8b60d5e11cb Mon Sep 17 00:00:00 2001 From: Luis Rodrigues Date: Fri, 13 Nov 2020 21:09:36 +0000 Subject: [PATCH 1/2] feat: adds aria-expanded filter to *ByRole --- src/__tests__/ariaAttributes.js | 31 +++++++++++++++++++++++++++++++ src/queries/role.js | 12 ++++++++++++ src/role-helpers.js | 10 ++++++++++ types/queries.d.ts | 5 +++++ 4 files changed, 58 insertions(+) diff --git a/src/__tests__/ariaAttributes.js b/src/__tests__/ariaAttributes.js index 6ee4bb2c..84334230 100644 --- a/src/__tests__/ariaAttributes.js +++ b/src/__tests__/ariaAttributes.js @@ -27,6 +27,15 @@ test('`checked` throws on unsupported roles', () => { ) }) +test('`expanded` throws on unsupported roles', () => { + const {getByRole} = render(`

Heading

`) + expect(() => + getByRole('heading', {expanded: true}), + ).toThrowErrorMatchingInlineSnapshot( + `"\\"aria-expanded\\" is not supported on role \\"heading\\"."`, + ) +}) + test('`checked: true|false` matches `checked` checkboxes', () => { const {getByRole} = renderIntoDocument( `
@@ -202,3 +211,25 @@ test('`level` throws on unsupported roles', () => { `"Role \\"button\\" cannot have \\"level\\" property."`, ) }) + +test('`expanded: true|false` matches `expanded` buttons', () => { + const {getByRole} = renderIntoDocument( + `
+
`, + ) + expect(getByRole('button', {expanded: true})).toBeInTheDocument() + expect(getByRole('button', {expanded: false})).toBeInTheDocument() +}) + +test('`expanded: true|false` matches `expanded` elements with proper role', () => { + const {getByRole} = renderIntoDocument( + `
+ + +
`, + ) + expect(getByRole('button', {expanded: true})).toBeInTheDocument() + expect(getByRole('button', {expanded: false})).toBeInTheDocument() +}) diff --git a/src/queries/role.js b/src/queries/role.js index 3102de9f..747e2ca0 100644 --- a/src/queries/role.js +++ b/src/queries/role.js @@ -4,6 +4,7 @@ import { computeAriaSelected, computeAriaChecked, computeAriaPressed, + computeAriaExpanded, computeHeadingLevel, getImplicitAriaRoles, prettyRoles, @@ -35,6 +36,7 @@ function queryAllByRole( checked, pressed, level, + expanded, } = {}, ) { checkContainerType(container) @@ -69,6 +71,13 @@ function queryAllByRole( } } + if (expanded !== undefined) { + // guard against unknown roles + if (allRoles.get(role)?.props['aria-expanded'] === undefined) { + throw new Error(`"aria-expanded" is not supported on role "${role}".`) + } + } + const subtreeIsInaccessibleCache = new WeakMap() function cachedIsSubtreeInaccessible(element) { if (!subtreeIsInaccessibleCache.has(element)) { @@ -115,6 +124,9 @@ function queryAllByRole( if (pressed !== undefined) { return pressed === computeAriaPressed(element) } + if (expanded !== undefined) { + return expanded === computeAriaExpanded(element) + } if (level !== undefined) { return level === computeHeadingLevel(element) } diff --git a/src/role-helpers.js b/src/role-helpers.js index 816e592e..57a8a330 100644 --- a/src/role-helpers.js +++ b/src/role-helpers.js @@ -222,6 +222,15 @@ function computeAriaPressed(element) { return checkBooleanAttribute(element, 'aria-pressed') } +/** + * @param {Element} element - + * @returns {boolean | undefined} - false/true if (not)expanded, undefined if not expand-able + */ +function computeAriaExpanded(element) { + // https://www.w3.org/TR/wai-aria-1.1/#aria-pressed + return checkBooleanAttribute(element, 'aria-expanded') +} + function checkBooleanAttribute(element, attribute) { const attributeValue = element.getAttribute(attribute) if (attributeValue === 'true') { @@ -267,5 +276,6 @@ export { computeAriaSelected, computeAriaChecked, computeAriaPressed, + computeAriaExpanded, computeHeadingLevel, } diff --git a/types/queries.d.ts b/types/queries.d.ts index 8418d99c..d81fdcef 100644 --- a/types/queries.d.ts +++ b/types/queries.d.ts @@ -88,6 +88,11 @@ export interface ByRoleOptions extends MatcherOptions { * pressed in the accessibility tree, i.e., `aria-pressed="true"` */ pressed?: boolean + /** + * If true only includes elements in the query set that are marked as + * expanded in the accessibility tree, i.e., `aria-expanded="true"` + */ + expanded?: boolean /** * Includes elements with the `"heading"` role matching the indicated level, * either by the semantic HTML heading elements `

-

` or matching From 3e092533d85e06b725918e449b7debfed5672f81 Mon Sep 17 00:00:00 2001 From: Luis Rodrigues Date: Fri, 13 Nov 2020 21:17:43 +0000 Subject: [PATCH 2/2] docs: fix documentation link --- src/role-helpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/role-helpers.js b/src/role-helpers.js index 57a8a330..2d6ef0f6 100644 --- a/src/role-helpers.js +++ b/src/role-helpers.js @@ -227,7 +227,7 @@ function computeAriaPressed(element) { * @returns {boolean | undefined} - false/true if (not)expanded, undefined if not expand-able */ function computeAriaExpanded(element) { - // https://www.w3.org/TR/wai-aria-1.1/#aria-pressed + // https://www.w3.org/TR/wai-aria-1.1/#aria-expanded return checkBooleanAttribute(element, 'aria-expanded') }