-
Notifications
You must be signed in to change notification settings - Fork 843
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
[EuiForm, EuiCallout] Added focus state to error callout #4497
Conversation
Since this is a community submitted pull request, a Jenkins build has not been kicked off automatically. Can an Elastic organization member please verify the contents of this patch and then kick off a build manually? |
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 making this change! Code changes look good overall, but have a couple small requests to improve documentation & clean up the component code a little.
display the callout above or below by passing | ||
<EuiCode>invalidCallout=“above“</EuiCode>(default) or | ||
<EuiCode>invalidCallout=“below“</EuiCode> depending upon | ||
your choice. Additionally you can also hide the callout by passing |
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.
display the callout above or below by passing | |
<EuiCode>invalidCallout=“above“</EuiCode>(default) or | |
<EuiCode>invalidCallout=“below“</EuiCode> depending upon | |
your choice. Additionally you can also hide the callout by passing | |
display the callout above or below the form by passing | |
<EuiCode>invalidCallout=“above“</EuiCode>(default) or | |
<EuiCode>invalidCallout=“below“</EuiCode>. | |
Additionally, you can hide the callout by passing |
these changes may need to be reformatted with different line lengths for prettier's linting
src/components/form/form.tsx
Outdated
@@ -73,7 +73,7 @@ export const EuiForm: FunctionComponent<EuiFormProps> = ({ | |||
|
|||
let optionalErrorAlert; | |||
|
|||
if (isInvalid && invalidCallout === 'above') { | |||
if (isInvalid && (invalidCallout === 'above' || invalidCallout === 'below')) { |
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.
Let's invert this to check for !none
vs. above || below
src/components/form/form.tsx
Outdated
if (invalidCallout === 'above') { | ||
return ( | ||
<Element className={classes} {...(rest as HTMLAttributes<HTMLElement>)}> | ||
{optionalErrorAlert} | ||
{children} | ||
</Element> | ||
); | ||
} else { | ||
return ( | ||
<Element className={classes} {...(rest as HTMLAttributes<HTMLElement>)}> | ||
{children} | ||
{optionalErrorAlert} | ||
</Element> | ||
); | ||
} |
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 seems a little cleaner to avoid the wrapping if
and conditionally render optionalErrorAlert
instead,
if (invalidCallout === 'above') { | |
return ( | |
<Element className={classes} {...(rest as HTMLAttributes<HTMLElement>)}> | |
{optionalErrorAlert} | |
{children} | |
</Element> | |
); | |
} else { | |
return ( | |
<Element className={classes} {...(rest as HTMLAttributes<HTMLElement>)}> | |
{children} | |
{optionalErrorAlert} | |
</Element> | |
); | |
} | |
if (invalidCallout === 'above') { | |
return ( | |
<Element className={classes} {...(rest as HTMLAttributes<HTMLElement>)}> | |
{optionalErrorAlert} | |
{children} | |
</Element> | |
); | |
} else { | |
return ( | |
<Element className={classes} {...(rest as HTMLAttributes<HTMLElement>)}> | |
{invalidCallout === 'above' && optionalErrorAlert} | |
{children} | |
{invalidCallout === 'below' && optionalErrorAlert} | |
</Element> |
{button} | ||
<EuiSpacer /> |
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.
Let's make this example interactive to show the two positions, I've tested by adding the following at line 36 of validation.js and adding EuiButtonGroup
to the list of component imports at the top of the file
const [calloutPosition, setCalloutPosition] = useState('above');
return (
<Fragment>
<EuiButtonGroup
legend="sets callout position in the form"
options={[
{ id: 'above', label: 'Show callout above form' },
{ id: 'below', label: 'Show callout below form' },
]}
idSelected={calloutPosition}
onChange={setCalloutPosition}
/>
<EuiSpacer />
<EuiForm
...
@akashgp09 thanks for the additional changes! We just had a discussion around this & what was asked for in #1854 and are going to think about the request more and what the best implementation is. For transparency, these thoughts include:
|
@akashgp09 If you feel up to experimenting on this one, I think the most ideal UX would be to:
|
thank you for the suggestions, will make the changes soon :-) |
running CI to deploy this for testing: jenkins test this |
Preview documentation changes for this PR: https://eui.elastic.co/pr_4497/ |
will wrapping the
|
Or should i implement forwardRef to |
I would go the |
src/components/form/form.tsx
Outdated
@@ -84,6 +80,10 @@ export const EuiForm: FunctionComponent<EuiFormProps> = ({ | |||
default="Please address the highlighted errors."> | |||
{(addressFormErrors: string) => ( | |||
<EuiCallOut | |||
tabIndex={-1} | |||
ref={(node) => { |
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 interesting. Because it creates the new ref
function every render pass, it will re-focus the element every time and not only first mount. I imagine this will be aggravating in cases when the user tries to edit an invalid field, but we'll test what happens in practice.
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.
So we need to focus the element only once, for the first mount ?
We can accomplish this by setting focus to the Element inside useEffect hook 🤔
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.
Tested this by adding a setState
to track the value of the example's textarea, and not only does the screen scroll back to the error box, but it takes focus off the textarea so I had to click back into it for each character. Either a useEffect, or moving the function passed to ref
into a useCallback
, should help clear that up.
jenkins test this |
Preview documentation changes for this PR: https://eui.elastic.co/pr_4497/ |
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.
Small syntax request & changelog update needs correction, but the code change & functionality looks good to me.
@chandlerprall i have made the changes as suggested. Please let me know if there are any changes required. |
Jenkins, test this |
Preview documentation changes for this PR: https://eui.elastic.co/pr_4497/ |
Jenkins, test this |
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 checked this in Chrome, FF, Safari and Chrome mobile. I even checked how it would behave if the form existed in an element with overflow. They all seem to behave well. 🎉
Preview documentation changes for this PR: https://eui.elastic.co/pr_4497/ |
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.
Changes LGTM; pulled & tested scenarios locally
Jenkins, test this |
Preview documentation changes for this PR: https://eui.elastic.co/pr_4497/ |
@chandlerprall @cchaos thank you for your continuous feedbacks and guidance on this one. Finally it's done ^_^ |
Summary
This PR Fixes: #1854
Added ability to show error callout at the bottom of the form .User can now passbelow
as an option toinvalidCallout
prop ofEuiForm
.Focus state shifts to
EuiCallout
when there is any validation error inEuiForm
.Screenshot
Checklist
- [ ] Props have proper autodocs and playground toggles- [ ] Checked Code Sandbox works for the any docs examples- [ ] Added or updated jest tests- [ ] Checked for breaking changes and labeled appropriately