Skip to content

Commit

Permalink
feat(ByLabelText): change selector to match target (not label) (#373)
Browse files Browse the repository at this point in the history
Closes #372

BREAKING CHANGE: If you used the `selector` option in `ByLabelText` queries, then you will probably need to update that code to be able to find the label you're looking for.
  • Loading branch information
airjp73 authored and kentcdodds committed Mar 4, 2020
1 parent 6084f53 commit e879745
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 12 deletions.
69 changes: 68 additions & 1 deletion src/__tests__/element-queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ test('can get elements labelled with aria-labelledby attribute', () => {
expect(getByLabelText('Section One').id).toBe('section-one')
})

test('can get sibling elements with aria-labelledby attrib ute', () => {
test('can get sibling elements with aria-labelledby attribute', () => {
const {getAllByLabelText} = render(`
<div>
<svg id="icon" aria-labelledby="icon-desc"></svg>
Expand All @@ -212,6 +212,73 @@ test('can get sibling elements with aria-labelledby attrib ute', () => {
expect(result[0].id).toBe('icon')
})

test('can filter results of label query based on selector', () => {
const {getAllByLabelText} = render(`
<div>
<label id="label1" for="input1">
Test Label
<input id="input2" />
</label>
<input id="input1" class="fancy-input" />
<span aria-labelledby="label1">Some hint text</span>
</div>
`)

const result = getAllByLabelText('Test Label', {selector: '.fancy-input'})
expect(result).toHaveLength(1)
expect(result[0].id).toBe('input1')
})

test('can find any form control when label text is inside other elements', () => {
const {getAllByLabelText} = render(`
<label>
<span>Test</span>
<span>Label</span>
<button />
<input />
<meter />
<output />
<progress />
<select />
<textarea />
</label>
`)

const result = getAllByLabelText('Test Label')
expect(result).toHaveLength(7)
})

test('can find non-input elements when aria-labelledby a label', () => {
const {getAllByLabelText} = render(`
<div>
<label id="label1">Test Label</label>
<ul aria-labelledby="label1">
<li>Hello</li>
</ul
</div>
`)

const result = getAllByLabelText('Test Label')
expect(result).toHaveLength(1)
expect(result[0].nodeName).toBe('UL')
})

test('can find the correct element when there are multiple matching labels', () => {
const {getByLabelText} = render(`
<label>
Test Label
<input />
</label>
<label>
Test Label
<textarea></textarea>
</label>
`)

const result = getByLabelText('Test Label', {selector: 'input'})
expect(result.nodeName).toBe('INPUT')
})

test('get can get form controls by placeholder', () => {
const {getByPlaceholderText} = render(`
<input id="username-id" placeholder="username" />,
Expand Down
31 changes: 20 additions & 11 deletions src/queries/label-text.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@ function queryAllByLabelText(
normalizer: matchNormalizer,
})
const labelledElements = labels
.map(label => {
.reduce((matchedElements, label) => {
const elementsForLabel = []
if (label.control) {
return label.control
elementsForLabel.push(label.control)
}
/* istanbul ignore if */
if (label.getAttribute('for')) {
Expand All @@ -57,21 +58,27 @@ function queryAllByLabelText(
// <label for="someId">text</label><input id="someId" />

// .control support has landed in jsdom (https://github.com/jsdom/jsdom/issues/2175)
return container.querySelector(`[id="${label.getAttribute('for')}"]`)
elementsForLabel.push(
container.querySelector(`[id="${label.getAttribute('for')}"]`),
)
}
if (label.getAttribute('id')) {
// <label id="someId">text</label><input aria-labelledby="someId" />
return container.querySelector(
`[aria-labelledby~="${label.getAttribute('id')}"]`,
)
container
.querySelectorAll(`[aria-labelledby~="${label.getAttribute('id')}"]`)
.forEach(element => elementsForLabel.push(element))
}
if (label.childNodes.length) {
// <label>text: <input /></label>
return label.querySelector(selector)
const formControlSelector =
'button, input, meter, output, progress, select, textarea'
label
.querySelectorAll(formControlSelector)
.forEach(element => elementsForLabel.push(element))
}
return null
})
.filter(label => label !== null)
return matchedElements.concat(elementsForLabel)
}, [])
.filter(element => element !== null)
.concat(queryAllByAttribute('aria-label', container, text, {exact}))

const possibleAriaLabelElements = queryAllByText(container, text, {
Expand All @@ -95,7 +102,9 @@ function queryAllByLabelText(
[],
)

return Array.from(new Set([...labelledElements, ...ariaLabelledElements]))
return Array.from(
new Set([...labelledElements, ...ariaLabelledElements]),
).filter(element => element.matches(selector))
}

// the getAll* query would normally look like this:
Expand Down

0 comments on commit e879745

Please sign in to comment.