Skip to content

Issue with *ByLabelText functions when using aria-labelledby with multiple IDs referencing aria-label #1352

@romain-trotard

Description

@romain-trotard

Hello all :)

Thanks for the library 🔥

  • @testing-library/dom version: v10.4.0

Relevant code or config:

I made this branch with a test https://github.com/romain-trotard/dom-testing-library/tree/labelledby-multiple-repro and some comments on the code I think we should change.

  const {getByLabelText} = render(`
      <div id="frameworks-row" role="row" aria-label="Frameworks">
        <button aria-labelledby="select frameworks-row" id="select" aria-label="Select" type="button">Select</button>
        <span>Frameworks</span>
        <span>Other information</span>
      </div>
  `)

  // Do not find `Select Frameworks` because it takes element.textContent 
  // and not `aria-label` for the element with id `frameworks-row`
  expect(getByLabelText('Select Frameworks').id).toBe('select')
  expect(getByLabelText('Frameworks').id).toBe('frameworks-row')

What you did:

There are some use cases where we want to labelled an element with aria-labelledby that references an element that has aria-label.
For example, in the template above the button element has a aria-labelledby that references himself (that has aria-label) and the div with id frameworks-row (that has aria-label)

What happened:

The selector getByLabelText('Select Frameworks') does not work because when getting the element with id: frameworks-row, it's not the aria-label that is retrieved but the textContent (here Select Frameworks Other information)

Reproduction:

https://github.com/romain-trotard/dom-testing-library/tree/labelledby-multiple-repro

npm run test

Problem description:

The selector getByLabelText('Select Frameworks') does not work because when getting the element with id: frameworks-row, it's not the aria-label that is retrieved but the textContent (here Select Frameworks Other information)

Suggested solution:

Here

textContent = (element as HTMLInputElement).value || element.textContent

We should get the aria-label attribute before getting textContent, like this:

textContent = (element as HTMLInputElement).value || element.getAttribute('aria-label') || element.textContent

aria-label pb

But we also need to find a way to concat these labels (when working with aria-labelledby) here:

const labelList = getLabels(container, labelledElement, {selector})
labelList
.filter(label => Boolean(label.formControl))
.forEach(label => {
if (
matcher(label.content, label.formControl, text, matchNormalizer) &&
label.formControl
) {
labelledElements.push(label.formControl)
}
})
const labelsValue = labelList
.filter(label => Boolean(label.content))
.map(label => label.content)
if (
matcher(labelsValue.join(' '), labelledElement, text, matchNormalizer)
) {
labelledElements.push(labelledElement)
}
if (labelsValue.length > 1) {

Note: For the moment, I haven't given the solution much thought.

If the bug is acknowledged, I would be happy to make a fix ;)

Thanks :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions