-
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
ToggleGroupControl: support disabled
options
#63450
Conversation
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.
To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
<ToggleGroupControlOption value="pizza" label="Pizza" /> | ||
<ToggleGroupControlOption value="rice" label="Rice" disabled /> | ||
<ToggleGroupControlOption value="pasta" label="Pasta" /> |
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.
@ramonjd just making sure you're ok with those food options
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.
Sorry, I was away last week.
I would have preferred risotto instead of just "rice", but I appreciate you asking. 😆
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.
Ah, good point. Feel free to open a PR
// due to how the component works, only the `disabled` prop should be used | ||
| 'aria-disabled' |
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.
I don't think it makes sense to have the options accessibleWhenDisabled
given how the component works cc @mirka
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.
I actually thought this was one of the cases where we always want the option to be focusable when disabled, similar to ToolbarButton (#63102) or tabs in a tablist. It's also a composite widget where there will be a limited number of options, and the number of tab stops it potentially adds to the tab sequence is either zero or negligible.
Implementing that in the radio case for ToggleGroupControl would require some additional design work though 🤔 But at least Ariakit supports the behavior nicely (StackBlitz demo).
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.
Implementing that in the radio case for ToggleGroupControl would require some additional design work though
Yep. Currently, since disabled
options are not really supported, the backdrop indicator also works as a focus indicator.
If we make the options accessibleWhenDisabled
, then we'll need to add separate focus rings.
But at least Ariakit supports the behavior nicely (StackBlitz demo).
Yup. We'll probably need to add the custom logic when rendering individual button
s
Just going off the screen recording, it’s easy for me to see that an option is disabled 👍. Still, I wonder if the disabled opacity makes the text too light for a11y guidelines. Unless that’s not required of a disabled state but isn’t it? It’s a thread-the-needle thing because even the enabled text is not that high contrast so lesser opacity will make it harder to see that an option is disabled. If the contrast is an issue perhaps we can come up with something else but |
@stokesman FWIW, inactive controls are exempt from the contrast guidelines. |
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.
Looks good and tests well 👍
Do we need a design ✅ just in case?
Thanks!
opacity: 0.4; | ||
cursor: default; |
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.
We have more and more of these disabled styles that do opacity: 0.4
. Perhaps this is a good opportunity to generalize at some point.
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.
Agreed, although this PR is not the right place for it. We should probably assess the current state of shared config in the package, and decide next steps. For example, some variables come from the base-styles
package, some are internal to the components package. Some variables are defined in Sass, some as CSS custom properties, and others in a JS object.
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.
Definitely should be a separate PR 👍
Seems good to me, 0.4 opacity is also what appears to be used elsewhere. |
Thank you for all the comments! I'll look into making disable options focusable and using I'll ask for a new round of review once that's completed |
3655931
to
98d566f
Compare
a2be8b8
to
adf4750
Compare
I applied changes to make disabled options keyboard-accessible (ie. focusable). The main design change is adding a focus ring for focused, disabled radio options, since the active indicator (backdrop) should not follow focus in that case. I've also updated tests to reflect the new changes. tgc.disabled.but.accessible.mp4 |
* indicator won't follow keyboard focus. This is not an issue for non-radio | ||
* items, since the backdrop is already decoupled from keyboard focus. | ||
*/ | ||
&[role='radio']:focus-visible::after { |
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.
The focus ring is applied to the after
pseudo-element so that we can control its size via the inset
prop, so that we can leave a little gap between the focus ring and the backdrop indicator.
${ ButtonContentView } { | ||
opacity: 0.4; | ||
} |
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.
Opacity is applied to the button contents, so that the focus ring can be at full opacity
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.
Nothing further from my perspective. The code looks good, and everything tests well, just as demonstrated on the video.
Thanks @ciampo 🚀
Thank you @tyxla ! I'll also wait for @jasmussen 's approval specifically for the disabled+focused styles before merging. |
I'm not the best to ask here, as this particular combo of focusing a component with roving focus and what looks like a classic tab-focus style is peculiar to me, specifically because the arrow-key to focus the disabled option still appears to have the previous item selected. It looks like you have two items focused at once. Is there any prior art to look at? What do other segmented controls with disabled options do with their roving focus? My instinct would go towards the backdrop still animating to the disabled item. But if this is the best practice, by all means. I wouldn't think this is a pattern that should be used in the block editor, even if the component supports it. |
@jasmussen it's a tricky one, indeed!
That's exactly what is going on: focus is on a disabled option, which can't be selected (because disabled) — and therefore the previous item is still selected.
Yeah, that's a downside of the current approach. Here is what it looks like when showing the "focus ring" for every focused option (not just disabled): Kapture.2024-07-15.at.17.24.05.mp4
Other libraries
Similarities with
|
I reflected on this a bit more:
Kapture.2024-07-15.at.17.59.25.mp4
Kapture.2024-07-15.at.17.24.05.mp4In conclusion, I think that the better choice, given the circumstances, is not to allow disabled option items to be keyboard focusable in What do y'all think ? @mirka @tyxla @jasmussen |
@jasmussen if I understand correctly, the focus between components shouldn't be affected. This is about moving between tabs, which is performed with the arrow keys.
From https://www.w3.org/TR/wai-aria-1.0/states_and_properties#aria-disabled:
@ciampo in theory, yes, skipping focus for disabled tabs is a possible path forward. However, I'm a bit concerned that if disabled tabs are not focusable, users with visual impairment will miss that the button even exists. Instead, if we keep the button focusable, the assistive technology will inform them that they're working with a disabled tab. As a potential solution, I wonder if we could perhaps make the focus of disabled tabs lighter than the regular one? |
We'll have the same challenge with the TabPanel, it's also designed to be a single focusable, with arrow-key navigation. I'm not against capturing edgecases that are theoretically used by 3rd party consumers, even if not by the block editor itself yet, but it's important we keep the key flows the components were designed for. |
@jasmussen this behavior is not changing. What needs to be decided is, with regards to
I agree, but (as in many other situations) we need to pick the best compromise for a better overall UX.
@tyxla I'm not sure I understand what you're proposing?
@jasmussen we already solved this challenge in
Kapture.2024-07-16.at.11.50.48.mp4It's tricky to apply the same treatment to Kapture.2024-07-15.at.17.24.05.mp4 |
Understood, and I'm not the right person to ask there. As I understand, it appears to be a best practice for disabled buttons to still be focusable. I understand this especially to be the case where, for example, you press "Reset" on a button, where this same action also disables the button as there are no more filters to reset. In that case, it needs to be focusable so as to not cause a focus loss. But in the case of a segmented control, I'm struggling to come up with cases where we'd want to show a segmented control that has disabled options. |
Okay, I think we can take this step-by-step. This PR aims to add support for |
This reverts commit 62959f0.
adf4750
to
fe854cf
Compare
I merged this PR while keeping |
* Add disabled option tests * Fix handling of disabled prop on ToggleGroupControl options * Add basic disabled styles * Update snapshots * CHANGELOG * Support accessibleWhenDisabled for ratio option items * Support accessibleWhenDisabled for button option items * Update snapshots * Update tests to reflect that options are keyboard focusable while disabled * Move CHANGELOG entry to enhancements section * Revert "Support accessibleWhenDisabled for ratio option items" This reverts commit 62959f0. --- Co-authored-by: ciampo <[email protected]> Co-authored-by: mirka <[email protected]> Co-authored-by: tyxla <[email protected]> Co-authored-by: stokesman <[email protected]> Co-authored-by: jasmussen <[email protected]>
What?
Related to #57862
Closes #57862
New take on #34945
disabled
option for theToggleGroupControl
componentWhy?
This was a missing feature in the component, a gap that should be filled.
How?
By forwarding the
disabled
prop to the correct internal components in theToggleGroupControlOptionBase
component.Testing Instructions
disabled
prop to some options, and interact with the component. Make sure that disabled option are correctly skipped and impossible to selectHere's a quick patch to test the storybook examples
Screenshots or screencast
tgc.before.mp4
tgc.after.mp4
✍️ Dev note
ToggleGroupControlOption
components can now be marked as disabled via the newly addeddisabled
prop. While disabled, an option cannot be selected by the end user.