Skip to content

feat(byRole): Exclude inaccessible elements #352

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 8 commits into from
Sep 23, 2019

Conversation

eps1lon
Copy link
Member

@eps1lon eps1lon commented Sep 19, 2019

BREAKING
Certain1 elements will no longer be included in byRole queries.
Examples:

  • <ul hidden /> will no longer by included in *byRole('list')
  • <main aria-hidden="true"><ul /><main> will no longer return an element in *byRole('list') or getByRole('main')

What:

Implement https://www.w3.org/TR/core-aam-1.1/#exclude_elements2 with the following deviation:

  • role="none" and role="presentation" is included (not following MUST criteria)
    I feel like this is more of a concern for an actual a11y tree implementation. getByRole('presentation') not returning anything even if the element is visible is probably confusing but I could see us throwing an error here. We could argue that one should use getByTestId instead since this makes it obvious you're not querying the a11y tree.
  • descendants of "Children Presentational: True" are included (not following SHOULD criteria)

Why:

Closes #350

How:

Implementation is kept simple and mirrors the spec. Faster implementation is considered for a future PR.

Checklist:

  • Documentation added to the
    docs site Will do once this is approved
  • [ ] Typescript definitions updated Separate type PR is prepared
  • Tests
  • Ready to be merged

I'm open to removing the option and making it default. In any case it should be released in a breaking change since it is potentially breaking.

1 excluded are elements that

  • have aria-hidden=true or any of its ancestors
  • have the hidden attribute or any of its ancestors
  • have display: none or any of its ancestors
  • have hidden as their computed visibility

@eps1lon eps1lon added enhancement New feature or request BREAKING CHANGE This change will require a major version bump labels Sep 19, 2019
Copy link
Member

@kentcdodds kentcdodds left a comment

Choose a reason for hiding this comment

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

Looking good. A few things. Also it looks like we're missing some tests and we should add this to all of the queries.

expect(() => getByRole('list')).toThrowErrorMatchingInlineSnapshot(`
"Unable to find an accessible element with the role "list"

There are no accessible roles.
Copy link
Member

Choose a reason for hiding this comment

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

We should probably add something to this message that says: "But there are some inaccessible roles. If you wish to access them, then set the hidden option to true. Learn more about this here: linktodocs.com"

Copy link
Member Author

Choose a reason for hiding this comment

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

This would require a lot of refactoring if we want to actually determine if there are hidden roles.

Would it be sufficient to say "There might be inaccessible roles" instead?

Copy link
Member Author

@eps1lon eps1lon Sep 21, 2019

Choose a reason for hiding this comment

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

Extended the message and added a link to the docs but didn't implement displaying accessible vs. inaccessible roles. I think this isn't required to get this merged but I would like to see this at some point as well.


${roles.replace(/\n/g, '\n ').replace(/\n\s\s\n/g, '\n\n')}
`.trim()
}

return `
Unable to find an element with the role "${role}"
Unable to find an ${
hidden === false ? 'accessible ' : ''
Copy link
Member

Choose a reason for hiding this comment

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

Remove the extra space.

Suggested change
hidden === false ? 'accessible ' : ''
hidden === false ? 'accessible' : ''

Copy link
Member Author

Choose a reason for hiding this comment

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

I think I need to keep this and remove the unconditional space later on. Otherwise we get

Unable to find an  element with the role "${role}
                 ^^ double space

for hidden: false

Copy link
Member Author

Choose a reason for hiding this comment

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

Extra space is still there (since I'm pretty sure it's required) but I fixed the double space issue.

@eps1lon eps1lon force-pushed the feat/byRole/accessible branch from 950c779 to 3f5debb Compare September 21, 2019 15:42
@eps1lon eps1lon force-pushed the feat/byRole/accessible branch from b3fe775 to 7cdfcfa Compare September 23, 2019 13:49
Copy link
Member

@kentcdodds kentcdodds left a comment

Choose a reason for hiding this comment

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

I'm good with this. Just one thing.


let currentElement = element
while (currentElement !== null) {
if (currentElement.hasAttribute('hidden')) {
Copy link
Member

Choose a reason for hiding this comment

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

Is it permissible to have hidden="false"?

Copy link
Member Author

Choose a reason for hiding this comment

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

This would also be considered hidden. I should use the property here since it does not require knowledge of attributes vs. properties. You can also check this in your user agent's default stylesheet which should have

[hidden] {
  display: none;
}

This is only a recommendation though so we rely on the hidden attribute definition:

The hidden attribute is a boolean attribute.

-- https://html.spec.whatwg.org/multipage/interaction.html#dom-hidden

The presence of a boolean attribute on an element represents the true value, and the absence of the attribute represents the false value.

-- https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attribute

Copy link
Member Author

Choose a reason for hiding this comment

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

Would be interesting to see what react does with boolean attributes. It would be nice if it would add a warning since hidden="false" does not work as one might expect.

And it does:

Warning: Received the string false for the boolean attribute hidden. The browser will interpret it as a truthy value. Did you mean hidden={false}?

@kentcdodds kentcdodds merged commit dbbea6e into testing-library:master Sep 23, 2019
@kentcdodds
Copy link
Member

I'm happy with this. Thank you!

@kentcdodds
Copy link
Member

🎉 This PR is included in version 6.4.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@eps1lon eps1lon deleted the feat/byRole/accessible branch September 23, 2019 18:54
@eps1lon
Copy link
Member Author

eps1lon commented Sep 23, 2019

Unfamiliar with semantic-release: Should I have used another message format or was this intended to be a feature release?

@kentcdodds
Copy link
Member

I set the commit message when I use "Rebase & Merge." I intended this to be a feature release because I consider it highly unlikely to break anyone's existing tests. I'll take the brunt of any anger or frustration if I'm wrong.

@jsphstls
Copy link

I do think a breaking change should have been a major version. I somehow picked this up with a new lockfile.

My breaking case is that I have a table with a scrolling body which essentially created two headers, one that is visible and one that is sr-only. The strategy is arguable. EIther way, I was used to finding both sets of columnheaders in the tests but now only one is found so the tests broke with seemingly unrelated changes. This change is ultimately good but the rollout strategy cost me some time.

@eps1lon
Copy link
Member Author

eps1lon commented Oct 14, 2019

sr-only

If this means "screen-reader only" then this is either a bug on our side or it detected an actual issue inside your markup. Could you share your markup + style that you would consider screen-reader only? This function should implement specification from the W3C which is either already implemented in assistive technology or will be.

@jsphstls
Copy link

Apologies, I meant "one that is visible and aria-hidden and one that is sr-only". The changes in this PR appear to be working fine.

My point is that I did have tests that broke because of this PR.

@eps1lon
Copy link
Member Author

eps1lon commented Oct 14, 2019

My point is that I did have tests that broke because of this PR.

Well as kent said

I'll take the brunt of any anger or frustration if I'm wrong.

😄

@kentcdodds
Copy link
Member

Sorry! 😬

forgive me

@eps1lon eps1lon mentioned this pull request Oct 28, 2019
@shaunak
Copy link

shaunak commented Feb 7, 2023

Is there a reason this doesn't exist for *byText queries?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
BREAKING CHANGE This change will require a major version bump enhancement New feature or request released
Projects
None yet
Development

Successfully merging this pull request may close these issues.

add accessible? option to byRole query
4 participants