✨(react) react-hook-form Select example
Our form elements needs to be usable with react-hook-form
This commit is contained in:
@@ -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. */}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user