-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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 radio option to ButtonGroup #20805
Conversation
Current behavior is consistent with RadioControl
@ajlende Very cool!! 😍 As far as a11y, I'm not sure how to correctly handle ButtonGroups x Radio. I've worked on them individually, but I've never combined the functionality. @diegohaz I'd love to hear your thoughts :) (I just poked around Reakit for inspiration) I took a quick peek at the code. Instead of using It will achieve the same effect, but I think this code path will be clearer and more stable. Thanks!! |
When I'm thinking about a11y, I think about the intention behind an interaction. It could just as easily be a horizontal radio group, but it just happens to be styled as a button group instead which is why I think the keyboard and aria attributes should reflect that. 🤷♂ And thanks for the suggestion! The implementations I found in the wild that used children were Base Web and Mineral UI which is probably why I thought of |
Awesome work here. Thank you for taking care of the experience around radio groups that should be used in more places in Gutenberg. While on the visual level and accessibility-wise everything seems to be great, I'm a bit on the fence whether adding extra functionality to the The question is whether we can make function Example() {
const radio = useRadioState( { state: "orange" } );
return (
<RadioControl { ...radio } aria-label="fruits" as={ ButtonGroup }>
<Radio { ...radio } label="Apple" value="apple" as={ Button } />
<Radio { ...radio } label="Orange" value="orange" as={ Button} />
<Radio { ...radio } label="Watermelon" value="watermelon" as={ Button } />
</RadioGroup>
);
} The challenge is that it's far from how @diegohaz might have better ideas what's the best way to approach it. In general, I'd be in favor of introducing a separate component for this use case rather than adding conditions to the existing |
The Reakit approach is interesting. I'm not a fan of having to pass a bundle of props to everything, but separating out the behavior from which components are rendered is cool. It seems that approach is very core to how Reakit works—it's a different paradigm than what I've seen around here. I should note that the behavior of a radio group in the context of a toolbar is different from on its own, like here. So if we did take that approach, you wouldn't be able to style RadioControl as a toolbar. You'd need a separate toolbar behavior which could get styled as you see fit. I saw in #16514 the desire to also have checkbox functionality added. With the way it is now, that would be achieved by adding a checkbox mode. There isn't an existing checkbox group that it would map nicely onto like this could map to radio control, but it could easily just be another separate component too. |
@ajlende Wowow! Thank you for these updates 😍 . I tested it locally, and it's working as advertised. I do like how simple this configuration is to set up (from a consumer perspective). I think using Context feels better! There's something about the children mapping that I feel like can be improved. However, I'm not too sure what yet. I gave it a shot today (separately), but was unable to come up with a better solution. cc'ing @diegohaz to see if he has any thoughts 😊 |
Thanks for the work @ajlende! I'm following this and researching more about radio/checkbox groups. Will have a deep look into the code very soon. |
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.
Thanks for the great work, @ajlende!
Most of the things I tested work really well:
-
If there's no default checked value, moving focus into the radio group doesn't check them.
-
VoiceOver can move between radio buttons using VO+Arrow keys without checking them (needs VO+Space to check).
-
Since radio buttons are being registered by their index in the children array, rendering radio buttons in a late render doesn't mess with their order (when using arrow keys), which is really cool! :)
Nice job! 👍
Things I missed:
-
There's no RTL support. Arrow keys will behave in the inverted order when rendering the button group in an RTL context. I'm not sure we fully support RTL though.
-
The fact that it needs each radio button to be a direct child may be a problem. This wouldn't work, for example:
const OtherOptions = () => ( <React.Fragment> <Button value="medium">Medium</Button> <Button value="large">Large</Button> </React.Fragment> ); const MyRadioButtonGroup = () => { const [ checked, setChecked ] = useState( 'medium' ); return ( <ButtonGroup mode="radio" onChange={ setChecked } checked={ checked }> <Button value="small">Small</Button> <OtherOptions /> </ButtonGroup> ); };
I believe we may have places where we would need to abstract some options into separate components.
I also think that, instead of updating ButtonGroup
and Button
, which will eventually not scale well, this should be a new component (RadioGroup
?).
In case you need any reference, I was playing with combining Reakit's Radio
component with @wordpress/components' ButtonGroup
. ButtonGroup
still needs to implement React.forwardRef
internally, but it looks pretty good: https://codesandbox.io/s/reakit-radiogroup-wordpress-buttongroup-9wk9b
It aligns with my previous comment. It might make sense to have a separate Aside, it's time to bring more low-level APIs from Reakit. I see that we share too much logic between those two projects and obviously Reakit has way much better testing coverage because it's more targeted. I personally admire the whole composability patterns used there that make all those integrations possible. |
Alright, I think this last batch of changes should have covered all the feedback so far. RTL doesn't work right now as mentioned earlier, and I held off from refactoring @diegohaz I appreciated the codesandbox! Using Reakit was easy enough to get working once I realized it had been updated on master. Having the interactions nicely bundled like that is really nice. 🙂 |
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.
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.
It looks good to me! I miss some unit/integration tests, but they can be added later while this is experimental. Other folks may have a different opinion though.
Thanks for working on this! 🚀
} ); | ||
const radioContext = { | ||
...radioState, | ||
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.
Very clever! 👍
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 miss some unit/integration tests, but they can be added later while this is experimental. Other folks may have a different opinion though.
I never think of it as a blocker. However it comes with the assumption that it’s going to be tackled in the follow-up unless there are reasons not to add more tests. I can’t see any at the moment. Now that we have React Testing Library integrated it should be only easier to write tests.
Nice work, the final API is really flexible 👍
@@ -929,6 +929,12 @@ | |||
"markdown_source": "../packages/components/src/radio-control/README.md", | |||
"parent": "components" | |||
}, | |||
{ | |||
"title": "RadioGroup", |
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.
Interesting, it’s experimental but we expose it in developer documentation. I hope to have a look into it and figure out how we can improve it. The good part is that https://developer.wordpress.org/block-editor/components/ is generated from the branch that targets WP 5.4 😃
return ( | ||
<RadioGroup | ||
// id is required for server side rendering | ||
id="default-radiogroup" |
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 might start inlining the comment in id 😂
id=“required-for-server-side-rendering-only”?
Description
This adds an option to make the
ButtonGroup
behave as aradiogroup
by using the propmode="radio"
.The behavior is consistent with what the design guidelines describe.
Accessibility
And it follows WAI-ARIA 1.1 Radio Group Practices (not nested in a toolbar) for accessibility.
Keyboard Interaction
Tab
andShift + Tab
: Move focus into and out of the radio group. When focus moves into a radio group:Space
: checks the focused radio button if it is not already checked.Right Arrow
andDown Arrow
: move focus to the next radio button in the group, uncheck the previously focused button, and check the newly focused button. If focus is on the last button, focus moves to the first button.Left Arrow
andUp Arrow
: move focus to the previous radio button in the group, uncheck the previously focused button, and check the newly focused button. If focus is on the first button, focus moves to the last button.WAI-ARIA Roles, States, and Properties
radiogroup
.radio
.radio
element hasaria-checked
set totrue
. If it is not checked, it hasaria-checked
set tofalse
.radio
element is labelled by its content, has a visible label referenced byaria-labelledby
, or has a label specified witharia-label
.radiogroup
element has a visible label referenced byaria-labelledby
or has a label specified witharia-label
.radiogroup
element orradio
elements with thearia-describedby
property.* Not sure if the last ones can really be enforced here except by throwing warnings into the console. They might just be up to the user to add.
How has this been tested?
Screenshots
Types of changes
New feature
Checklist: