mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
feat(test): react hook form tests & stories (#2931)
* feat(input): add Input with React Hook Form tests * refactor(input): add missing types * feat(checkbox): add checkbox with React Hook Form tests * feat(select): add react-hook-form to dev dep * feat(select): add react hook form story * feat(select): react hook form tests * fix(select): incorrect button reference * feat(deps): add react-hook-form to dev dep in autocomplete * feat(autocomplete): react hook form story * feat(autocomplete): react hook form tests * fix(autocomplete): rollback wrapper type * feat(switch): add react hook form tests * refactor(stories): reorder stories items
This commit is contained in:
parent
76f4dd8e76
commit
633f9d208b
@ -1,6 +1,7 @@
|
||||
import * as React from "react";
|
||||
import {act, render} from "@testing-library/react";
|
||||
import {render, renderHook, act} from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import {useForm} from "react-hook-form";
|
||||
|
||||
import {Autocomplete, AutocompleteItem, AutocompleteSection} from "../src";
|
||||
import {Modal, ModalContent, ModalBody, ModalHeader, ModalFooter} from "../../modal/src";
|
||||
@ -220,3 +221,105 @@ describe("Autocomplete", () => {
|
||||
expect(autocomplete).toHaveAttribute("aria-expanded", "false");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Autocomplete with React Hook Form", () => {
|
||||
let autocomplete1: HTMLInputElement;
|
||||
let autocomplete2: HTMLInputElement;
|
||||
let autocomplete3: HTMLInputElement;
|
||||
let submitButton: HTMLButtonElement;
|
||||
let wrapper: any;
|
||||
let onSubmit: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
const {result} = renderHook(() =>
|
||||
useForm({
|
||||
defaultValues: {
|
||||
withDefaultValue: "cat",
|
||||
withoutDefaultValue: "",
|
||||
requiredField: "",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
register,
|
||||
formState: {errors},
|
||||
} = result.current;
|
||||
|
||||
onSubmit = jest.fn();
|
||||
|
||||
wrapper = render(
|
||||
<form className="flex w-full max-w-xs flex-col gap-2" onSubmit={handleSubmit(onSubmit)}>
|
||||
<Autocomplete
|
||||
data-testid="autocomplete-1"
|
||||
{...register("withDefaultValue")}
|
||||
aria-label="Favorite Animal"
|
||||
items={itemsData}
|
||||
label="Favorite Animal"
|
||||
>
|
||||
{(item) => <AutocompleteItem key={item.value}>{item.label}</AutocompleteItem>}
|
||||
</Autocomplete>
|
||||
<Autocomplete
|
||||
data-testid="autocomplete-2"
|
||||
{...register("withoutDefaultValue")}
|
||||
aria-label="Favorite Animal"
|
||||
items={itemsData}
|
||||
label="Favorite Animal"
|
||||
>
|
||||
{(item) => <AutocompleteItem key={item.value}>{item.label}</AutocompleteItem>}
|
||||
</Autocomplete>
|
||||
<Autocomplete
|
||||
data-testid="autocomplete-3"
|
||||
{...register("requiredField", {required: true})}
|
||||
aria-label="Favorite Animal"
|
||||
items={itemsData}
|
||||
label="Favorite Animal"
|
||||
>
|
||||
{(item) => <AutocompleteItem key={item.value}>{item.label}</AutocompleteItem>}
|
||||
</Autocomplete>
|
||||
{errors.requiredField && <span className="text-danger">This field is required</span>}
|
||||
<button data-testid="submit-button" type="submit">
|
||||
Submit
|
||||
</button>
|
||||
</form>,
|
||||
);
|
||||
|
||||
autocomplete1 = wrapper.getByTestId("autocomplete-1");
|
||||
autocomplete2 = wrapper.getByTestId("autocomplete-2");
|
||||
autocomplete3 = wrapper.getByTestId("autocomplete-3");
|
||||
submitButton = wrapper.getByTestId("submit-button");
|
||||
});
|
||||
|
||||
it("should work with defaultValues", () => {
|
||||
expect(autocomplete1).toHaveValue("Cat");
|
||||
expect(autocomplete2).toHaveValue("");
|
||||
expect(autocomplete3).toHaveValue("");
|
||||
});
|
||||
|
||||
it("should not submit form when required field is empty", async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
await user.click(submitButton);
|
||||
|
||||
expect(onSubmit).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it("should submit form when required field is not empty", async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
await user.click(autocomplete3);
|
||||
|
||||
expect(autocomplete3).toHaveAttribute("aria-expanded", "true");
|
||||
|
||||
let listboxItems = wrapper.getAllByRole("option");
|
||||
|
||||
await user.click(listboxItems[1]);
|
||||
|
||||
expect(autocomplete3).toHaveValue("Dog");
|
||||
|
||||
await user.click(submitButton);
|
||||
|
||||
expect(onSubmit).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@ -74,7 +74,8 @@
|
||||
"framer-motion": "^11.0.28",
|
||||
"clean-package": "2.2.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
"react-dom": "^18.0.0",
|
||||
"react-hook-form": "^7.51.3"
|
||||
},
|
||||
"clean-package": "../../../clean-package.config.json"
|
||||
}
|
||||
@ -2,6 +2,7 @@ import type {ValidationResult} from "@react-types/shared";
|
||||
|
||||
import React, {Key} from "react";
|
||||
import {Meta} from "@storybook/react";
|
||||
import {useForm} from "react-hook-form";
|
||||
import {autocomplete, input, button} from "@nextui-org/theme";
|
||||
import {
|
||||
Pokemon,
|
||||
@ -686,6 +687,45 @@ const CustomStylesWithCustomItemsTemplate = ({color, ...args}: AutocompleteProps
|
||||
);
|
||||
};
|
||||
|
||||
const WithReactHookFormTemplate = (args: AutocompleteProps) => {
|
||||
const {
|
||||
register,
|
||||
formState: {errors},
|
||||
handleSubmit,
|
||||
} = useForm({
|
||||
defaultValues: {
|
||||
withDefaultValue: "cat",
|
||||
withoutDefaultValue: "",
|
||||
requiredField: "",
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (data: any) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(data);
|
||||
alert("Submitted value: " + JSON.stringify(data));
|
||||
};
|
||||
|
||||
return (
|
||||
<form className="flex w-full max-w-xs flex-col gap-2" onSubmit={handleSubmit(onSubmit)}>
|
||||
<Autocomplete {...args} {...register("withDefaultValue")}>
|
||||
{items}
|
||||
</Autocomplete>
|
||||
<Autocomplete {...args} {...register("withoutDefaultValue")}>
|
||||
{items}
|
||||
</Autocomplete>
|
||||
<Autocomplete {...args} {...register("requiredField", {required: true})}>
|
||||
{items}
|
||||
</Autocomplete>
|
||||
|
||||
{errors.requiredField && <span className="text-danger">This field is required</span>}
|
||||
<button className={button({class: "w-fit"})} type="submit">
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export const Default = {
|
||||
render: Template,
|
||||
args: {
|
||||
@ -733,15 +773,6 @@ export const DisabledOptions = {
|
||||
},
|
||||
};
|
||||
|
||||
export const WithDescription = {
|
||||
render: MirrorTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
description: "Select your favorite animal",
|
||||
},
|
||||
};
|
||||
|
||||
export const LabelPlacement = {
|
||||
render: LabelPlacementTemplate,
|
||||
|
||||
@ -782,6 +813,27 @@ export const EndContent = {
|
||||
},
|
||||
};
|
||||
|
||||
export const IsInvalid = {
|
||||
render: Template,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
isInvalid: true,
|
||||
variant: "bordered",
|
||||
defaultSelectedKey: "dog",
|
||||
errorMessage: "Please select a valid animal",
|
||||
},
|
||||
};
|
||||
|
||||
export const WithDescription = {
|
||||
render: MirrorTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
description: "Select your favorite animal",
|
||||
},
|
||||
};
|
||||
|
||||
export const WithoutScrollShadow = {
|
||||
render: Template,
|
||||
|
||||
@ -847,15 +899,37 @@ export const WithValidation = {
|
||||
},
|
||||
};
|
||||
|
||||
export const IsInvalid = {
|
||||
render: Template,
|
||||
export const WithSections = {
|
||||
render: WithSectionsTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
},
|
||||
};
|
||||
|
||||
export const WithCustomSectionsStyles = {
|
||||
render: WithCustomSectionsStylesTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
},
|
||||
};
|
||||
|
||||
export const WithAriaLabel = {
|
||||
render: WithAriaLabelTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
label: "Select an animal 🐹",
|
||||
"aria-label": "Select an animal",
|
||||
},
|
||||
};
|
||||
|
||||
export const WithReactHookForm = {
|
||||
render: WithReactHookFormTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
isInvalid: true,
|
||||
variant: "bordered",
|
||||
defaultSelectedKey: "dog",
|
||||
errorMessage: "Please select a valid animal",
|
||||
},
|
||||
};
|
||||
|
||||
@ -885,32 +959,6 @@ export const CustomItems = {
|
||||
},
|
||||
};
|
||||
|
||||
export const WithSections = {
|
||||
render: WithSectionsTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
},
|
||||
};
|
||||
|
||||
export const WithCustomSectionsStyles = {
|
||||
render: WithCustomSectionsStylesTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
},
|
||||
};
|
||||
|
||||
export const WithAriaLabel = {
|
||||
render: WithAriaLabelTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
label: "Select an animal 🐹",
|
||||
"aria-label": "Select an animal",
|
||||
},
|
||||
};
|
||||
|
||||
export const CustomStyles = {
|
||||
render: CustomStylesTemplate,
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import * as React from "react";
|
||||
import {render, act} from "@testing-library/react";
|
||||
import {render, renderHook, act} from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import {useForm} from "react-hook-form";
|
||||
|
||||
import {Checkbox, CheckboxProps} from "../src";
|
||||
|
||||
@ -128,3 +129,74 @@ describe("Checkbox", () => {
|
||||
expect(onChange).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Checkbox with React Hook Form", () => {
|
||||
let checkbox1: HTMLInputElement;
|
||||
let checkbox2: HTMLInputElement;
|
||||
let checkbox3: HTMLInputElement;
|
||||
let submitButton: HTMLButtonElement;
|
||||
let onSubmit: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
const {result} = renderHook(() =>
|
||||
useForm({
|
||||
defaultValues: {
|
||||
withDefaultValue: true,
|
||||
withoutDefaultValue: false,
|
||||
requiredField: false,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
register,
|
||||
formState: {errors},
|
||||
} = result.current;
|
||||
|
||||
onSubmit = jest.fn();
|
||||
|
||||
render(
|
||||
<form className="flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}>
|
||||
<Checkbox {...register("withDefaultValue")} />
|
||||
<Checkbox {...register("withoutDefaultValue")} />
|
||||
<Checkbox {...register("requiredField", {required: true})} />
|
||||
{errors.requiredField && <span className="text-danger">This field is required</span>}
|
||||
<button type="submit">Submit</button>
|
||||
</form>,
|
||||
);
|
||||
|
||||
checkbox1 = document.querySelector("input[name=withDefaultValue]")!;
|
||||
checkbox2 = document.querySelector("input[name=withoutDefaultValue]")!;
|
||||
checkbox3 = document.querySelector("input[name=requiredField]")!;
|
||||
submitButton = document.querySelector("button")!;
|
||||
});
|
||||
|
||||
it("should work with defaultValues", () => {
|
||||
expect(checkbox1.checked).toBe(true);
|
||||
expect(checkbox2.checked).toBe(false);
|
||||
expect(checkbox3.checked).toBe(false);
|
||||
});
|
||||
|
||||
it("should not submit form when required field is empty", async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
await user.click(submitButton);
|
||||
|
||||
expect(onSubmit).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it("should submit form when required field is not empty", async () => {
|
||||
act(() => {
|
||||
checkbox3.click();
|
||||
});
|
||||
|
||||
expect(checkbox3.checked).toBe(true);
|
||||
|
||||
const user = userEvent.setup();
|
||||
|
||||
await user.click(submitButton);
|
||||
|
||||
expect(onSubmit).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import * as React from "react";
|
||||
import {render} from "@testing-library/react";
|
||||
import {render, renderHook, fireEvent} from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import {useForm} from "react-hook-form";
|
||||
|
||||
import {Input} from "../src";
|
||||
|
||||
@ -146,3 +147,78 @@ describe("Input", () => {
|
||||
expect(onClear).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Input with React Hook Form", () => {
|
||||
let input1: HTMLInputElement;
|
||||
let input2: HTMLInputElement;
|
||||
let input3: HTMLInputElement;
|
||||
let submitButton: HTMLButtonElement;
|
||||
let onSubmit: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
const {result} = renderHook(() =>
|
||||
useForm({
|
||||
defaultValues: {
|
||||
withDefaultValue: "wkw",
|
||||
withoutDefaultValue: "",
|
||||
requiredField: "",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
register,
|
||||
formState: {errors},
|
||||
} = result.current;
|
||||
|
||||
onSubmit = jest.fn();
|
||||
|
||||
render(
|
||||
<form className="flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}>
|
||||
<Input isClearable label="With default value" {...register("withDefaultValue")} />
|
||||
<Input
|
||||
data-testid="input-2"
|
||||
label="Without default value"
|
||||
{...register("withoutDefaultValue")}
|
||||
/>
|
||||
<Input
|
||||
data-testid="input-3"
|
||||
label="Required"
|
||||
{...register("requiredField", {required: true})}
|
||||
/>
|
||||
{errors.requiredField && <span className="text-danger">This field is required</span>}
|
||||
<button type="submit">Submit</button>
|
||||
</form>,
|
||||
);
|
||||
|
||||
input1 = document.querySelector("input[name=withDefaultValue]")!;
|
||||
input2 = document.querySelector("input[name=withoutDefaultValue]")!;
|
||||
input3 = document.querySelector("input[name=requiredField]")!;
|
||||
submitButton = document.querySelector("button")!;
|
||||
});
|
||||
|
||||
it("should work with defaultValues", () => {
|
||||
expect(input1).toHaveValue("wkw");
|
||||
expect(input2).toHaveValue("");
|
||||
expect(input3).toHaveValue("");
|
||||
});
|
||||
|
||||
it("should not submit form when required field is empty", async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
await user.click(submitButton);
|
||||
|
||||
expect(onSubmit).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it("should submit form when required field is not empty", async () => {
|
||||
fireEvent.change(input3, {target: {value: "updated"}});
|
||||
|
||||
const user = userEvent.setup();
|
||||
|
||||
await user.click(submitButton);
|
||||
|
||||
expect(onSubmit).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import * as React from "react";
|
||||
import {act, render} from "@testing-library/react";
|
||||
import {render, renderHook, act} from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import {useForm} from "react-hook-form";
|
||||
|
||||
import {Select, SelectItem, SelectSection, type SelectProps} from "../src";
|
||||
import {Modal, ModalContent, ModalHeader, ModalBody, ModalFooter} from "../../modal/src";
|
||||
@ -397,3 +398,94 @@ describe("Select", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Select with React Hook Form", () => {
|
||||
let select1: HTMLElement;
|
||||
let select2: HTMLElement;
|
||||
let select3: HTMLElement;
|
||||
let submitButton: HTMLButtonElement;
|
||||
let wrapper: any;
|
||||
let onSubmit: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
const {result} = renderHook(() =>
|
||||
useForm({
|
||||
defaultValues: {
|
||||
withDefaultValue: "cat",
|
||||
withoutDefaultValue: "",
|
||||
requiredField: "",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const {
|
||||
register,
|
||||
formState: {errors},
|
||||
handleSubmit,
|
||||
} = result.current;
|
||||
|
||||
onSubmit = jest.fn();
|
||||
|
||||
wrapper = render(
|
||||
<form className="flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}>
|
||||
<Select data-testid="select-1" items={itemsData} {...register("withDefaultValue")}>
|
||||
{(item) => <SelectItem key={item.value}>{item.label}</SelectItem>}
|
||||
</Select>
|
||||
|
||||
<Select data-testid="select-2" items={itemsData} {...register("withoutDefaultValue")}>
|
||||
{(item) => <SelectItem key={item.value}>{item.label}</SelectItem>}
|
||||
</Select>
|
||||
|
||||
<Select
|
||||
data-testid="select-3"
|
||||
items={itemsData}
|
||||
{...register("requiredField", {required: true})}
|
||||
>
|
||||
{(item) => <SelectItem key={item.value}>{item.label}</SelectItem>}
|
||||
</Select>
|
||||
|
||||
{errors.requiredField && <span className="text-danger">This field is required</span>}
|
||||
<button data-testid="submit-button" type="submit">
|
||||
Submit
|
||||
</button>
|
||||
</form>,
|
||||
);
|
||||
|
||||
select1 = wrapper.getByTestId("select-1");
|
||||
select2 = wrapper.getByTestId("select-2");
|
||||
select3 = wrapper.getByTestId("select-3");
|
||||
submitButton = wrapper.getByTestId("submit-button");
|
||||
});
|
||||
|
||||
it("should work with defaultValues", () => {
|
||||
expect(select1).toHaveTextContent("Cat");
|
||||
expect(select2).toHaveTextContent("");
|
||||
expect(select3).toHaveTextContent("");
|
||||
});
|
||||
|
||||
it("should not submit form when required field is empty", async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
await user.click(submitButton);
|
||||
|
||||
expect(onSubmit).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it("should submit form when required field is not empty", async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
await user.click(select3);
|
||||
|
||||
expect(select3).toHaveAttribute("aria-expanded", "true");
|
||||
|
||||
let listboxItems = wrapper.getAllByRole("option");
|
||||
|
||||
await user.click(listboxItems[1]);
|
||||
|
||||
expect(select3).toHaveTextContent("Dog");
|
||||
|
||||
await user.click(submitButton);
|
||||
|
||||
expect(onSubmit).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@ -73,7 +73,8 @@
|
||||
"@react-stately/data": "3.11.2",
|
||||
"clean-package": "2.2.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
"react-dom": "^18.0.0",
|
||||
"react-hook-form": "^7.51.3"
|
||||
},
|
||||
"clean-package": "../../../clean-package.config.json"
|
||||
}
|
||||
@ -2,6 +2,7 @@
|
||||
import type {ValidationResult} from "@react-types/shared";
|
||||
|
||||
import React, {ChangeEvent} from "react";
|
||||
import {useForm} from "react-hook-form";
|
||||
import {Meta} from "@storybook/react";
|
||||
import {select, button} from "@nextui-org/theme";
|
||||
import {PetBoldIcon, SelectorIcon} from "@nextui-org/shared-icons";
|
||||
@ -585,6 +586,47 @@ const AsyncLoadingTemplate = ({color, variant, ...args}: SelectProps<Pokemon>) =
|
||||
);
|
||||
};
|
||||
|
||||
const WithReactHookFormTemplate = (args: SelectProps) => {
|
||||
const {
|
||||
register,
|
||||
formState: {errors},
|
||||
handleSubmit,
|
||||
} = useForm({
|
||||
defaultValues: {
|
||||
withDefaultValue: "cat",
|
||||
withoutDefaultValue: "",
|
||||
requiredField: "",
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (data: any) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(data);
|
||||
alert("Submitted value: " + JSON.stringify(data));
|
||||
};
|
||||
|
||||
return (
|
||||
<form className="flex w-full max-w-xs flex-col gap-2" onSubmit={handleSubmit(onSubmit)}>
|
||||
<Select data-testid="select-1" {...args} {...register("withDefaultValue")}>
|
||||
{items}
|
||||
</Select>
|
||||
|
||||
<Select data-testid="select-2" {...args} {...register("withoutDefaultValue")}>
|
||||
{items}
|
||||
</Select>
|
||||
|
||||
<Select data-testid="select-3" {...args} {...register("requiredField", {required: true})}>
|
||||
{items}
|
||||
</Select>
|
||||
|
||||
{errors.requiredField && <span className="text-danger">This field is required</span>}
|
||||
<button className={button({class: "w-fit"})} type="submit">
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export const Default = {
|
||||
render: MirrorTemplate,
|
||||
|
||||
@ -631,23 +673,15 @@ export const DisabledOptions = {
|
||||
},
|
||||
};
|
||||
|
||||
export const WithDescription = {
|
||||
render: MirrorTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
description: "Select your favorite animal",
|
||||
},
|
||||
};
|
||||
|
||||
export const WithoutLabel = {
|
||||
export const IsInvalid = {
|
||||
render: Template,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
label: null,
|
||||
"aria-label": "Select an animal",
|
||||
placeholder: "Select an animal",
|
||||
isInvalid: true,
|
||||
variant: "bordered",
|
||||
defaultSelectedKeys: ["dog"],
|
||||
errorMessage: "Please select a valid animal",
|
||||
},
|
||||
};
|
||||
|
||||
@ -675,6 +709,26 @@ export const StartContent = {
|
||||
},
|
||||
};
|
||||
|
||||
export const WithDescription = {
|
||||
render: MirrorTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
description: "Select your favorite animal",
|
||||
},
|
||||
};
|
||||
|
||||
export const WithoutLabel = {
|
||||
render: Template,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
label: null,
|
||||
"aria-label": "Select an animal",
|
||||
placeholder: "Select an animal",
|
||||
},
|
||||
};
|
||||
|
||||
export const WithoutScrollShadow = {
|
||||
render: Template,
|
||||
|
||||
@ -726,15 +780,62 @@ export const WithErrorMessageFunction = {
|
||||
},
|
||||
};
|
||||
|
||||
export const IsInvalid = {
|
||||
render: Template,
|
||||
export const WithChips = {
|
||||
render: CustomItemsTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
isInvalid: true,
|
||||
variant: "bordered",
|
||||
defaultSelectedKeys: ["dog"],
|
||||
errorMessage: "Please select a valid animal",
|
||||
selectionMode: "multiple",
|
||||
isMultiline: true,
|
||||
labelPlacement: "outside",
|
||||
classNames: {
|
||||
base: "max-w-xs",
|
||||
trigger: "min-h-12 py-2",
|
||||
},
|
||||
renderValue: (items: SelectedItems<User>) => {
|
||||
return (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{items.map((item) => (
|
||||
<Chip key={item.key}>{item.data?.name}</Chip>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithSections = {
|
||||
render: WithSectionsTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
},
|
||||
};
|
||||
|
||||
export const WithCustomSectionsStyles = {
|
||||
render: WithCustomSectionsStylesTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
},
|
||||
};
|
||||
|
||||
export const WithAriaLabel = {
|
||||
render: WithAriaLabelTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
label: "Select an animal 🐹",
|
||||
"aria-label": "Select an animal",
|
||||
},
|
||||
};
|
||||
|
||||
export const WithReactHookForm = {
|
||||
render: WithReactHookFormTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
},
|
||||
};
|
||||
|
||||
@ -808,57 +909,6 @@ export const CustomRenderValue = {
|
||||
},
|
||||
};
|
||||
|
||||
export const WithChips = {
|
||||
render: CustomItemsTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
variant: "bordered",
|
||||
selectionMode: "multiple",
|
||||
isMultiline: true,
|
||||
labelPlacement: "outside",
|
||||
classNames: {
|
||||
base: "max-w-xs",
|
||||
trigger: "min-h-12 py-2",
|
||||
},
|
||||
renderValue: (items: SelectedItems<User>) => {
|
||||
return (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{items.map((item) => (
|
||||
<Chip key={item.key}>{item.data?.name}</Chip>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithSections = {
|
||||
render: WithSectionsTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
},
|
||||
};
|
||||
|
||||
export const WithCustomSectionsStyles = {
|
||||
render: WithCustomSectionsStylesTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
},
|
||||
};
|
||||
|
||||
export const WithAriaLabel = {
|
||||
render: WithAriaLabelTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
label: "Select an animal 🐹",
|
||||
"aria-label": "Select an animal",
|
||||
},
|
||||
};
|
||||
|
||||
export const CustomStyles = {
|
||||
render: CustomStylesTemplate,
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import * as React from "react";
|
||||
import {act, render} from "@testing-library/react";
|
||||
import {render, renderHook, act} from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import {useForm} from "react-hook-form";
|
||||
|
||||
import {Switch} from "../src";
|
||||
|
||||
@ -11,7 +13,7 @@ describe("Switch", () => {
|
||||
});
|
||||
|
||||
it("ref should be forwarded", () => {
|
||||
const ref = React.createRef<HTMLDivElement>();
|
||||
const ref = React.createRef<HTMLInputElement>();
|
||||
|
||||
render(<Switch ref={ref} aria-label="switch" />);
|
||||
expect(ref.current).not.toBeNull();
|
||||
@ -198,3 +200,74 @@ describe("Switch", () => {
|
||||
expect(wrapper.getByTestId("end-icon")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Switch with React Hook Form", () => {
|
||||
let switch1: HTMLInputElement;
|
||||
let switch2: HTMLInputElement;
|
||||
let switch3: HTMLInputElement;
|
||||
let submitButton: HTMLButtonElement;
|
||||
let onSubmit: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
const {result} = renderHook(() =>
|
||||
useForm({
|
||||
defaultValues: {
|
||||
defaultTrue: true,
|
||||
defaultFalse: false,
|
||||
requiredField: false,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const {
|
||||
register,
|
||||
formState: {errors},
|
||||
handleSubmit,
|
||||
} = result.current;
|
||||
|
||||
onSubmit = jest.fn();
|
||||
|
||||
render(
|
||||
<form className="flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}>
|
||||
<Switch {...register("defaultTrue")}>By default this switch is true</Switch>
|
||||
<Switch {...register("defaultFalse")}>By default this switch is false</Switch>
|
||||
<Switch {...register("requiredField", {required: true})}>This switch is required</Switch>
|
||||
{errors.requiredField && <span className="text-danger">This switch is required</span>}
|
||||
<button type="submit">Submit</button>
|
||||
</form>,
|
||||
);
|
||||
|
||||
switch1 = document.querySelector("input[name=defaultTrue]")!;
|
||||
switch2 = document.querySelector("input[name=defaultFalse]")!;
|
||||
switch3 = document.querySelector("input[name=requiredField]")!;
|
||||
submitButton = document.querySelector("button")!;
|
||||
});
|
||||
|
||||
it("should work with defaultValues", () => {
|
||||
expect(switch1.checked).toBe(true);
|
||||
expect(switch2.checked).toBe(false);
|
||||
expect(switch3.checked).toBe(false);
|
||||
});
|
||||
|
||||
it("should not submit form when required field is empty", async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
await user.click(submitButton);
|
||||
|
||||
expect(onSubmit).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it("should submit form when required field is not empty", async () => {
|
||||
act(() => {
|
||||
switch3.click();
|
||||
});
|
||||
|
||||
expect(switch3.checked).toBe(true);
|
||||
|
||||
const user = userEvent.setup();
|
||||
|
||||
await user.click(submitButton);
|
||||
|
||||
expect(onSubmit).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@ -221,6 +221,14 @@ export const WithIcons = {
|
||||
},
|
||||
};
|
||||
|
||||
export const WithReactHookForm = {
|
||||
render: WithReactHookFormTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
},
|
||||
};
|
||||
|
||||
export const Controlled = {
|
||||
render: ControlledTemplate,
|
||||
|
||||
@ -244,11 +252,3 @@ export const CustomWithHooks = {
|
||||
...defaultProps,
|
||||
},
|
||||
};
|
||||
|
||||
export const WithReactHookForm = {
|
||||
render: WithReactHookFormTemplate,
|
||||
|
||||
args: {
|
||||
...defaultProps,
|
||||
},
|
||||
};
|
||||
|
||||
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
@ -783,6 +783,9 @@ importers:
|
||||
react-dom:
|
||||
specifier: ^18.2.0
|
||||
version: 18.2.0(react@18.2.0)
|
||||
react-hook-form:
|
||||
specifier: ^7.51.3
|
||||
version: 7.51.3(react@18.2.0)
|
||||
|
||||
packages/components/avatar:
|
||||
dependencies:
|
||||
@ -2311,6 +2314,9 @@ importers:
|
||||
react-dom:
|
||||
specifier: ^18.2.0
|
||||
version: 18.2.0(react@18.2.0)
|
||||
react-hook-form:
|
||||
specifier: ^7.51.3
|
||||
version: 7.51.3(react@18.2.0)
|
||||
|
||||
packages/components/skeleton:
|
||||
dependencies:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user