-
Notifications
You must be signed in to change notification settings - Fork 377
feat(Form): add functionality for form group roles #7516
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
Conversation
|
Preview: https://patternfly-react-pr-7516.surge.sh A11y report: https://patternfly-react-pr-7516-a11y.surge.sh |
965ee6e to
283a8fe
Compare
| fieldId: string; | ||
| /** Object for identifying whether the form group has multiple inputs for a single label, and | ||
| * whether the multiple inputs are radio inputs or not. */ | ||
| multipleInputs?: MultipleInputsObject; |
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 think this is pretty slick - but it does feel very different from anything we've done before with props. I think I like it. I'm curious what others think.
wise-king-sullyman
left a comment
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.
Overall this looks great! I think the testing is really solid but I did make one suggestion and have one thing I'll need to look into more and get back to you about.
I also noticed one slight typing/demo issue and that's the only real "hard" blocker out of all my comments.
| fieldId: string; | ||
| /** Object for identifying whether the form group has multiple inputs for a single label, and | ||
| * whether the multiple inputs are radio inputs or not. */ | ||
| multipleInputs?: MultipleInputsObject; |
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.
In some examples you're passing multipleInputs as a boolean, but it's just typed as an object here. I think that either those examples should be updated to pass an empty object or the type here should be updated to be an object or a boolean.
If you go the type updating route though I feel like this prop name could use a slight edit.
I do also wonder if (as we've talked about a bit) this API would maybe make more sense with two separate props instead of an object prop that wraps another singular prop. I'm not positive how I feel about this idea though and would love input from others 🙂
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 definitely understand the concern with the API here. A few ways that we could go about this off the top of my head:
- Update the interface so that
isRadioGroupis required when passing inmultipleInputs, and updating examples so that eithermultipleInputs={{ isRadioGroup: true }}ormultipleInputs={{ isRadioGroup: false }}is passed in (as opposed to justmultipleInputs - Renaming the prop to something along the lines of
groupOptionsorgroupType, then adding in another prop to the interface along the lines ofinputAmount: 'single' | 'multiple'(more similar to your DataList implementation). This would result in a prop looking likegroupOptions={{ inputAmount: 'multiple', isRadioGroup: 'true' }}. Not personally sure if this would make the most sense, however, since the default behavior is already a single input per form group, and this might require passing in props to retain that default behavior. - Just breaking things out to two separate props like you mentioned, maybe
hasMultipleInputsandisRadioGroup, and just noting in theisRadioGroupdescription that it should only be passed in whenhasMultipleInputsis also passed in.
Tagging @nicolethoen as well for input on the above proposals
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.
Yeah those are all the direct options I can think of as well, and honestly I don't love any of them. Your current API implementation is probably my favorite of these options.
Is there any way we could automatically determine if a group has multiple inputs rather than requiring the consumer to pass that in as a prop? I feel like that would really be the best approach from the consumer side.
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 could possibly check whether the children prop of the Form Group is an array (more than 1 input will return true), but this could possibly get skewed if any non-input content is rendered within the Form Group, such as helper text or validation text. I tinkered around a little and there's some workaround to that, but it may not be 100% accurate all the time due to there being various variables to consider (if it's a native input element, whether it's a React component, whether that React component is an input, etc.)
This is just from some quick tinkering around, though, so I might be missing something more obvious.
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.
Yeah that doesn't seem ideal. I have one vague idea taking shape which would involve in some way adding registration behavior of some sort to our input components. If that would even work though it would probably be out of scope for this issue, so for the moment at least I'm going to join in saying that your implementation is slick and I think I like it.
|
|
||
| expect(labelElement.tagName).toBe('SPAN'); | ||
| }); | ||
| }); |
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.
One test I would add here: a sanity check test for the expected behavior when multipleInputs is not passed or is otherwise falsy.
| const labelElement = screen.getByRole('group').querySelector('.pf-c-form__label'); | ||
|
|
||
| expect(labelElement.tagName).toBe('SPAN'); |
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.
This feels very non-RTL to me, but I'll have to think about if there's a way to test this that would be more in line with a real user experience and circle back to you. There very well might not be.
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.
Do you think testing against a snapshot would make more sense here? Once unit tests are rewritten to align better with our current intentions, this would ideally only result in 2 snapshots for the component.
Another possibility might be testing user events. If there's a form group with a single input, clicking the label should place focus on the input; if there's a form group with multiple inputs, however, clicking the span won't place focus anywhere.
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.
Another possibility might be testing user events. If there's a form group with a single input, clicking the label should place focus on the input; if there's a form group with multiple inputs, however, clicking the span won't place focus anywhere.
That seems like it aligns super well with the actual difference in UX and is the way I would go, awesome idea!
283a8fe to
aa7c6b7
Compare
wise-king-sullyman
left a comment
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 really like the testing approach here! I just have a couple of small things about the selectors being used.
| const labelElement = screen.getByTestId('form-group-test-id').querySelector('.pf-c-form__label'); | ||
| const input = screen.getByTestId('form-group-test-id').querySelector('input'); |
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.
To better replicate the real user experience I would like if we could get away from relying on test ids to select these elements if possible.
I haven't had the time to test this myself yet to confirm, but I think that this should be possible via something like:
| const labelElement = screen.getByTestId('form-group-test-id').querySelector('.pf-c-form__label'); | |
| const input = screen.getByTestId('form-group-test-id').querySelector('input'); | |
| const labelElement = screen.getByText('label'); | |
| const input = screen.getByRole('textbox'); |
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 catch! The getByText is more obvious in hindsight, but didn't think of the textbox role 🔥
| ); | ||
|
|
||
| const labelElement = screen.getByRole('group').querySelector('.pf-c-form__label'); | ||
| const inputs = screen.getByRole('group').querySelectorAll('input'); |
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.
And for selecting multiple inputs you should be able to just use getAllByRole rather than getByRole.
cf8221a to
c5b164c
Compare
|
Based on conversation, the latest update renames the new prop to simply "role", and all tests and examples have been updated accordingly. |
wise-king-sullyman
left a comment
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 really like this, just one question.
| /** Sets the role of the form group if multiple inputs are being passed in. If multiple radio inputs | ||
| * are passed in use "radiogroup", otherwise use "group" when passing in multiple of any other input. | ||
| */ | ||
| role?: 'group' | '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.
One last thought about this, would it be better to just type this as string and just conditionally check for group or radiogroup explicitly as needed, so that if a consumer wants a different role to apply they can do so?
cc @nicolethoen
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 agree - I think it can be string in case someone wants to explicitly set role=none or role=presentation or who knows what else
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.
In terms of the scope of the Form Group fix, if we allow any role to be passed in that might require another update in Core (right now I believe they're only applying either group or radiogroup; EDIT: this may only really be applicable if Core were to show an example of any other role, and even then probably only affects the handlebars file so this could actually be a moot point).
If the intent of a form group is to act more like a fieldset (which by default gets announced as a "group"), only allowing either of those two options seems like the better solution. If we'd want to allow any role to be passed, however, it might also be worth adding a disclaimer in the prop description about using other roles (basically a "be careful" sort of thing).
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 think that we should allow any role to be passed through for now at least, since like @nicolethoen mentioned yesterday FormGroup is currently often used in a non-fieldset-like fashion. I also wonder if any consumer might currently be applying a different role to a FormGroup for some reason, in which case limiting it to only group/radiogroup could be breaking.
You bring up good points though, and this seems like a topic that deserves further discussion and maybe a change in the next breaking change release?
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.
That's a fair point. Will update the type to a string, and added this to our breaking change doc just to keep it in mind.
mcarrano
left a comment
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.
c5b164c to
7c490d1
Compare
wise-king-sullyman
left a comment
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 great!
mcarrano
left a comment
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 now. Thanks @thatblindgeye !
mattnolting
left a comment
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.
LGTM 🎉
srambach
left a comment
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.
LGTM 👍
| /** Sets the role of the form group. Pass in "radiogroup" when the form group contains multiple | ||
| * radio inputs, or pass in "group" when the form group contains multiple of any other input type. | ||
| */ | ||
| role?: string; |
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.
Can this only be radiogroup or group? If so i would suggest defining a union.
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 can be more than just radiogroup or group, role is an attribute like aria-label that could be a lot of things. But if it's radio/radiogroup this PR makes it automatically also do some dynamic wiring of a few aria-attributes so that the inputgroups of radio or checkboxes will be labelled correctly.
jessiehuff
left a comment
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.
LGTM! Tested in VO, JAWS, and NVDA and all announce them as a group as expected. :)
|
Your changes have been released in:
Thanks for your contribution! 🎉 |


What: Closes #6097, closes #7517
Followup from patternfly/patternfly#4240 and patternfly/patternfly#4424
Form preview build
Added a prop object to the FormGroup component, which when passed in will:
spaninstead of alabelelementspanto the.pf-c-form__groupelement via thearia-labelledbyattributerole="group"orrole="radiogroup"to the.pf-c-form__groupelement, depending on whether themultipleInputs.isRadioGroupprop is set to true or notAlso update examples and added a radio form group to more closely resemble the Core example.
Additional issues: