Skip to content

fix(getByLabelText): Support aria-labelledby attr containing multiple ids #59

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,15 @@
"contributions": [
"code"
]
},
{
"login": "rubencosta",
"name": "Ruben Costa",
"avatar_url": "https://avatars3.githubusercontent.com/u/577921?v=4",
"profile": "http://nuances.co",
"contributions": [
"code"
]
}
]
}
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
[![downloads][downloads-badge]][npmtrends]
[![MIT License][license-badge]][license]

[![All Contributors](https://img.shields.io/badge/all_contributors-26-orange.svg?style=flat-square)](#contributors)
[![All Contributors](https://img.shields.io/badge/all_contributors-27-orange.svg?style=flat-square)](#contributors)
[![PRs Welcome][prs-badge]][prs]
[![Code of Conduct][coc-badge]][coc]

Expand Down Expand Up @@ -873,7 +873,8 @@ Thanks goes to these people ([emoji key][emojis]):
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| [<img src="https://avatars1.githubusercontent.com/u/1241511?s=460&v=4" width="100px;"/><br /><sub><b>Anto Aravinth</b></sub>](https://github.com/antoaravinth)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=antoaravinth "Code") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=antoaravinth "Tests") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=antoaravinth "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/3462296?v=4" width="100px;"/><br /><sub><b>Jonah Moses</b></sub>](https://github.com/JonahMoses)<br />[📖](https://github.com/kentcdodds/dom-testing-library/commits?author=JonahMoses "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/4002543?v=4" width="100px;"/><br /><sub><b>Łukasz Gandecki</b></sub>](http://team.thebrain.pro)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=lgandecki "Code") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=lgandecki "Tests") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=lgandecki "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/498274?v=4" width="100px;"/><br /><sub><b>Ivan Babak</b></sub>](https://sompylasar.github.io)<br />[🐛](https://github.com/kentcdodds/dom-testing-library/issues?q=author%3Asompylasar "Bug reports") [🤔](#ideas-sompylasar "Ideas, Planning, & Feedback") [💻](https://github.com/kentcdodds/dom-testing-library/commits?author=sompylasar "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=sompylasar "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/4439618?v=4" width="100px;"/><br /><sub><b>Jesse Day</b></sub>](https://github.com/jday3)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=jday3 "Code") | [<img src="https://avatars0.githubusercontent.com/u/15199?v=4" width="100px;"/><br /><sub><b>Ernesto García</b></sub>](http://gnapse.github.io)<br />[💬](#question-gnapse "Answering Questions") [💻](https://github.com/kentcdodds/dom-testing-library/commits?author=gnapse "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=gnapse "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/2747424?v=4" width="100px;"/><br /><sub><b>Josef Maxx Blake</b></sub>](http://jomaxx.com)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=jomaxx "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=jomaxx "Documentation") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=jomaxx "Tests") |
| [<img src="https://avatars3.githubusercontent.com/u/725236?v=4" width="100px;"/><br /><sub><b>Alex Cook</b></sub>](https://github.com/alecook)<br />[📖](https://github.com/kentcdodds/dom-testing-library/commits?author=alecook "Documentation") [💡](#example-alecook "Examples") | [<img src="https://avatars3.githubusercontent.com/u/10348212?v=4" width="100px;"/><br /><sub><b>Daniel Cook</b></sub>](https://github.com/dfcook)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=dfcook "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=dfcook "Documentation") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=dfcook "Tests") | [<img src="https://avatars2.githubusercontent.com/u/21194045?s=400&v=4" width="100px;"/><br /><sub><b>Thomas Chia</b></sub>](https://github.com/thchia)<br />[🐛](https://github.com/kentcdodds/dom-testing-library/issues?q=author%3Athchia "Bug reports") [💻](https://github.com/kentcdodds/dom-testing-library/commits?author=thchia "Code") | [<img src="https://avatars1.githubusercontent.com/u/28659384?v=4" width="100px;"/><br /><sub><b>Tim Deschryver</b></sub>](https://github.com/tdeschryver)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=tdeschryver "Code") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=tdeschryver "Tests") | [<img src="https://avatars3.githubusercontent.com/u/1571667?v=4" width="100px;"/><br /><sub><b>Alex Krolick</b></sub>](https://alexkrolick.com)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=alexkrolick "Code") | [<img src="https://avatars2.githubusercontent.com/u/2224291?v=4" width="100px;"/><br /><sub><b>Maddi Joyce</b></sub>](http://www.maddijoyce.com)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=maddijoyce "Code") | [<img src="https://avatars1.githubusercontent.com/u/25429764?v=4" width="100px;"/><br /><sub><b>Peter Kamps</b></sub>](https://github.com/npeterkamps)<br />[🐛](https://github.com/kentcdodds/dom-testing-library/issues?q=author%3Anpeterkamps "Bug reports") [💻](https://github.com/kentcdodds/dom-testing-library/commits?author=npeterkamps "Code") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=npeterkamps "Tests") |
| [<img src="https://avatars2.githubusercontent.com/u/21689428?v=4" width="100px;"/><br /><sub><b>Jonathan Stoye</b></sub>](http://jonathanstoye.de)<br />[📖](https://github.com/kentcdodds/dom-testing-library/commits?author=JonathanStoye "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/4126644?v=4" width="100px;"/><br /><sub><b>Sanghyeon Lee</b></sub>](https://github.com/yongdamsh)<br />[💡](#example-yongdamsh "Examples") | [<img src="https://avatars3.githubusercontent.com/u/8015514?v=4" width="100px;"/><br /><sub><b>Justice Mba </b></sub>](https://github.com/Dajust)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=Dajust "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=Dajust "Documentation") [🤔](#ideas-Dajust "Ideas, Planning, & Feedback") | [<img src="https://avatars3.githubusercontent.com/u/340761?v=4" width="100px;"/><br /><sub><b>Wayne Crouch</b></sub>](https://github.com/wgcrouch)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=wgcrouch "Code") | [<img src="https://avatars1.githubusercontent.com/u/4996462?v=4" width="100px;"/><br /><sub><b>Ben Elliott</b></sub>](http://benjaminelliott.co.uk)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=benelliott "Code") |
| [<img src="https://avatars2.githubusercontent.com/u/21689428?v=4" width="100px;"/><br /><sub><b>Jonathan Stoye</b></sub>](http://jonathanstoye.de)<br />[📖](https://github.com/kentcdodds/dom-testing-library/commits?author=JonathanStoye "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/4126644?v=4" width="100px;"/><br /><sub><b>Sanghyeon Lee</b></sub>](https://github.com/yongdamsh)<br />[💡](#example-yongdamsh "Examples") | [<img src="https://avatars3.githubusercontent.com/u/8015514?v=4" width="100px;"/><br /><sub><b>Justice Mba </b></sub>](https://github.com/Dajust)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=Dajust "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=Dajust "Documentation") [🤔](#ideas-Dajust "Ideas, Planning, & Feedback") | [<img src="https://avatars3.githubusercontent.com/u/340761?v=4" width="100px;"/><br /><sub><b>Wayne Crouch</b></sub>](https://github.com/wgcrouch)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=wgcrouch "Code") | [<img src="https://avatars1.githubusercontent.com/u/4996462?v=4" width="100px;"/><br /><sub><b>Ben Elliott</b></sub>](http://benjaminelliott.co.uk)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=benelliott "Code") | [<img src="https://avatars3.githubusercontent.com/u/577921?v=4" width="100px;"/><br /><sub><b>Ruben Costa</b></sub>](http://nuances.co)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=rubencosta "Code") |

<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors][all-contributors] specification.
Expand Down
18 changes: 13 additions & 5 deletions src/__tests__/element-queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import 'jest-dom/extend-expect'
import {render} from './helpers/test-utils'

beforeEach(() => {
window.Cypress = null;
});
window.Cypress = null
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Off-topic but why null, not undefined? @kentcdodds

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Someone else did that I think. I probably would have set it to undefined, but I didn't care enough to ask them to change it.

})

test('query can return null', () => {
const {
Expand Down Expand Up @@ -82,12 +82,20 @@ test('get can get form controls by label text', () => {
<label for="fourth.id">4th</label>
<input id="fourth.id" />
</div>
<div>
<div>
<label id="fifth-label-one">5th one</label>
<label id="fifth-label-two">5th two</label>
<input aria-labelledby="fifth-label-one fifth-label-two" id="fifth-id" />
</div>
</div>
`)
expect(getByLabelText('1st').id).toBe('first-id')
expect(getByLabelText('2nd').id).toBe('second-id')
expect(getByLabelText('3rd').id).toBe('third-id')
expect(getByLabelText('4th').id).toBe('fourth.id')
expect(getByLabelText('5th one').id).toBe('fifth-id')
expect(getByLabelText('5th two').id).toBe('fifth-id')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's 1-to-many relation between label and element, getByLabelText should either return an array of matching elements, or return the element with "most relevant" label (e.g. matching label's id should be closer to the end of an element's labelledby list, though there still can be more than one of those), or something else.

Try with Mozilla's example in the test:

<div id="billing">Billing</div>

<div>
    <div id="name">Name</div>
    <input type="text" aria-labelledby="billing name"/>
</div>
<div>
    <div id="address">Address</div>
    <input type="text" aria-labelledby="billing address"/>
</div>

https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-labelledby_attribute

Which element do you want to find with getByLabelText('Billing')? I'd expect two.

What about getByLabelText('Billing Name')? I'd expect to find one, but it won't.

If you want to really support this multi-label use case, it's more complex than changing the selector.

Copy link
Author

@rubencosta rubencosta Jun 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @sompylasar and thanks for the super fast feedback :)

From what I understand if there is support aria-labelledby then having 1-to-n elements to labels should also be supported and I think that it already is with getAllByLabelText since it will return an array of matching elements.
For the getByLabelText I understand your concern about which element to return when there are multiple valid matches but IMO trying to come up with a decision for which is the "most relevant" element is not necessary since AFAIK this is not part of the spec and the default behavior of querySelector is also to return the first match. That would actually be my expectation.

Which element do you want to find with getByLabelText('Billing')? I'd expect two.

Why would you expect two? The default behavior of getBy*() is to return the first match correct? If something else is necessary you can pass a function. So let's say that I actually want the billing address:

getByLabelText(
        (content, element) =>
          content === 'Billing' && element.getAttribute('aria-labelledby').includes('address'),
      )

What about getByLabelText('Billing Name')? I'd expect to find one, but it won't.

I don't understand this one since getLabelText takes the text of one label and you are passing the text of two different labels. Maybe would be nice to be able to do this?

To me it does not seem so complex but I am a new user of this library and might not understand it's goals. Anyway I am happy to add the tests for the examples in https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-labelledby_attribute and see that all are supported but I don't know if this makes sense. For my use case I basically would like getByLabelText to find elements that use aria-labelledby with A space-separated list of element IDs which is a valid value.

Update

After testing out my proposed solution for finding a specific element when there are multiple associated with a label does not work with the current implementation because the element that gets passed to the matcher funcion is actually the label element and not the labelled element. IMO this is not intuitive comparing with the getByText(fn) that gets called with the element that you want to select.

Another problem that I found while using the MDN examples is that getByLabelText actually only looks for label elements but according to MDN page:

This attribute can be used with any typical HTML form element; it is not limited to elements that have an ARIA role assigned.

IMO a possible solution would be:

  1. Changing the behavior of queryAllByLabelText so that it always returns an array of elements associated with the label that can be correctly filtered by the user if the user provides a matcher function.
  2. For this, It should call the matcher function with the textContent (of the labelElement), the labelElement and the labelledElement so that it would be possible to match a specific label element and also to match a specific labelled element.
  3. Unrestricting the selector so that it stops being label specific (at least for the aria-labelledby case).

@sompylasar do you think this could work? Are there other concerns that I missed?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would you expect two? The default behavior of getBy*() is to return the first match correct?

Yes, agree, getBy for first-in-DOM, getAllBy for all.

Thanks for the long reply and the Update! I'm glad that you found some API inconsistencies.

What about getByLabelText('Billing Name')? I'd expect to find one, but it won't.

I don't understand this one since getLabelText takes the text of one label and you are passing the text of two different labels. Maybe would be nice to be able to do this?

To me it does not seem so complex but I am a new user of this library and might not understand it's goals.

The goal of this library, at least of this part with the queries (let @kentcdodds correct me if I'm wrong) is to provide DOM query functions to look for elements based on what the user can read (with eyes or a screenreader).

If the element says it's labelledby two labels, and the Mozilla example shows that, it means that one label is not enough to understand the purpose of the labelledby element (the input): it's not enough to look for "Name", it may be ambiguous, as there can be another section before "Billing" like "Personal" with the same "Name" label.

  1. Changing the behavior of queryAllByLabelText so that it always returns an array of elements associated with the label that can be correctly filtered by the user if the user provides a matcher function.

This sounds like how this function should work, I wonder why it doesn't and how it works now.

  1. For this, It should call the matcher function with the textContent (of the labelElement), the labelElement and the labelledElement so that it would be possible to match a specific label element and also to match a specific labelled element.

I agree with providing more context to the matcher functions for the caller to make the decisions.

  1. Unrestricting the selector so that it stops being label specific (at least for the aria-labelledby case).

Yes, this should only apply to aria-labelledby case, it should still work the same for plain label.

RE: which direction the library should go, I degelate to @kentcdodds

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I understand if there is support aria-labelledby then having 1-to-n elements to labels should also be supported and I think that it already is with getAllByLabelText since it will return an array of matching elements.

This is correct. I don't think changes are necessary. If getAllByLabelText is not working as expected, let's file that as a separate bug.

})

test('get can get form controls by placeholder', () => {
Expand Down Expand Up @@ -174,15 +182,15 @@ test('getAll* matchers return an array', () => {
<div>
<img
data-testid="poster"
alt="finding nemo poster"
alt="finding nemo poster"
src="/finding-nemo.png" />
<img
data-testid="poster"
alt="finding dory poster"
alt="finding dory poster"
src="/finding-dory.png" />
<img
data-testid="poster"
alt="jumanji poster"
alt="jumanji poster"
src="/jumanji.png" />
<p>Where to next?</p>
<label for="username">User Name</label>
Expand Down
48 changes: 36 additions & 12 deletions src/queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import {getNodeText} from './get-node-text'
import {prettyDOM} from './pretty-dom'

function debugDOM(htmlElement) {
const limit = process.env.DEBUG_PRINT_LIMIT || 7000
const inNode = (typeof module !== 'undefined' && module.exports)
const inCypress = (typeof window !== 'undefined' && window.Cypress)
const limit = process.env.DEBUG_PRINT_LIMIT || 7000
const inNode = typeof module !== 'undefined' && module.exports
const inCypress = typeof window !== 'undefined' && window.Cypress
/* istanbul ignore else */
if (inCypress) {
return ''
Expand Down Expand Up @@ -66,7 +66,7 @@ function queryAllByLabelText(
if (label.getAttribute('id')) {
// <label id="someId">text</label><input aria-labelledby="someId" />
return container.querySelector(
`[aria-labelledby="${label.getAttribute('id')}"]`,
`[aria-labelledby~="${label.getAttribute('id')}"]`,
)
}
if (label.childNodes.length) {
Expand Down Expand Up @@ -155,7 +155,10 @@ function queryByAltText(...args) {
function getAllByTestId(container, id, ...rest) {
const els = queryAllByTestId(container, id, ...rest)
if (!els.length) {
throw getElementError(`Unable to find an element by: [data-testid="${id}"]`, container)
throw getElementError(
`Unable to find an element by: [data-testid="${id}"]`,
container,
)
}
return els
}
Expand All @@ -167,7 +170,10 @@ function getByTestId(...args) {
function getAllByTitle(container, title, ...rest) {
const els = queryAllByTitle(container, title, ...rest)
if (!els.length) {
throw getElementError(`Unable to find an element with the title: ${title}.`, container)
throw getElementError(
`Unable to find an element with the title: ${title}.`,
container,
)
}
return els
}
Expand All @@ -179,7 +185,10 @@ function getByTitle(...args) {
function getAllByValue(container, value, ...rest) {
const els = queryAllByValue(container, value, ...rest)
if (!els.length) {
throw getElementError(`Unable to find an element with the value: ${value}.`, container)
throw getElementError(
`Unable to find an element with the value: ${value}.`,
container,
)
}
return els
}
Expand All @@ -191,7 +200,10 @@ function getByValue(...args) {
function getAllByPlaceholderText(container, text, ...rest) {
const els = queryAllByPlaceholderText(container, text, ...rest)
if (!els.length) {
throw getElementError(`Unable to find an element with the placeholder text of: ${text}`, container)
throw getElementError(
`Unable to find an element with the placeholder text of: ${text}`,
container,
)
}
return els
}
Expand All @@ -205,9 +217,15 @@ function getAllByLabelText(container, text, ...rest) {
if (!els.length) {
const labels = queryAllLabelsByText(container, text, ...rest)
if (labels.length) {
throw getElementError(`Found a label with the text of: ${text}, however no form control was found associated to that label. Make sure you're using the "for" attribute or "aria-labelledby" attribute correctly.`, container)
throw getElementError(
`Found a label with the text of: ${text}, however no form control was found associated to that label. Make sure you're using the "for" attribute or "aria-labelledby" attribute correctly.`,
container,
)
} else {
throw getElementError(`Unable to find a label with the text of: ${text}`, container)
throw getElementError(
`Unable to find a label with the text of: ${text}`,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other messages it's "with the text:" but here it's "with the text of:" — inconsistent.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sompylasar should I act on this?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, I just noticed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't bother. We can fix that in another PR if we want.

container,
)
}
}
return els
Expand All @@ -220,7 +238,10 @@ function getByLabelText(...args) {
function getAllByText(container, text, ...rest) {
const els = queryAllByText(container, text, ...rest)
if (!els.length) {
throw getElementError(`Unable to find an element with the text: ${text}. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.`, container)
throw getElementError(
`Unable to find an element with the text: ${text}. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.`,
container,
)
}
return els
}
Expand All @@ -232,7 +253,10 @@ function getByText(...args) {
function getAllByAltText(container, alt, ...rest) {
const els = queryAllByAltText(container, alt, ...rest)
if (!els.length) {
throw getElementError(`Unable to find an element with the alt text: ${alt}`, container)
throw getElementError(
`Unable to find an element with the alt text: ${alt}`,
container,
)
}
return els
}
Expand Down