Skip to content

Commit 372ac60

Browse files
authored
feat(role): support {pressed: true} for buttons (#729)
Hat tip to Sebastian Silbermann for his prior work on PR #692
1 parent ed87ae9 commit 372ac60

File tree

4 files changed

+58
-0
lines changed

4 files changed

+58
-0
lines changed

src/__tests__/ariaAttributes.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@ test('`selected` throws on unsupported roles', () => {
99
)
1010
})
1111

12+
test('`pressed` throws on unsupported roles', () => {
13+
const {getByRole} = render(`<input aria-pressed="true" type="text" />`)
14+
expect(() =>
15+
getByRole('textbox', {pressed: true}),
16+
).toThrowErrorMatchingInlineSnapshot(
17+
`"\\"aria-pressed\\" is not supported on role \\"textbox\\"."`,
18+
)
19+
})
20+
1221
test('`checked` throws on unsupported roles', () => {
1322
const {getByRole} = render(`<input aria-checked="true" type="text">`)
1423
expect(() =>
@@ -136,3 +145,25 @@ test('`selected: true` matches `aria-selected="true"` on supported roles', () =>
136145
'selected-tab',
137146
])
138147
})
148+
149+
test('`pressed: true|false` matches `pressed` buttons', () => {
150+
const {getByRole} = renderIntoDocument(
151+
`<div>
152+
<button aria-pressed="true" />
153+
<button aria-pressed="false" />
154+
</div>`,
155+
)
156+
expect(getByRole('button', {pressed: true})).toBeInTheDocument()
157+
expect(getByRole('button', {pressed: false})).toBeInTheDocument()
158+
})
159+
160+
test('`pressed: true|false` matches `pressed` elements with proper role', () => {
161+
const {getByRole} = renderIntoDocument(
162+
`<div>
163+
<span role="button" aria-pressed="true">✔</span>
164+
<span role="button" aria-pressed="false">𝒙</span>
165+
</div>`,
166+
)
167+
expect(getByRole('button', {pressed: true})).toBeInTheDocument()
168+
expect(getByRole('button', {pressed: false})).toBeInTheDocument()
169+
})

src/queries/role.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {roles as allRoles} from 'aria-query'
33
import {
44
computeAriaSelected,
55
computeAriaChecked,
6+
computeAriaPressed,
67
getImplicitAriaRoles,
78
prettyRoles,
89
isInaccessible,
@@ -31,6 +32,7 @@ function queryAllByRole(
3132
queryFallbacks = false,
3233
selected,
3334
checked,
35+
pressed,
3436
} = {},
3537
) {
3638
checkContainerType(container)
@@ -51,6 +53,13 @@ function queryAllByRole(
5153
}
5254
}
5355

56+
if (pressed !== undefined) {
57+
// guard against unknown roles
58+
if (allRoles.get(role)?.props['aria-pressed'] === undefined) {
59+
throw new Error(`"aria-pressed" is not supported on role "${role}".`)
60+
}
61+
}
62+
5463
const subtreeIsInaccessibleCache = new WeakMap()
5564
function cachedIsSubtreeInaccessible(element) {
5665
if (!subtreeIsInaccessibleCache.has(element)) {
@@ -94,6 +103,9 @@ function queryAllByRole(
94103
if (checked !== undefined) {
95104
return checked === computeAriaChecked(element)
96105
}
106+
if (pressed !== undefined) {
107+
return pressed === computeAriaPressed(element)
108+
}
97109
// don't care if aria attributes are unspecified
98110
return true
99111
})

src/role-helpers.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,15 @@ function computeAriaChecked(element) {
209209
return checkBooleanAttribute(element, 'aria-checked')
210210
}
211211

212+
/**
213+
* @param {Element} element -
214+
* @returns {boolean | undefined} - false/true if (not)pressed, undefined if not press-able
215+
*/
216+
function computeAriaPressed(element) {
217+
// https://www.w3.org/TR/wai-aria-1.1/#aria-pressed
218+
return checkBooleanAttribute(element, 'aria-pressed')
219+
}
220+
212221
function checkBooleanAttribute(element, attribute) {
213222
const attributeValue = element.getAttribute(attribute)
214223
if (attributeValue === 'true') {
@@ -229,4 +238,5 @@ export {
229238
isInaccessible,
230239
computeAriaSelected,
231240
computeAriaChecked,
241+
computeAriaPressed,
232242
}

types/queries.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ export interface ByRoleOptions extends MatcherOptions {
8383
* checked in the accessibility tree, i.e., `aria-checked="true"`
8484
*/
8585
checked?: boolean
86+
/**
87+
* If true only includes elements in the query set that are marked as
88+
* pressed in the accessibility tree, i.e., `aria-pressed="true"`
89+
*/
90+
pressed?: boolean
8691
/**
8792
* Includes every role used in the `role` attribute
8893
* For example *ByRole('progressbar', {queryFallbacks: true})` will find <div role="meter progressbar">`.

0 commit comments

Comments
 (0)