Skip to content
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

Add query that supports finding sections by their header text #403

Closed
bowernite opened this issue Nov 15, 2019 · 5 comments
Closed

Add query that supports finding sections by their header text #403

bowernite opened this issue Nov 15, 2019 · 5 comments

Comments

@bowernite
Copy link

Describe the feature you'd like:

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 😃

@kentcdodds
Copy link
Member

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.

@bowernite
Copy link
Author

bowernite commented Nov 18, 2019

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.

  • A <h*> to identify a <section>
  • A <h*> to identify a <form>
  • A <legend> to identify a group of inputs other than radio buttons and checkboxes

Maybe 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)

@eps1lon
Copy link
Member

eps1lon commented Nov 18, 2019

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.

@bowernite bowernite changed the title Add query that supports finding radiogroups by legend text Add query that supports finding sections by their header text Nov 19, 2019
@kentcdodds
Copy link
Member

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!

@dwjohnston
Copy link

If anyone is interested, here's how I do this in cypress.

Works well enough:

    cy.findByRole("heading", {name: /the heading/i}).should("exist").parent().within(() => {
      return cy.findByText("the subsequent text").should("exist");
    }); 

It does assume that the 'value' text and the header are all contained within an element. So something like this:

<h2> heading 1 </h2> 
<p>Value 1</p>
<h2>  heading 2 </h2> 
<p>value 2</p>

Could provide false passes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants