Files
cunningham/packages/react/src/components/Forms/Select/multi.stories.tsx
Nathan Vasse b86ba5cc8e (react) add select multi options custom render
We want to be able to render the options in a customized manner.
2023-10-19 11:35:19 +02:00

477 lines
10 KiB
TypeScript

import React, { useRef, useState } from "react";
import { useForm, FormProvider } 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 { onSubmit } from ":/components/Forms/Examples/ReactHookForm/reactHookFormUtils";
import { Select, SelectHandle } from ":/components/Forms/Select";
import { Button } from ":/components/Button";
import {
getCountryOption,
RhfSelect,
} from ":/components/Forms/Select/stories-utils";
export default {
title: "Components/Forms/Select/Multi",
component: Select,
} as Meta<typeof Select>;
const Template: StoryFn<typeof Select> = (args) => {
return (
<div style={{ paddingBottom: "200px" }}>
<Select {...args} multi={true} />
</div>
);
};
const CITIES = Array.from({ length: 10 }).map(() => faker.location.city());
const OPTIONS = CITIES.map((city) => ({
label: city,
value: city.toLowerCase(),
}));
export const Uncontrolled = {
render: Template,
args: {
label: "Select cities",
options: OPTIONS,
defaultValue: [OPTIONS[4].value],
},
};
export const Disabled = {
render: Template,
args: {
label: "Select cities",
options: OPTIONS,
defaultValue: OPTIONS[4].value,
disabled: true,
},
};
export const WithText = {
render: Template,
args: {
label: "Select cities",
options: OPTIONS,
defaultValue: OPTIONS[4].value,
text: "This is a text, you can display anything you want here like warnings, information or errors.",
},
};
export const HiddenLabel = {
render: Template,
args: {
label: "Select cities",
hideLabel: true,
options: OPTIONS,
defaultValue: OPTIONS[4].value,
},
};
export const Controlled = () => {
const [value, setValue] = useState([OPTIONS[6].value, OPTIONS[8].value]);
return (
<div>
<div className="clr-greyscale-900">
Value: <span>{JSON.stringify(value)}</span>
</div>
<Select
label="Select cities"
multi={true}
options={OPTIONS}
value={value}
onChange={(e) => setValue(e.target.value as string[])}
/>
<Button onClick={() => setValue([])}>Reset</Button>
</div>
);
};
export const Overflow = {
render: Template,
args: {
label: "Select cities",
options: [
{
value: "1",
label: "Very long long long long long long long city name",
},
],
defaultValue: "1",
},
};
export const SearchableEmpty = {
render: Template,
args: {
label: "Select cities",
options: OPTIONS,
searchable: true,
},
};
export const SearchableUncontrolled = {
render: Template,
args: {
label: "Select cities",
options: OPTIONS,
defaultValue: OPTIONS[4].value,
searchable: true,
},
};
export const SearchableDisabled = {
render: Template,
args: {
label: "Select cities",
options: OPTIONS,
defaultValue: OPTIONS[4].value,
searchable: true,
disabled: true,
},
};
export const SearchableControlled = () => {
const [value, setValue] = useState([OPTIONS[6].value, OPTIONS[8].value]);
return (
<div>
<div className="clr-greyscale-900">
Value: <span>{JSON.stringify(value)}</span>
</div>
<Select
label="Select cities"
options={OPTIONS}
searchable={true}
multi={true}
value={value}
onChange={(e) => setValue(e.target.value as string[])}
/>
<Button onClick={() => setValue([])}>Reset</Button>
</div>
);
};
export const FullWidth = {
render: Template,
args: {
label: "Select cities",
options: OPTIONS,
defaultValue: OPTIONS[4].value,
searchable: true,
fullWidth: true,
},
};
export const NotClearable = {
render: Template,
args: {
label: "Select cities",
options: OPTIONS,
defaultValue: OPTIONS[4].value,
clearable: false,
},
};
export const DisabledOptions = {
render: Template,
args: {
label: "Select cities",
options: OPTIONS.map((option, i) => ({ ...option, disabled: i % 3 === 0 })),
},
};
export const SearchableDisabledOptions = {
render: Template,
args: {
searchable: true,
label: "Select cities",
options: OPTIONS.map((option, i) => ({ ...option, disabled: i % 3 === 0 })),
},
};
export const Success = {
render: Template,
args: {
label: "Select cities",
options: OPTIONS,
state: "success",
text: "Well done",
},
};
export const Error = {
render: Template,
args: {
label: "Select cities",
options: OPTIONS,
state: "error",
text: "Something went wrong",
},
};
export const NoOptions = {
render: Template,
args: {
label: "No options available",
options: [],
},
};
export const CustomRender = {
render: Template,
args: {
label: "Select a country",
showLabelWhenSelected: false,
options: [
getCountryOption("Germany", "DE"),
getCountryOption("France", "FR"),
getCountryOption("United States", "US"),
getCountryOption("Spain", "ES"),
getCountryOption("China", "CN"),
],
},
};
export const SearchableCustomRender = {
render: Template,
args: {
label: "Select a country",
showLabelWhenSelected: false,
searchable: true,
defaultValue: ["france", "united states"],
options: [
getCountryOption("Germany", "DE"),
getCountryOption("France", "FR"),
getCountryOption("United States", "US"),
getCountryOption("Spain", "ES"),
getCountryOption("China", "CN"),
],
},
};
export const Ref = () => {
const ref = useRef<SelectHandle>(null);
return (
<>
<div className="pb-s">
<Button
onClick={() =>
setTimeout(() => {
// eslint-disable-next-line no-console
console.log("calling blur() ...");
ref.current?.blur();
}, 2000)
}
>
Trigger onBlur in 2 seconds
</Button>
</div>
<Select label="Select a city" options={OPTIONS} multi={true} ref={ref} />
</>
);
};
export const SearchableRef = () => {
const ref = useRef<SelectHandle>(null);
return (
<>
<div className="pb-s">
<Button
onClick={() =>
setTimeout(() => {
// eslint-disable-next-line no-console
console.log("calling blur() ...");
ref.current?.blur();
}, 2000)
}
>
Trigger onBlur in 2 seconds
</Button>
</div>
<Select
label="Select a city"
options={OPTIONS}
multi={true}
searchable={true}
ref={ref}
/>
</>
);
};
export const FormExample = () => {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const data = new FormData(e.target as any);
// eslint-disable-next-line no-console
console.log(data.getAll("cities"));
// eslint-disable-next-line no-console
console.log(data.getAll("test"));
};
return (
<form onSubmit={handleSubmit}>
<div className="mb-s">
<Select
label="Your favorite cities"
name="cities"
options={OPTIONS}
text="The cities you love the most"
state="success"
defaultValue={[OPTIONS[4].value]}
multi={true}
/>
</div>
<div className="mb-s">
<Select
label="Your departments"
name="departments"
searchable={true}
multi={true}
options={[
{
label: "Legal",
},
{
label: "Tech",
},
{
label: "AI",
},
{
label: "Accounting",
},
]}
/>
</div>
<div className="mb-s">
<Select
label="Your skills"
text="Any error you want"
name="skills"
multi={true}
options={[
{
label: "Front-end",
},
{
label: "Back-end",
},
{
label: "Full-stack",
},
]}
state="error"
/>
</div>
<div className="mb-s">
<Select
label="Your options"
text="This field is disabled"
name="grade"
disabled={true}
multi={true}
options={[
{
label: "Bronze",
},
{
label: "Silver",
},
{
label: "Gold",
},
{
label: "Platinum",
},
]}
defaultValue={["Platinum", "Gold"]}
/>
</div>
<Button>Submit</Button>
</form>
);
};
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 methods = useForm<SelectExampleFormValues>({
defaultValues: {
capitalCity: [],
},
mode: "onChange",
reValidateMode: "onChange",
resolver: yupResolver(selectExampleSchema),
});
return (
<FormProvider {...methods}>
<form
style={{
display: "flex",
flexDirection: "column",
gap: "1rem",
width: "400px",
}}
onSubmit={methods.handleSubmit(onSubmit)}
>
<div className="clr-greyscale-900">
Which cities are capital of countries ?
</div>
<RhfSelect
name="capitalCity"
label="Select a city"
multi={true}
fullWidth={true}
options={[
{
label: "Dijon",
value: CitiesOptionEnum.DIJON,
},
{
label: "Paris",
value: CitiesOptionEnum.PARIS,
},
{
label: "Tokyo",
value: CitiesOptionEnum.TOKYO,
},
{
label: "Vannes",
value: CitiesOptionEnum.VANNES,
},
]}
/>
<Button fullWidth={true}>Submit</Button>
</form>
</FormProvider>
);
};