(react) react-hook-form Select example

Our form elements needs to be usable with react-hook-form
This commit is contained in:
Romain Le Cellier
2023-07-31 15:21:55 +02:00
parent 4e53857159
commit e3563f85d1
6 changed files with 218 additions and 0 deletions

View File

@@ -70,6 +70,7 @@ export const SelectMonoAux = ({
value, value,
disabled, disabled,
clearable = true, clearable = true,
onBlur,
}: SelectAuxProps) => { }: SelectAuxProps) => {
const { t } = useCunningham(); const { t } = useCunningham();
const labelProps = downshiftReturn.getLabelProps(); const labelProps = downshiftReturn.getLabelProps();
@@ -101,6 +102,10 @@ export const SelectMonoAux = ({
"c__select--disabled": disabled, "c__select--disabled": disabled,
}, },
)} )}
onBlur={() =>
onBlur &&
onBlur({ target: { value: downshiftReturn.selectedItem?.value } })
}
> >
{/* We disabled linting for this specific line because we consider that the onClick props is only used for */} {/* We disabled linting for this specific line because we consider that the onClick props is only used for */}
{/* mouse users, so this do not engender any issue for accessibility. */} {/* mouse users, so this do not engender any issue for accessibility. */}

View File

@@ -112,6 +112,14 @@ using the component in a controlled way.
<Story id="components-forms-select-mono--controlled"/> <Story id="components-forms-select-mono--controlled"/>
</Canvas> </Canvas>
## Usage with react-hook-form
You can use this input with [react-hook-form](https://react-hook-form.com/docs)
<Canvas sourceState="shown">
<Story id="components-forms-select-mono--react-hook-form"/>
</Canvas>
## Props ## Props
The props of this component are as close as possible to the native select component. You can see the list of props below. The props of this component are as close as possible to the native select component. You can see the list of props below.

View File

@@ -1,9 +1,17 @@
import { Meta, StoryFn } from "@storybook/react"; import { Meta, StoryFn } from "@storybook/react";
import React, { useState } from "react"; import React, { useState } from "react";
import { useForm, Controller, ControllerRenderProps } from "react-hook-form";
import * as Yup from "yup";
import { faker } from "@faker-js/faker"; import { faker } from "@faker-js/faker";
import { yupResolver } from "@hookform/resolvers/yup";
import { Select } from ":/components/Forms/Select"; import { Select } from ":/components/Forms/Select";
import { Button } from ":/components/Button"; import { Button } from ":/components/Button";
import { CunninghamProvider } from ":/components/Provider"; import { CunninghamProvider } from ":/components/Provider";
import {
getFieldState,
getFieldErrorMessage,
onSubmit,
} from ":/tests/reactHookFormUtils";
export default { export default {
title: "Components/Forms/Select/Mono", title: "Components/Forms/Select/Mono",
@@ -156,6 +164,87 @@ export const SearchableControlled = () => {
); );
}; };
export const ReactHookForm = () => {
enum CitiesOptionEnum {
NONE = "",
DIJON = "dijon",
PARIS = "paris",
TOKYO = "tokyo",
}
interface SelectExampleFormValues {
joTown: CitiesOptionEnum;
}
const selectExampleSchema = Yup.object().shape({
joTown: Yup.string()
.required()
.oneOf([CitiesOptionEnum.PARIS], "That's not the right town!"),
});
const { handleSubmit, formState, control } = useForm<SelectExampleFormValues>(
{
defaultValues: {
joTown: CitiesOptionEnum.NONE,
},
mode: "onChange",
reValidateMode: "onChange",
resolver: yupResolver(selectExampleSchema),
},
);
const renderSelect = ({
field,
}: {
field: ControllerRenderProps<SelectExampleFormValues, "joTown">;
}) => {
return (
<>
<div>Where will the 2024 Olympics take place?</div>
<Select
label="Select a city"
options={[
{
label: "Dijon",
value: CitiesOptionEnum.DIJON,
},
{
label: "Paris",
value: CitiesOptionEnum.PARIS,
},
{
label: "Tokyo",
value: CitiesOptionEnum.TOKYO,
},
]}
state={getFieldState("joTown", formState)}
text={getFieldErrorMessage("joTown", formState)}
onBlur={field.onBlur}
onChange={field.onChange}
value={field.value}
/>
</>
);
};
return (
<CunninghamProvider>
<form
style={{
display: "flex",
flexDirection: "column",
gap: "1rem",
width: "400px",
}}
onSubmit={handleSubmit(onSubmit)}
>
<Controller control={control} name="joTown" render={renderSelect} />
<Button fullWidth={true}>Submit</Button>
</form>
</CunninghamProvider>
);
};
export const FullWidth = { export const FullWidth = {
render: Template, render: Template,

View File

@@ -23,6 +23,9 @@ export type SelectProps = PropsWithChildren &
onChange?: (event: { onChange?: (event: {
target: { value: string | number | undefined | string[] }; target: { value: string | number | undefined | string[] };
}) => void; }) => void;
onBlur?: (event: {
target: { value: string | number | undefined | string[] };
}) => void;
disabled?: boolean; disabled?: boolean;
clearable?: boolean; clearable?: boolean;
multi?: boolean; multi?: boolean;

View File

@@ -97,6 +97,14 @@ using the component in a controlled way.
<Story id="components-forms-select-multi--controlled"/> <Story id="components-forms-select-multi--controlled"/>
</Canvas> </Canvas>
## Usage with react-hook-form
You can use this input with [react-hook-form](https://react-hook-form.com/docs)
<Canvas sourceState="shown">
<Story id="components-forms-select-multi--react-hook-form"/>
</Canvas>
## Props ## Props
They are the same as the [Select](?path=/docs/components-forms-select-mono--docs#props) component. They are the same as the [Select](?path=/docs/components-forms-select-mono--docs#props) component.

View File

@@ -1,9 +1,17 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { useForm, Controller, ControllerRenderProps } from "react-hook-form";
import * as Yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { Meta, StoryFn } from "@storybook/react"; import { Meta, StoryFn } from "@storybook/react";
import { faker } from "@faker-js/faker"; import { faker } from "@faker-js/faker";
import { Select } from ":/components/Forms/Select"; import { Select } from ":/components/Forms/Select";
import { CunninghamProvider } from ":/components/Provider"; import { CunninghamProvider } from ":/components/Provider";
import { Button } from ":/components/Button"; import { Button } from ":/components/Button";
import {
getFieldState,
getFieldErrorMessage,
onSubmit,
} from ":/tests/reactHookFormUtils";
export default { export default {
title: "Components/Forms/Select/Multi", title: "Components/Forms/Select/Multi",
@@ -208,6 +216,103 @@ export const Error = {
}, },
}; };
export const ReactHookForm = () => {
enum CitiesOptionEnum {
NONE = "",
DIJON = "dijon",
PARIS = "paris",
TOKYO = "tokyo",
VANNES = "vannes",
}
interface SelectExampleFormValues {
capitalCity: CitiesOptionEnum[];
}
const selectExampleSchema = Yup.object().shape({
capitalCity: Yup.array()
.required()
.test({
test: (cityList) =>
cityList.every((city) =>
[CitiesOptionEnum.PARIS, CitiesOptionEnum.TOKYO].includes(city),
),
message: "Wrong answer!",
}),
});
const { handleSubmit, formState, control } = useForm<SelectExampleFormValues>(
{
defaultValues: {
capitalCity: [],
},
mode: "onChange",
reValidateMode: "onChange",
resolver: yupResolver(selectExampleSchema),
},
);
const renderSelect = ({
field,
}: {
field: ControllerRenderProps<SelectExampleFormValues, "capitalCity">;
}) => {
return (
<>
<div>Which are the capital of the country ?</div>
<Select
label="Select a city"
multi={true}
options={[
{
label: "Dijon",
value: CitiesOptionEnum.DIJON,
},
{
label: "Paris",
value: CitiesOptionEnum.PARIS,
},
{
label: "Tokyo",
value: CitiesOptionEnum.TOKYO,
},
{
label: "Vannes",
value: CitiesOptionEnum.VANNES,
},
]}
state={getFieldState("capitalCity", formState)}
text={getFieldErrorMessage("capitalCity", formState)}
onBlur={field.onBlur}
onChange={field.onChange}
value={field.value}
/>
</>
);
};
return (
<CunninghamProvider>
<form
style={{
display: "flex",
flexDirection: "column",
gap: "1rem",
width: "400px",
}}
onSubmit={handleSubmit(onSubmit)}
>
<Controller
control={control}
name="capitalCity"
render={renderSelect}
/>
<Button fullWidth={true}>Submit</Button>
</form>
</CunninghamProvider>
);
};
export const FormExample = () => { export const FormExample = () => {
const handleSubmit = (e: React.FormEvent) => { const handleSubmit = (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();