Skip to content

Commit 3ab989f

Browse files
alexkrolickKent C. Dodds
authored and
Kent C. Dodds
committed
feat(labels): add support for aria-labelledby (testing-library#121)
Fixes testing-library#120 <!-- Thanks for your interest in the project. Bugs filed and PRs submitted are appreciated! Please make sure that you are familiar with and follow the Code of Conduct for this project (found in the CODE_OF_CONDUCT.md file). Also, please make sure you're familiar with and follow the instructions in the contributing guidelines (found in the CONTRIBUTING.md file). If you're new to contributing to open source projects, you might find this free video course helpful: http://kcd.im/pull-request Please fill out the information below to expedite the review and (hopefully) merge of your pull request! --> <!-- What changes are being made? (What feature/bug is being fixed here?) --> **What**: <!-- Why are these changes necessary? --> **Why**: <!-- How were these changes implemented? --> **How**: <!-- Have you done all of these things? --> **Checklist**: <!-- add "N/A" to the end of each line that's irrelevant to your changes --> <!-- to check an item, place an "x" in the box like so: "- [x] Documentation" --> - [ ] Documentation - [ ] Tests - [ ] Ready to be merged <!-- In your opinion, is this ready to be merged as soon as it's reviewed? --> - [ ] Added myself to contributors table <!-- this is optional, see the contributing guidelines for instructions --> <!-- feel free to add additional comments -->
1 parent 7b57f73 commit 3ab989f

File tree

4 files changed

+54
-6
lines changed

4 files changed

+54
-6
lines changed

.size-snapshot.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"dist/dom-testing-library.umd.js": {
3-
"bundled": 115443,
4-
"minified": 50960,
5-
"gzipped": 15294
3+
"bundled": 122519,
4+
"minified": 52052,
5+
"gzipped": 15632
66
}
77
}

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,10 +211,16 @@ const inputNode = getByLabelText(container, 'Username')
211211
// <label for="username-input">Username</label>
212212
// <input id="username-input" />
213213
//
214-
// The aria-labelledby attribute
214+
// The aria-labelledby attribute with form elements
215215
// <label id="username-label">Username</label>
216216
// <input aria-labelledby="username-label" />
217217
//
218+
// The aria-labelledby attribute with other elements
219+
// <section aria-labelledby="section-one-header">
220+
// <h3 id="section-one-header">Section One</h3>
221+
// <p>some content...</p>
222+
// <section>
223+
//
218224
// Wrapper labels
219225
// <label>Username <input /></label>
220226
//

src/__tests__/element-queries.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ test('matches case with RegExp matcher', () => {
8282
expect(queryByText(/Step 1 of 4/)).not.toBeTruthy()
8383
})
8484

85-
test('get can get form controls by label text', () => {
85+
test('can get form controls by label text', () => {
8686
const {getByLabelText} = render(`
8787
<div>
8888
<label>
@@ -116,6 +116,27 @@ test('get can get form controls by label text', () => {
116116
expect(getByLabelText('5th two').id).toBe('fifth-id')
117117
})
118118

119+
test('can get elements labelled with aria-labelledby attribute', () => {
120+
const {getByLabelText, getAllByLabelText} = render(`
121+
<div>
122+
<h1 id="content-header">The Gettysburg Address</h1>
123+
<main id="sibling-of-content-header" aria-labelledby="content-header">
124+
<section aria-labelledby="content-header section-one-header" id="section-one">
125+
<h2 id="section-one-header">Section One</h2>
126+
<p>Four score and seven years ago, ...</p>
127+
</section>
128+
</main>
129+
<p>The Gettysburg Address</p>
130+
</div>
131+
`)
132+
const result = getAllByLabelText('The Gettysburg Address').map(el => el.id)
133+
expect(result).toHaveLength(2)
134+
expect(result).toEqual(
135+
expect.arrayContaining(['sibling-of-content-header', 'section-one']),
136+
)
137+
expect(getByLabelText('Section One').id).toBe('section-one')
138+
})
139+
119140
test('get can get form controls by placeholder', () => {
120141
const {getByPlaceholderText} = render(`
121142
<input id="username-id" placeholder="username" />,

src/queries.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,28 @@ function queryAllByLabelText(
5959
.filter(label => label !== null)
6060
.concat(queryAllByAttribute('aria-label', container, text, {exact}))
6161

62-
return labelledElements
62+
const possibleAriaLabelElements = queryAllByText(container, text, {
63+
exact,
64+
...matchOpts,
65+
}).filter(el => el.tagName !== 'LABEL') // don't reprocess labels
66+
67+
const ariaLabelledElements = possibleAriaLabelElements.reduce(
68+
(allLabelledElements, nextLabelElement) => {
69+
const labelId = nextLabelElement.getAttribute('id')
70+
71+
if (!labelId) return allLabelledElements
72+
73+
// ARIA labels can label multiple elements
74+
const labelledNodes = Array.from(
75+
container.querySelectorAll(`[aria-labelledby~="${labelId}"]`),
76+
)
77+
78+
return allLabelledElements.concat(labelledNodes)
79+
},
80+
[],
81+
)
82+
83+
return Array.from(new Set([...labelledElements, ...ariaLabelledElements]))
6384
}
6485

6586
function queryByLabelText(...args) {

0 commit comments

Comments
 (0)