-
Notifications
You must be signed in to change notification settings - Fork 9
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
Ux-157 FormElement api simplification #663
Conversation
@@ -178,7 +178,6 @@ function TimePicker(props) { | |||
mm={time.mm} | |||
onClick={handleClick} | |||
period={time.period} | |||
{...moreProps} |
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.
moreProps should be getting spread on the root TimePicker component rather than on the trigger input
. I wonder why isn't the TimePicker component using a similar api to the DatePicker in that the Trigger Input is passed as a child? We would then be able to set id
and aria-describedBy
with the same pattern as DatePicker. The alternative would be to add an additional prop of triggerInputProps
to the TimePicker component, however I'm not sure that this would be preferable
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.
you can add a new subcomponent <TimePicker.Trigger />
to override the props of the component I guess when was built there was not a need for it. But I don't thinks the point for this PR.
Did you explore to have a wrapper for transform something like this: <FormElement label="Form Label">
<FormElement.Instructions>
<span>
Example text for extra panel for questionnaires. Example text for extra panel for questionnaires Example
text for extra panel for questionnaires. Example text for extra panel for questionnaires
</span>
</FormElement.Instructions>
<FormElement.Content>
<Input
onChange={handleChange}
value={value}
placeholder="Form placeholder"
hasError={Boolean(errorText.length)}
/>
</FormElement.Content>
<FormElement.Description>
<span>This is description text</span>
</FormElement.Description>
<FormElement.Error>{errorText}</FormElement.Error>
<FormElement.Help>
Give me some help. <a href="wegalvanize.com">Learn more</a>.
</FormElement.Help>
</FormElement> into: <FormElementShort
label="Form Label"
instructions="..."
description="..."
error="..."
help="..."
>
<Input
onChange={handleChange}
value={value}
placeholder="Form placeholder"
hasError={Boolean(errorText.length)}
/>
</FormElementShort> ^ the previous wrapper could be an addition to our current API, is short and in the background will be rendering our current FormElement API |
how are we managing currently if we have two inputs in the same [name] [lastname] instead of [name]
[lastName] Unsure what you mean? They can be styled to be displayed inline if necessary. Or I'm not understanding your question. |
No I didn't look into that, This approach is new to me, I've never heard of adding additional shortcut components in any discussions. Perhaps elaborate on the reasons for it so we can have an open discussion? If we like it perhaps there are other components that could benefit from a similar shortcut wrapper approach? For me it appears to be adding non essential components though. |
|
||
if (!children) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<sc.ContentContainer data-pka-anchor="form-element.content" {...moreProps}> | ||
{typeof children === "function" ? children({ idForLabel, refLabel, ariaDescribedBy }) : children} | ||
{renderExtractedChildren()} |
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.
add a console.error or throw new Error if typeof children === "function"
export default { | ||
Input: ({ extractedElements, idForLabel, ariaDescribedBy }) => | ||
getAccessibleElement(extractedElements, (element, index = 0) => | ||
cloneWithAriaAttributes({ element, idForLabel: index === 0 ? idForLabel : null, ariaDescribedBy }) |
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 behaviour of the first Input being provided the id
(that is associated with the formElement label) will be able to be overridden in a follow up pr
Also on line https://github.com/acl-services/paprika/pull/663/files#diff-1a53d8778ef9b8807972d1aa4463c8c9R42
packages/FormElement/CHANGELOG.md
Outdated
@@ -49,3 +49,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). | |||
-- Changed the spacing between label, content and instructions | |||
|
|||
[@tristanjasper](https://github.com/tristanjasper). | |||
|
|||
## [0.3.29] - 2020-08-20 |
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 you should follow the new format that @allison-c introduced, write this on the [unreleased] section read in our slack channel in paprika the announcement about it 🙏 :)
size={size} | ||
/> | ||
)} | ||
<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.
is this a breaking change or our FormElements still support the renderFunction?
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 will be a breaking change. I don't think we should encourage using the renderFunction as an acceptable api moving forward
}; | ||
|
||
const defaultProps = { | ||
idForLabel: null, | ||
ariaDescribedBy: null, | ||
refLabel: null, | ||
wrapperAriaDescribedBy: null, |
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.
why do we have ariaDescribedBy and wrapperAriaDescribedBy, shouldn't this be part of a more universal prop named a11yText (this is what we use across our library)?
Do you have an example where we are using wrapperAriaDescribedBy
?
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.
You can see how we use it in this storybook example - https://github.com/acl-services/paprika/pull/663/files#diff-1a53d8778ef9b8807972d1aa4463c8c9R54
In the case of wrapping FormElements with other FormElements we need to make sure that the aria-describedBy
strings coming from the wrapping FormElement are being passed down to the children
Perhaps a better name for these props could be a11yDescribedByIds
so I'll switch to that
Critiquing without offering an alternative is unfair but I'll try to offer at least something at the end 😄 I want to throw in a general idea: I think it's commendable to try to ease the burden for consumers, but this should always be weighed against complexity or potential for issues within the library. If the library's code becomes too convoluted or otherwise hard to maintain, we're ultimately doing a disservice to the consumers. Particularly in this case, one thing I'm seeing is we're essentially making This isn't a normal dependency though since I don't see a perfect way out, I feel like we may have painted ourselves into a corner introducing this generic <FormElement.Content>
{a11yProps => (
<Input
{...a11yProps}
{...otherProps}
/>
)}
</FormElement.Content> where |
Yes, the reason the api is the way it is currently is exactly for that reason (we didn't want to tie the accessibility implementation too tightly to the components using it ) I see positives and negatives from both approaches. Potentially that solution could be a good option as well as the proposal I have in this pr @alexzherdev. I think that could keep the separation of components, and make it a little bit easier for consumers to implement. There are some issues there though, in that not in all cases do be want to spread all of the I'll consider some alternatives to the api as well if we were to retain the render function approach |
Closing this pr as want to reconsider the approach for api simplification. |
}; | ||
|
||
if (!extractedChildren?.children.length) { | ||
delete extractedChildren.children; |
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.
extractedChildrenHelper generates an empty children array when not needed, perhaps something we should prevent from happening
@alexzherdev Following on from your concerns in comment - #663 (comment) I've modified certain paprika components (DatePicker, TimePicker) to expect a consistent application of |
@@ -149,20 +166,21 @@ function TimePicker(props) { | |||
|
|||
const { t } = useI18n(); | |||
return ( | |||
<sc.TimePicker onFocus={handleFocus} onBlur={handleBlur}> | |||
<sc.TimePicker onFocus={handleFocus} onBlur={handleBlur} {...moreProps}> |
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.
spreading moreProps
here was the original intention so updated this (confirmed with @KaanDarcey)
isDisabled={isDisabled} | ||
isReadOnly={isReadOnly} | ||
size={size} | ||
overridelabelassociation="true" // Add to override default label association |
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 is a proposal for how we can override which element is associated with the label, perhaps wrapping with a FormElement.AssociateLabel
component could be another approach, or any better proposals?
I think we have hit a point with this Component where is starting to be clear that our current approach doesn't escalate correctly neither play well for our consumer as being easier to use or an easy good pattern to compose our Form requirements. Currently is verbose and keep going in this direction will add unnecessary complexity to the FormElement. In the previous attempt we discussed about this propose approach and agree wiring our custom inputs component to this FormElement will make it easy to break it and hard to maintain. Since the idea is to simplify this component, maybe would worth to take a step back approach and make a table of the requirements we need to support with this component and rethink what would be the best solutions for this. I think pushing this component to try to do what we want in their current form might not the best decision. Maybe is time to check https://github.com/adobe/react-spectrum/tree/main/packages/%40react-spectrum and see if we can adopt it or get inspiration how they solve this issue. Another possible solution would be totally detach every piece of this component and provide helpers that generates ids to make easier to compose these elements. like: import ListBox from "@paprika/listbox"
import Input from "@paprika/listbox"
import { Label, Description, Help, Error, etc, Elements, useFormElement } from "@paprika/formElement"
const { generateA11yProps, isVisuallyHiden } = useFormElement();
const inputA11yProps = generateA11yProps();
const listBoxA11yProps = generateA11yProps();
<Element.Group kind={Element.types.kind.horizontal}>
<Element>
<Label {...inputA11yProps.label}>Name</Label>
<Input {...inputA11yProps.input} />
</Element>
<Element>
<Label {...listBoxA11yProps} isVisuallyHidden>preference</Label>
<ListBox {...listBoxA11yProps} {...stuff} />
<Description>Some description</Description>
<Element />
<Element.Group>
|
I generally agree with the above. What we're trying to do is the opposite of what's recommended here https://www.youtube.com/watch?v=AiJ8tRRH0f8 (which I agree with) |
Ok I'll look into the above route |
Purpose 🚀
Proposal for FormElement api simplification so accessibility is handled by the component itself rather than the consumer.
Previously a render function was required as a child of
FormElement.Content
in order to expose accessibility tagsidForLabel, ariaDescribedBy
andrefLabel
which were not obviously available to the developer or how to use them, without looking into the details and storybook examples as each paprika component uses those tags differently.This PR now takes care this by applying the accessibility tags automatically no matter what component you pass it as a child. This means it is less developer effort to work with.
It is still expected that any other aria-labels such as
aria-required
should still be supplied by the consumer.When passing more than one
Input
orinput
element label focusIt is expected that in a follow up PR a follow up Api change to allow overriding the behaviour of the default formElement labelId being applied to the first Input.
Additional changes:
Added support for TimePicker with formElement
Notes ✏️
details of code change / secondary purposes of this PR
Updates 📦
Storybook 📕
http://storybooks.highbond-s3.com/paprika/ux-157-form-element-simplification
Screenshots 📸
optional but highly recommended
References 🔗
https://aclgrc.atlassian.net/browse/UXD-157