-
Notifications
You must be signed in to change notification settings - Fork 221
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: Add uncontrolled input example using React Hook Form (#1820)
[React Hook Form](https://react-hook-form.com/) is a popular library for working with uncontrolled inputs. This PR creates an uncontrolled input example that matches the Formik one in functionality. I also grouped all the `<form>` examples together, and added the submit button to the `Select` example [category:Documentation]
- Loading branch information
Showing
8 changed files
with
228 additions
and
56 deletions.
There are no files selected for viewing
4 changes: 2 additions & 2 deletions
4
modules/preview-react/_examples/stories/SelectWithFormik.stories.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,10 @@ | ||
import {Meta, Story} from '@storybook/addon-docs'; | ||
import {SelectWithFormik} from './examples/SelectWithFormik'; | ||
|
||
<Meta title="Examples/Select With Formik/React" /> | ||
<Meta title="Examples/Forms/Select Using Formik/React" /> | ||
|
||
# Canvas Kit Examples | ||
|
||
## Select With Formik | ||
## Select Using Formik | ||
|
||
<ExampleCodeBlock code={SelectWithFormik} /> |
4 changes: 2 additions & 2 deletions
4
modules/preview-react/_examples/stories/TextInputWithFormik.stories.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,10 @@ | ||
import {Meta, Story} from '@storybook/addon-docs'; | ||
import {TextInputWithFormik} from './examples/TextInputWithFormik'; | ||
|
||
<Meta title="Examples/Text Input With Formik/React" /> | ||
<Meta title="Examples/Forms/Controlled Inputs Using Formik/React" /> | ||
|
||
# Canvas Kit Examples | ||
|
||
## Text Input With Formik | ||
## Controlled Text Inputs Using Formik | ||
|
||
<ExampleCodeBlock code={TextInputWithFormik} /> |
10 changes: 10 additions & 0 deletions
10
modules/preview-react/_examples/stories/TextInputWithReactHookForm.stories.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import {Meta, Story} from '@storybook/addon-docs'; | ||
import {TextInputWithReactHookForm} from './examples/TextInputWithReactHookForm'; | ||
|
||
<Meta title="Examples/Forms/Uncontrolled Inputs Using React Hook Form/React" /> | ||
|
||
# Canvas Kit Examples | ||
|
||
## Uncontrolled Text Inputs Using React Hook Form | ||
|
||
<ExampleCodeBlock code={TextInputWithReactHookForm} /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
137 changes: 137 additions & 0 deletions
137
modules/preview-react/_examples/stories/examples/TextInputWithReactHookForm.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import React from 'react'; | ||
|
||
import {useForm, FieldErrorsImpl} from 'react-hook-form'; | ||
import {object, SchemaOf, string} from 'yup'; | ||
|
||
import {TextInput} from '@workday/canvas-kit-preview-react/text-input'; | ||
import {HStack, VStack} from '@workday/canvas-kit-react/layout'; | ||
import {TertiaryButton, PrimaryButton} from '@workday/canvas-kit-react/button'; | ||
import {visibleIcon, invisibleIcon} from '@workday/canvas-system-icons-web'; | ||
import {useUniqueId} from '@workday/canvas-kit-react/common'; | ||
|
||
type YupValidationResolver = <T extends {}>( | ||
validationSchema: SchemaOf<T> | ||
) => (data: T) => Promise<{values: T; errors: {}} | {values: {}; errors: FieldErrorsImpl<T>}>; | ||
|
||
const useYupValidationResolver: YupValidationResolver = validationSchema => { | ||
return React.useCallback( | ||
async data => { | ||
try { | ||
const values = await validationSchema.validate(data, {abortEarly: false}); | ||
return {values, errors: {}}; | ||
} catch (errors) { | ||
return { | ||
values: {}, | ||
errors: errors.inner.reduce( | ||
(allErrors, currentError) => ({ | ||
...allErrors, | ||
[currentError.path]: { | ||
type: currentError.type ?? 'validation', | ||
message: currentError.message, | ||
}, | ||
}), | ||
{} | ||
), | ||
}; | ||
} | ||
}, | ||
[validationSchema] | ||
); | ||
}; | ||
|
||
interface LoginSchema { | ||
email: string; | ||
password: string; | ||
} | ||
|
||
const passwordMinimum = 8; | ||
const passwordHint = `Password should be of minimum ${passwordMinimum} characters length`; | ||
const emailRequired = 'Email is required'; | ||
const passwordRequired = 'Password is required'; | ||
|
||
const validationSchema: SchemaOf<LoginSchema> = object({ | ||
email: string() | ||
.email('Enter a valid email') | ||
.required(emailRequired), | ||
password: string() | ||
.min(passwordMinimum, passwordHint) | ||
.required(passwordRequired), | ||
}); | ||
|
||
export const TextInputWithReactHookForm = () => { | ||
const { | ||
handleSubmit, | ||
register, | ||
formState: {errors}, | ||
} = useForm<LoginSchema>({ | ||
defaultValues: { | ||
email: '[email protected]', | ||
password: 'foobarbaz', | ||
}, | ||
resolver: useYupValidationResolver(validationSchema), | ||
mode: 'onTouched', | ||
}); | ||
|
||
const onSubmit = handleSubmit(values => { | ||
setShowPassword(false); | ||
// Send data to server | ||
setTimeout(() => { | ||
alert(JSON.stringify(values, null, 2)); | ||
}, 0); | ||
}); | ||
|
||
const [showPassword, setShowPassword] = React.useState(false); | ||
const passwordId = useUniqueId(); | ||
const passwordRef = React.useRef<HTMLInputElement | null>(null); | ||
const {ref: passwordCallbackRef, ...passwordRegistration} = register('password'); | ||
const combinePasswordRef = (ref: HTMLInputElement | null) => { | ||
passwordCallbackRef(ref); | ||
passwordRef.current = ref; | ||
}; | ||
|
||
return ( | ||
<form onSubmit={onSubmit} action="."> | ||
<VStack spacing="xs" alignItems="flex-start"> | ||
<TextInput orientation="vertical" isRequired={true} hasError={!!errors.email}> | ||
<TextInput.Label>Email</TextInput.Label> | ||
<TextInput.Field | ||
{...register('email')} | ||
autoComplete="username" | ||
placeholder="[email protected]" | ||
/> | ||
<TextInput.Hint>{errors.email?.message}</TextInput.Hint> | ||
</TextInput> | ||
<TextInput | ||
orientation="vertical" | ||
id={passwordId} | ||
isRequired={true} | ||
hasError={!!errors.password} | ||
> | ||
<TextInput.Label>Password</TextInput.Label> | ||
<HStack spacing="xxs"> | ||
<TextInput.Field | ||
{...passwordRegistration} | ||
type={showPassword ? 'text' : 'password'} | ||
autoComplete="current-password" | ||
spellCheck={false} | ||
ref={combinePasswordRef} | ||
/> | ||
<TertiaryButton | ||
type="button" | ||
icon={showPassword ? invisibleIcon : visibleIcon} | ||
aria-label={showPassword ? 'Hide Password' : 'Show Password'} | ||
aria-controls={`input-${passwordId}`} | ||
onClick={() => { | ||
setShowPassword(state => !state); | ||
passwordRef.current?.focus(); | ||
}} | ||
/> | ||
</HStack> | ||
<TextInput.Hint>{errors.password?.message || passwordHint}</TextInput.Hint> | ||
</TextInput> | ||
|
||
<PrimaryButton type="submit">Submit</PrimaryButton> | ||
</VStack> | ||
</form> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.