Skip to content

Commit 5d628fa

Browse files
authored
Merge branch 'master' into pr/680
2 parents 6637beb + fd0d670 commit 5d628fa

File tree

6 files changed

+110
-2
lines changed

6 files changed

+110
-2
lines changed

.all-contributorsrc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,6 +1106,16 @@
11061106
"contributions": [
11071107
"doc"
11081108
]
1109+
},
1110+
{
1111+
"login": "idanen",
1112+
"name": "Idan Entin",
1113+
"avatar_url": "https://avatars2.githubusercontent.com/u/1687893?v=4",
1114+
"profile": "https://github.com/idanen",
1115+
"contributions": [
1116+
"code",
1117+
"test"
1118+
]
11091119
}
11101120
],
11111121
"repoHost": "https://github.com"

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,9 @@ Thanks goes to these people ([emoji key][emojis]):
276276
<td align="center"><a href="https://www.jacobparis.com/"><img src="https://avatars2.githubusercontent.com/u/5633704?v=4" width="100px;" alt=""/><br /><sub><b>Jacob Paris</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=JacobParis" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=JacobParis" title="Tests">⚠️</a></td>
277277
<td align="center"><a href="https://keiya01.github.io/portfolio"><img src="https://avatars1.githubusercontent.com/u/34934510?v=4" width="100px;" alt=""/><br /><sub><b>keiya sasaki</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=keiya01" title="Documentation">📖</a></td>
278278
</tr>
279+
<tr>
280+
<td align="center"><a href="https://github.com/idanen"><img src="https://avatars2.githubusercontent.com/u/1687893?v=4" width="100px;" alt=""/><br /><sub><b>Idan Entin</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=idanen" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=idanen" title="Tests">⚠️</a></td>
281+
</tr>
279282
</table>
280283

281284
<!-- markdownlint-enable -->

src/__tests__/ariaAttributes.js

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {render} from './helpers/test-utils'
1+
import {render, renderIntoDocument} from './helpers/test-utils'
22

