-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Updating Button
handling of aria-pressed
#54665
Updating Button
handling of aria-pressed
#54665
Conversation
| 'aria-selected' | ||
| undefined; | ||
|
||
switch ( role ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the Button
component can be given an href
, should there also be a case for role="link"
with aria-current
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potentially, but aria-current
can actually be used for (almost) any item "within a container or set of related elements", so special-casing it here might not make sense. Happy to discuss though.
|
||
it( 'should use given aria-selected value if provided', () => { | ||
render( | ||
<Button isPressed role="option" aria-selected={ false } /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there scenarios where isPressed
is true
with aria-selected
and aria-checked
set to false
for roles like option
and checkbox
? I'm curious!
This also made me wonder if we could check the value of aria-selected
, aria-checked
, etc. via userEvent
to test closer to how it'll be used in the DOM. 🤔
I've been thinking about this a bit since I put the PR up, and I'm wondering if overloading type booleanish = boolean | 'true' | 'false';
type ButtonProps = {
...
} & (
| {
role?: 'button';
isChecked?: never;
isPressed?: booleanish | 'mixed';
isSelected?: never;
}
| {
role: 'checkbox' | 'menuitemcheckbox';
isChecked?: booleanish | 'mixed';
isPressed?: never;
isSelected?: never;
}
| {
role: 'menuitemradio' | 'radio' | 'switch';
isChecked?: booleanish;
isPressed?: never;
isSelected?: never;
}
| {
role: 'gridcell' | 'row' | 'tab';
isChecked?: never;
isPressed?: never;
isSelected?: booleanish;
}
| {
role: 'option';
isChecked?: booleanish;
isPressed?: never;
isSelected?: booleanish;
}
); ✅ <Button isPressed />
⛔️ <Button isSelected />
✅ <Button role="option" isSelected />
⛔️ <Button role="tab" isPressed />
✅ <Button role="checkbox" isChecked />
⛔️ <Button role="radio" isChecked="mixed" /> Reinforcing more appropriate semantics does lose a little bit of flexibility, but how often does a |
The approach highlighted above would definitely enforce better semantics, but I'm not sure that adding even more props to a component that has already many props would be the right approach. That would also imply that, as the HTML spec gets updated, we would need to keep the component up to date. Another approach that came to mind, instead, could be to deprecate the Even if out of scope for this PR specifically, we could then use TypeScript to indicate which
|
I'd be fine with deprecating
I don't know if anything uses the
How do you see that working?
I'm not aware of any high-level library that does this specifically, but it might exist somewhere. Would need a bit of further research. TypeScript exposes interfaces for the HTML DOM API, and the various ARIA attributes, but I don't think either provide sufficient information for us to piggy back. There's always the JSX A11y ESLint plugin as an option, but that feels somewhat counter to using TypeScript! Maybe we need to port that to TypeScript as a separate package! |
After a quick chat, the agreed next steps are:
|
Closing in favour of #54740. |
What?
This PR gives the
Button
component greater flexibility in howisPressed
is handled, by looking for arole
and setting the appropriate ARIA state.Why?
When the
isPressed
prop is used onButton
,aria-pressed
is set accordingly. But whenButton
is used with a role that is notbutton
(the default),aria-pressed
is not the appropriate attribute to set.How?
By checking for a
role
property, we can pick the appropriate attribute to set on the renderedbutton
, rather than always settingaria-pressed
. This also allows consumers to override the attribute value if they need to useisPressed
and the corresponding attribute differently.Testing Instructions
Unit tests have been included to cover the relevant roles.