📝(react) react-hook-form forms Sports examples

Our forms needs to be usable with react-hook-form
This commit is contained in:
Romain Le Cellier
2023-08-01 15:07:30 +02:00
parent d0941ae0a7
commit 5dde74c10d
2 changed files with 182 additions and 52 deletions

View File

@@ -1,20 +1,149 @@
import { yupResolver } from "@hookform/resolvers/yup";
import { Meta } from "@storybook/react"; import { Meta } from "@storybook/react";
import React from "react"; import React from "react";
import { useForm, Controller, ControllerRenderProps } from "react-hook-form";
import * as Yup from "yup";
import { Input } from ":/components/Forms/Input"; import { Input } from ":/components/Forms/Input";
import { Checkbox } from ":/components/Forms/Checkbox";
import { Button } from ":/components/Button"; import { Button } from ":/components/Button";
import { Select } from ":/components/Forms/Select"; import { Select } from ":/components/Forms/Select";
import { CunninghamProvider } from ":/components/Provider"; import { CunninghamProvider } from ":/components/Provider";
import { FileUploader } from ":/components/Forms/FileUploader"; import { Radio, RadioGroup } from ":/components/Forms/Radio";
import { Switch } from ":/components/Forms/Switch"; import {
import { Radio } from ":/components/Forms/Radio"; getFieldState,
getFieldErrorMessage,
onSubmit,
} from ":/tests/reactHookFormUtils";
export default { export default {
title: "Components/Forms/Examples", title: "Components/Forms/Reac-Hook-Form",
} as Meta; } as Meta;
enum GenderEnum {
MALE = "male",
FEMALE = "female",
OTHER = "other",
}
enum CompetitionEnum {
ATHLETICS = "Athletics",
SWIMMING = "Swimming",
MARATHON = "Marathon",
}
enum RewardEnum {
BRONZE = "bronze",
SILVER = "silver",
GOLD = "gold",
FLOCON = "flocon",
OURSON = "ourson",
CHAMOIS = "chamois",
}
interface SportsStoryFormValues {
firstName: string;
lastName: string;
gender: GenderEnum;
competition: CompetitionEnum;
rewards: RewardEnum[];
}
const sportsSchema = Yup.object().shape({
firstName: Yup.string().required(),
lastName: Yup.string().required(),
gender: Yup.string<GenderEnum>().required(),
competition: Yup.string()
.defined()
.required()
.oneOf(Object.values(CompetitionEnum)),
rewards: Yup.array().of(Yup.string<RewardEnum>().defined()).defined(),
});
export const Sports = () => { export const Sports = () => {
const { register, handleSubmit, formState, control } =
useForm<SportsStoryFormValues>({
defaultValues: {
firstName: "",
lastName: "",
rewards: [],
},
mode: "onChange",
reValidateMode: "onChange",
resolver: yupResolver(sportsSchema),
});
const renderCompetitionSelect = ({
field,
}: {
field: ControllerRenderProps<SportsStoryFormValues, "competition">;
}) => {
return (
<Select
label="Competition"
options={[
{
label: "Athletics",
value: CompetitionEnum.ATHLETICS,
},
{
label: "Swimming",
value: CompetitionEnum.SWIMMING,
},
{
label: "Marathon",
value: CompetitionEnum.MARATHON,
},
]}
state={getFieldState("competition", formState)}
text={getFieldErrorMessage("competition", formState)}
onBlur={field.onBlur}
onChange={field.onChange}
value={field.value}
/>
);
};
const renderRewardsMultiSelect = ({
field,
}: {
field: ControllerRenderProps<SportsStoryFormValues, "rewards">;
}) => {
return (
<Select
label="Rewards"
options={[
{
label: "Bronze",
value: RewardEnum.BRONZE,
},
{
label: "Silver",
value: RewardEnum.SILVER,
},
{
label: "Gold",
value: RewardEnum.GOLD,
},
{
label: "Flocon",
value: RewardEnum.FLOCON,
},
{
label: "Ourson",
value: RewardEnum.OURSON,
},
{
label: "Chamois",
value: RewardEnum.CHAMOIS,
},
]}
state={getFieldState("rewards", formState)}
text={getFieldErrorMessage("rewards", formState)}
onBlur={field.onBlur}
onChange={field.onChange}
value={field.value}
multi={true}
/>
);
};
return ( return (
<CunninghamProvider> <CunninghamProvider>
<form <form
@@ -24,6 +153,7 @@ export const Sports = () => {
gap: "1rem", gap: "1rem",
width: "400px", width: "400px",
}} }}
onSubmit={handleSubmit(onSubmit)}
> >
<h1 <h1
className="fs-h3 fw-bold clr-greyscale-900" className="fs-h3 fw-bold clr-greyscale-900"
@@ -32,54 +162,51 @@ export const Sports = () => {
Register Register
</h1> </h1>
<div style={{ display: "flex", gap: "1rem" }}> <div style={{ display: "flex", gap: "1rem" }}>
<Input label="First name" /> <Input
<Input label="Last name" /> label="First name"
state={getFieldState("firstName", formState)}
text={getFieldErrorMessage("firstName", formState)}
{...register("firstName")}
/>
<Input
label="Last name"
state={getFieldState("lastName", formState)}
text={getFieldErrorMessage("lastName", formState)}
{...register("lastName")}
/>
</div> </div>
<div style={{ display: "flex", gap: "0.5rem" }}> <RadioGroup
<Radio name="gender" label="Male" fullWidth={true} /> state={getFieldState("gender", formState)}
<Radio name="gender" label="Female" /> text={getFieldErrorMessage("gender", formState)}
<Radio name="gender" label="Other" /> style={{ display: "flex", flexDirection: "row", gap: "0.5rem" }}
</div> >
<Select <Radio
label="Competition" label="Male"
options={[ fullWidth={true}
{ state={getFieldState("gender", formState)}
label: "Athletics", {...register("gender")}
}, />
{ <Radio
label: "Swimming", label="Female"
}, state={getFieldState("gender", formState)}
{ {...register("gender")}
label: "Marathon", />
}, <Radio
]} label="Other"
fullWidth={true} state={getFieldState("gender", formState)}
{...register("gender")}
/>
</RadioGroup>
<Controller
control={control}
name="competition"
render={renderCompetitionSelect}
/> />
<Select <Controller
label="Previous rewards" control={control}
multi={true} name="rewards"
options={[ render={renderRewardsMultiSelect}
{
label: "Bronze",
},
{
label: "Silver",
},
{
label: "Gold",
},
{
label: "Flocon",
},
{
label: "Ourson",
},
{
label: "Chamois",
},
]}
fullWidth={true}
/> />
<Button fullWidth={true}>Apply</Button> <Button fullWidth={true}>Apply</Button>
<Button fullWidth={true} color="secondary"> <Button fullWidth={true} color="secondary">

View File

@@ -35,7 +35,8 @@ export const RadioGroup = ({
state, state,
text, text,
rightText, rightText,
}: PropsWithChildren & FieldProps) => { style,
}: PropsWithChildren & FieldProps & { style: React.CSSProperties }) => {
return ( return (
<Field <Field
className="c__radio__group c__checkbox__group" className="c__radio__group c__checkbox__group"
@@ -44,7 +45,9 @@ export const RadioGroup = ({
rightText={rightText} rightText={rightText}
compact={true} compact={true}
> >
<div className="c__checkbox__group__list">{children}</div> <div className="c__checkbox__group__list" style={style}>
{children}
</div>
</Field> </Field>
); );
}; };