33
test('`selected` throws on unsupported roles', () => {
44
const {getByRole} = render(`<input aria-selected="true" type="text">`)
@@ -9,6 +9,59 @@ test('`selected` throws on unsupported roles', () => {
99
)
1010
})
1111

12+
test('`checked` throws on unsupported roles', () => {
13+
const {getByRole} = render(`<input aria-checked="true" type="text">`)
14+
expect(() =>
15+
getByRole('textbox', {checked: true}),
16+
).toThrowErrorMatchingInlineSnapshot(
17+
`"\\"aria-checked\\" is not supported on role \\"textbox\\"."`,
18+
)
19+
})
20+
21+
test('`checked: true|false` matches `checked` checkboxes', () => {
22+
const {getByRole} = renderIntoDocument(
23+
`<div>
24+
<input type="checkbox" checked />
25+
<input type="checkbox" />
26+
</div>`,
27+
)
28+
expect(getByRole('checkbox', {checked: true})).toBeInTheDocument()
29+
expect(getByRole('checkbox', {checked: false})).toBeInTheDocument()
30+
})
31+
32+
test('`checked: true|false` matches `checked` elements with proper role', () => {
33+
const {getByRole} = renderIntoDocument(
34+
`<div>
35+
<span role="checkbox" aria-checked="true">✔</span>
36+
<span role="checkbox" aria-checked="false">𝒙</span>
37+
</div>`,
38+
)
39+
expect(getByRole('checkbox', {checked: true})).toBeInTheDocument()
40+
expect(getByRole('checkbox', {checked: false})).toBeInTheDocument()
41+
})
42+
43+
test('`checked: true|false` does not match element in `indeterminate` state', () => {
44+
const {queryByRole, getByLabelText} = renderIntoDocument(
45+
`<div>
46+
<span role="checkbox" aria-checked="mixed">not so much</span>
47+
<input type="checkbox" checked aria-label="indeteminate yes" />
48+
<input type="checkbox" aria-label="indeteminate no" />
49+
</div>`,
50+
)
51+
getByLabelText(/indeteminate yes/i).indeterminate = true
52+
getByLabelText(/indeteminate no/i).indeterminate = true
53+
54+
expect(
55+
queryByRole('checkbox', {checked: true, name: /indeteminate yes/i}),
56+
).toBeNull()
57+
expect(
58+
queryByRole('checkbox', {checked: false, name: /indeteminate no/i}),
59+
).toBeNull()
60+
expect(
61+
queryByRole('checkbox', {checked: true, name: /not so much/i}),
62+
).toBeNull()
63+
})
64+
1265
test('`selected: true` matches `aria-selected="true"` on supported roles', () => {
1366
const {getAllByRole} = render(`
1467
<select>

src/queries/role.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {computeAccessibleName} from 'dom-accessibility-api'
22
import {roles as allRoles} from 'aria-query'
33
import {
44
computeAriaSelected,
5+
computeAriaChecked,
56
getImplicitAriaRoles,
67
prettyRoles,
78
isInaccessible,
@@ -29,6 +30,7 @@ function queryAllByRole(
2930
normalizer,
3031
queryFallbacks = false,
3132
selected,
33+
checked,
3234
} = {},
3335
) {
3436
checkContainerType(container)
@@ -42,6 +44,13 @@ function queryAllByRole(
4244
}
4345
}
4446

47+
if (checked !== undefined) {
48+
// guard against unknown roles
49+
if (allRoles.get(role)?.props['aria-checked'] === undefined) {
50+
throw new Error(`"aria-checked" is not supported on role "${role}".`)
51+
}
52+
}
53+
4554
const subtreeIsInaccessibleCache = new WeakMap()
4655
function cachedIsSubtreeInaccessible(element) {
4756
if (!subtreeIsInaccessibleCache.has(element)) {
@@ -82,6 +91,9 @@ function queryAllByRole(
8291
if (selected !== undefined) {
8392
return selected === computeAriaSelected(element)
8493
}
94+
if (checked !== undefined) {
95+
return checked === computeAriaChecked(element)
96+
}
8597
// don't care if aria attributes are unspecified
8698
return true
8799
})

src/role-helpers.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,32 @@ function computeAriaSelected(element) {
185185
if (element.tagName === 'OPTION') {
186186
return element.selected
187187
}
188+
188189
// explicit value
189-
const attributeValue = element.getAttribute('aria-selected')
190+
return checkBooleanAttribute(element, 'aria-selected')
191+
}
192+
193+
/**
194+
* @param {Element} element -
195+
* @returns {boolean | undefined} - false/true if (not)checked, undefined if not checked-able
196+
*/
197+
function computeAriaChecked(element) {
198+
// implicit value from html-aam mappings: https://www.w3.org/TR/html-aam-1.0/#html-attribute-state-and-property-mappings
199+
// https://www.w3.org/TR/html-aam-1.0/#details-id-56
200+
// https://www.w3.org/TR/html-aam-1.0/#details-id-67
201+
if ('indeterminate' in element && element.indeterminate) {
202+
return undefined
203+
}
204+
if ('checked' in element) {
205+
return element.checked
206+
}
207+
208+
// explicit value
209+
return checkBooleanAttribute(element, 'aria-checked')
210+
}
211+
212+
function checkBooleanAttribute(element, attribute) {
213+
const attributeValue = element.getAttribute(attribute)
190214
if (attributeValue === 'true') {
191215
return true
192216
}
@@ -204,4 +228,5 @@ export {
204228
prettyRoles,
205229
isInaccessible,
206230
computeAriaSelected,
231+
computeAriaChecked,
207232
}

types/queries.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ export interface ByRoleOptions extends MatcherOptions {
7878
* selected in the accessibility tree, i.e., `aria-selected="true"`
7979
*/
8080
selected?: boolean
81+
/**
82+
* If true only includes elements in the query set that are marked as
83+
* checked in the accessibility tree, i.e., `aria-checked="true"`
84+
*/
85+
checked?: boolean
8186
/**
8287
* Includes every role used in the `role` attribute
8388
* For example *ByRole('progressbar', {queryFallbacks: true})` will find <div role="meter progressbar">`.

0 commit comments

Comments
 (0)