(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,
disabled,
clearable = true,
onBlur,
}: SelectAuxProps) => {
const { t } = useCunningham();
const labelProps = downshiftReturn.getLabelProps();
@@ -101,6 +102,10 @@ export const SelectMonoAux = ({
"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 */}
{/* 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"/>
</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
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 React, { useState } from "react";
import { useForm, Controller, ControllerRenderProps } from "react-hook-form";
import * as Yup from "yup";
import { faker } from "@faker-js/faker";
import { yupResolver } from "@hookform/resolvers/yup";
import { Select } from ":/components/Forms/Select";
import { Button } from ":/components/Button";
import { CunninghamProvider } from ":/components/Provider";
import {
getFieldState,
getFieldErrorMessage,
onSubmit,
} from ":/tests/reactHookFormUtils";
export default {
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 = {
render: Template,

View File

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

View File

@@ -97,6 +97,14 @@ using the component in a controlled way.
<Story id="components-forms-select-multi--controlled"/>
</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
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 { 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 { faker } from "@faker-js/faker";
import { Select } from ":/components/Forms/Select";
import { CunninghamProvider } from ":/components/Provider";
import { Button } from ":/components/Button";
import {
getFieldState,
getFieldErrorMessage,
onSubmit,
} from ":/tests/reactHookFormUtils";
export default {
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 = () => {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();