-
Notifications
You must be signed in to change notification settings - Fork 526
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
Adds CheckboxGroup and RadioGroup components to replace ChoiceFieldset #1862
Conversation
🦋 Changeset detectedLatest commit: 6e0c553 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Alternative API to considerI think the API works as it is, but I think it could be improved by splitting this into Check out the Loom for a more detailed explanation: https://www.loom.com/share/3cf3340ebfd54b469750eaf8debe1d23 Current API<ChoiceGroup>
<ChoiceGroup.Label>Radios</ChoiceGroup.Label>
<ChoiceGroup.Caption>These are some radio inputs</ChoiceGroup.Caption>
<FormControl>
<Radio name="radioChoices" value="radioOne" onChange={handleRadioChange} />
<FormControl.Label>Radio one</FormControl.Label>
</FormControl>
<FormControl>
<Radio name="radioChoices" value="radioTwo" onChange={handleRadioChange} />
<FormControl.Label>Radio two</FormControl.Label>
</FormControl>
<FormControl>
<Radio name="radioChoices" value="radioThree" onChange={handleRadioChange} />
<FormControl.Label>Radio three</FormControl.Label>
</FormControl>
{hasError && <ChoiceGroup.Validation>Some error message</ChoiceGroup.Validation>}
</ChoiceGroup> PROS:
CONS:
Alternative API: CheckboxGroup / RadioGroupRadioGroup type RadioGroupProps = {
name: string
onChange?: (value: string) => void
}
// ⬇️ usage example ⬇️
<RadioGroup onChange={(value) => { setSelectedRadioValue(value) }} name="radioInputGroup">
<RadioGroup.Label>Radios</RadioGroup.Label>
<RadioGroup.Caption>These are some radio inputs</RadioGroup.Caption>
<FormControl>
<Radio value="choiceOne" onChange={(e) => { console.log('input data:', e.currentTarget) }} />
<FormControl.Label>Selectable choice</FormControl.Label>
</FormControl>
<FormControl>
<Radio value="choiceTwo" />
<FormControl.Label>Selectable choice</FormControl.Label>
</FormControl>
<FormControl>
<Radio value="choiceThree" />
<FormControl.Label>Selectable choice</FormControl.Label>
</FormControl>
{hasError && <RadioGroup.Validation>Some error message</RadioGroup.Validation>}
</RadioGroup> CheckboxGroup type CheckboxGroupProps = {
onChange?: (values: string[]) => void
}
// ⬇️ usage example ⬇️
<CheckboxGroup onChange={(values) => { setSelectedCheckboxValues(values) }}>
<CheckboxGroup.Label>Checkboxes</CheckboxGroup.Label>
<CheckboxGroup.Caption>These are some checkbox inputs</CheckboxGroup.Caption>
<FormControl>
<Checkbox value="choiceOne" onChange={(e) => { console.log('input data:', e.currentTarget) }} />
<FormControl.Label>Selectable choice</FormControl.Label>
</FormControl>
<FormControl>
<Checkbox value="choiceTwo" />
<FormControl.Label>Selectable choice</FormControl.Label>
</FormControl>
<FormControl>
<Checkbox value="choiceThree" />
<FormControl.Label>Selectable choice</FormControl.Label>
</FormControl>
{hasError && <CheckboxGroup.Validation>Some error message</CheckboxGroup.Validation>}
</CheckboxGroup> PROS:
CONS:
|
size-limit report 📦
|
❤️ the video walkthrough @mperrotti Here are some initial thoughts...
Agree on naming concerns. As an alternative suggestion, how about <FormControlGroup variant="radio">
<FormControlGroup.Label>Radios</FormControlGroup.Label>
<FormControlGroup.Caption>These are some radio inputs</FormControlGroup.Caption>
<FormControl>
<Radio name="radioChoices" value="radioOne" onChange={handleRadioChange} />
<FormControl.Label>Radio one</FormControl.Label>
</FormControl>
<FormControl>
<Radio name="radioChoices" value="radioTwo" onChange={handleRadioChange} />
<FormControl.Label>Radio two</FormControl.Label>
</FormControl>
<FormControl>
<Radio name="radioChoices" value="radioThree" onChange={handleRadioChange} />
<FormControl.Label>Radio three</FormControl.Label>
</FormControl>
{hasError && <FormControlGroup.Validation>Some error message</FormControlGroup.Validation>}
</FormControlGroup>
Given we've opted for less magic under-the-hood in other places, it would be more consistent to continue with this more declarative and explicit pattern despite it being more verbose. I loved the idea that you can override a one handler in particular, but keep the others inheriting from the parent. One thing that makes me nervous though, is using Context to enable this behaviour. It feels like a very expensive in-memory solution to a problem that only really provides ergonomic benefits? I’d love to understand the trade-offs a bit more though.
I like the idea, but the reasons you mentioned felt like we were engineering for hypothetical, future scenarios which might not play out? Would it be better to wait for those new patterns to land first? |
I agree—we can wait for those new patterns to land first.
I'm fine with this. We can always add an |
👍 . I'm struggling with a better name suggestion that only applies to checkboxes and radio's. I'm also coming round to You listed context as a con if you did that, is Context needed? Can you use local state instead for example? You also listed onChange inconsistencies and
In that example I'm suggesting If I recall, we also dropped it previously as we don't support custom input components in FormControl, but if we did, this type of prop might be needed. |
docs/content/ChoiceGroup.mdx
Outdated
### Basic | ||
|
||
```jsx live | ||
<ChoiceGroup> |
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.
Shall we update the FormControl
radio and checkbox example here to use this component? These examples feel very similar now. Maybe adding a Flash or internal link pointing to ChoiceGroup
so users know it exists.
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.
Yes, definitely! Good catch.
docs/content/ChoiceGroup.mdx
Outdated
render(<WithOnChangeHandlers />) | ||
``` | ||
|
||
### Checkbox group |
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.
Should this one be merged into the basic example to appear alongside it? It feels like a 'basic' example to but quite tucked away in the examples.
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.
Yea, good suggestion.
docs/content/ChoiceGroup.mdx
Outdated
### Disabled | ||
|
||
```jsx live | ||
<ChoiceGroup 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.
FYI some cursor inconsistencies here
Screen.Recording.2022-02-16.at.20.33.26.mov
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.
Good catch. I'll fix that.
docs/content/ChoiceGroup.mdx
Outdated
### Required | ||
|
||
```jsx live | ||
<ChoiceGroup required> |
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.
Would we ever want to allow the colour of the asterisk to be changed? For example, on some forms you'll see it in red for extra prominence.
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.
As of now, we do not. The forms as they exist today are totally custom and not following any kind of system.
src/ChoiceGroup/ChoiceGroup.tsx
Outdated
<Box as="legend" mb={isLegendVisible ? 2 : undefined} padding={0}> | ||
{slots.Label} | ||
{slots.Caption} | ||
<VisuallyHidden> |
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.
Although it's visually hidden, could this become an issue for both screen readers and crawlers that you're duplicating the validation message markup here?
For screen-readers specifically, it's probably worth adding an aria-hidden
here.
☝️ Just noticed you added it to the one at the bottom
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.
Also, could this be guarded? So it doesn't generate an empty tag if there's no validation message yet?
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.
Good call about making this guarded. Will do 👍
In that case, I think I might give it a try.
I could attempt to do it with local state instead of Context, but I'm not sure if it will be possible. We need a way to get the
The only reason I think that It doesn't need to do that extra stuff, but I think it would be a shame not to. |
…ct into mp/choicefieldset-replacement
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'm liking this API. Nice work, @mperrotti!
I skimmed the docs and the code and everything looks good to me 👍
Closes https://github.com/github/primer/issues/694
Screenshots
Merge checklist
Take a look at the What we look for in reviews section of the contributing guidelines for more information on how we review PRs.