There is currently no query, that I know of, that can find a radiogroup <fieldset> by its labeled <legend>. This can be problematic, since multiple radio inputs might have the same text but responsible for different parts of a form.
Example:
<fieldset>
<legend>Do you like ice cream?</legend>
<label><input type="radio" name="iceCream" value="true" />Yes</label>
<label><input type="radio" name="iceCream" value="false" />No</label>
</fieldset>
<fieldset>
<legend>Do you like cookies?</legend>
<label><input type="radio" name="cookies" value="true" />Yes</label>
<label><input type="radio" name="cookies" value="false" />No</label>
</fieldset>
Alternatives that I've seen for this problem (i.e. getByLabelText(/yes/i) don't work, because they'll find radio buttons from both of the groups.
You could also getByRole('group') to find the <fieldset>s, but again you can't select which one based on its labeled <legend>.
I actually wonder if this could just work as a part of getByLabelText 馃
Anyways, seemed like a valid use case, since this is the "right" way to build accessible radio button groups 馃槂
Hi @babramczyk,
I think this is a good idea for discussion, though I'm curious what you would expect a getByLegendText to return since it's sort-of labeling the entire fieldset, not the elements within the fieldset.
You could get the fieldset today with:
const cookiesFieldset = getByText(/cookies/i, {selector: 'legend'}).closest('fieldset')
And then you could get the radio buttons with:
const yes = within(cookiesFieldset).getByText(/yes/i)
const no = within(cookiesFieldset).getByText(/no/i)
I think that's reasonable.
I'd love to hear from others.
Your implementation is definitely better than what I had, and seems to get the job done.
The bummer with it is that there's still a gap for a built-in testing-library query for something that is super common, and a recommended usage for accessible markup (using <fieldset> and <legend> in this way).
Now that I think about it, this discussion doesn't even have to be limited to radio buttons and checkboxes. It seems like, going with testing-library's theme of "scanning the page a user would to find elements", there's many more examples where a user would need to rely on a header as a contextual hint for a section of the page.
<h*> to identify a <section><h*> to identify a <form><legend> to identify a group of inputs other than radio buttons and checkboxesMaybe we should be thinking about it that way -- getByHeading/getByHeaderText
const DeveloperDetails = () => (
<section>
<h2>Brett's Details</h2>
<ul>
<li>Last name: Abramczyk</li>
</ul>
</section>
<section>
<h2>Kent's Details</h2>
<ul>
<li>Last name: Dodds</li>
</ul>
</section>
)
const { getByHeading } = render(<DeveloperDetails />)
const brettsDetails = getByHeading(/brett\'s details/i)
expect(brettsDetails).toHaveTextContent(/last name: abramczyk/i)
const kentsDetails = getByHeading(/kent\'s details/i)
expect(kentsDetails).toHaveTextContent(/last name: dodds/i)
getByHeading/getByHeaderText
This sounds very difficult. You're effectively asking for a method that can slice up a tree into chunks separated by certain elements element.
byRole('heading', { name: headerText }).nextSibling sounds easy enough but this heavily depends on the specific DOM tree.
Hi folks 馃憢
I think we've discussed this as much as we need to. If you really want to have those extra queries, you can add them for yourself.
Thanks everyone!
Most helpful comment
Hi @babramczyk,
I think this is a good idea for discussion, though I'm curious what you would expect a
getByLegendTextto return since it's sort-of labeling the entire fieldset, not the elements within the fieldset.You could get the fieldset today with:
And then you could get the radio buttons with:
I think that's reasonable.
I'd love to hear from others.