Prettier 3.0.0 comes with new standards so we need to upgrade our files to comply with it.
1237 lines
31 KiB
TypeScript
1237 lines
31 KiB
TypeScript
import userEvent from "@testing-library/user-event";
|
|
import { render, screen, waitFor } from "@testing-library/react";
|
|
import React, { FormEvent, useState } from "react";
|
|
import { expect } from "vitest";
|
|
import { within } from "@testing-library/dom";
|
|
import { CunninghamProvider } from ":/components/Provider";
|
|
import { Select } from ":/components/Forms/Select/index";
|
|
import {
|
|
expectMenuToBeClosed,
|
|
expectMenuToBeOpen,
|
|
expectOptions,
|
|
expectOptionToBeDisabled,
|
|
expectOptionToBeUnselected,
|
|
expectSelectedOptions,
|
|
} from ":/components/Forms/Select/test-utils";
|
|
import { Button } from ":/components/Button";
|
|
|
|
describe("<Select multi={true} />", () => {
|
|
describe("Simple", async () => {
|
|
it("should select options and clear them one by one", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
},
|
|
{
|
|
label: "Panama",
|
|
},
|
|
{
|
|
label: "London",
|
|
},
|
|
{
|
|
label: "New York",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
},
|
|
]}
|
|
multi={true}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
const input = screen.getByRole("combobox", {
|
|
name: "Cities",
|
|
});
|
|
const menu: HTMLDivElement = screen.getByRole("listbox", {
|
|
name: "Cities",
|
|
});
|
|
const label = screen.getByText("Cities");
|
|
|
|
// Expect no options to be selected.
|
|
expectSelectedOptions([]);
|
|
|
|
// Make sure the label is set as placeholder.
|
|
expect(Array.from(label.classList)).toContain("placeholder");
|
|
|
|
await user.click(input);
|
|
|
|
// Make sure the menu is opened and options are rendered.
|
|
expectMenuToBeOpen(menu);
|
|
expect(
|
|
screen.queryByRole("option", { name: "Paris" }),
|
|
).toBeInTheDocument();
|
|
|
|
// Make sure the option is not selected.
|
|
const option: HTMLLIElement = screen.getByRole("option", {
|
|
name: "London",
|
|
});
|
|
expectOptionToBeUnselected(option);
|
|
|
|
// Select an option.
|
|
await user.click(option);
|
|
|
|
// Make sure the option is selected.
|
|
expectSelectedOptions(["London"]);
|
|
|
|
// Make sure the menu stays open.
|
|
expectMenuToBeOpen(menu);
|
|
|
|
// Make sure the option is removed from the menu.
|
|
expect(
|
|
screen.queryByRole("option", { name: "London" }),
|
|
).not.toBeInTheDocument();
|
|
|
|
// Select other options.
|
|
await user.click(
|
|
screen.getByRole("option", {
|
|
name: "Tokyo",
|
|
}),
|
|
);
|
|
await user.click(
|
|
screen.getByRole("option", {
|
|
name: "Panama",
|
|
}),
|
|
);
|
|
|
|
await waitFor(() => expectSelectedOptions(["London", "Tokyo", "Panama"]));
|
|
|
|
// Clear one option.
|
|
await user.click(
|
|
within(
|
|
screen.getByText("Panama").parentNode as HTMLDivElement,
|
|
).getByRole("button", {
|
|
name: "Clear selection",
|
|
}),
|
|
);
|
|
await waitFor(() => expectSelectedOptions(["London", "Tokyo"]));
|
|
|
|
// Clear one option.
|
|
await user.click(
|
|
within(
|
|
screen.getByText("London").parentNode as HTMLDivElement,
|
|
).getByRole("button", {
|
|
name: "Clear selection",
|
|
}),
|
|
);
|
|
await waitFor(() => expectSelectedOptions(["Tokyo"]));
|
|
|
|
// Clear one option.
|
|
await user.click(
|
|
within(
|
|
screen.getByText("Tokyo").parentNode as HTMLDivElement,
|
|
).getByRole("button", {
|
|
name: "Clear selection",
|
|
}),
|
|
);
|
|
expectSelectedOptions([]);
|
|
});
|
|
|
|
it("should clear all selected options", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
},
|
|
{
|
|
label: "Panama",
|
|
},
|
|
{
|
|
label: "London",
|
|
},
|
|
{
|
|
label: "New York",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
},
|
|
]}
|
|
multi={true}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
const input = screen.getByRole("combobox", {
|
|
name: "Cities",
|
|
});
|
|
await user.click(input);
|
|
|
|
// Select options.
|
|
await user.click(
|
|
screen.getByRole("option", {
|
|
name: "London",
|
|
}),
|
|
);
|
|
await user.click(
|
|
screen.getByRole("option", {
|
|
name: "Tokyo",
|
|
}),
|
|
);
|
|
await user.click(
|
|
screen.getByRole("option", {
|
|
name: "Panama",
|
|
}),
|
|
);
|
|
expectSelectedOptions(["London", "Tokyo", "Panama"]);
|
|
|
|
// Clear selection.
|
|
const clearButton = screen.getByRole("button", {
|
|
name: "Clear all selections",
|
|
});
|
|
await user.click(clearButton);
|
|
expectSelectedOptions([]);
|
|
});
|
|
|
|
it("should select with defaultValue using label", async () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
},
|
|
{
|
|
label: "London",
|
|
},
|
|
{
|
|
label: "New York",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
},
|
|
]}
|
|
defaultValue={["Tokyo", "New York"]}
|
|
multi={true}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
expectSelectedOptions(["New York", "Tokyo"]);
|
|
});
|
|
|
|
it("should select with defaultValue using value", async () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
value: "paris",
|
|
},
|
|
{
|
|
label: "London",
|
|
value: "london",
|
|
},
|
|
{
|
|
label: "New York",
|
|
value: "new_york",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
value: "tokyo",
|
|
},
|
|
]}
|
|
defaultValue={["tokyo", "new_york"]}
|
|
multi={true}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
expectSelectedOptions(["New York", "Tokyo"]);
|
|
});
|
|
|
|
it("works controlled", async () => {
|
|
const Wrapper = () => {
|
|
const [value, setValue] = useState<string[]>(["london"]);
|
|
return (
|
|
<CunninghamProvider>
|
|
<div>
|
|
<div>Value = {JSON.stringify(value)}|</div>
|
|
<Button onClick={() => setValue([])}>Clear</Button>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
value: "paris",
|
|
},
|
|
{
|
|
label: "London",
|
|
value: "london",
|
|
},
|
|
{
|
|
label: "New York",
|
|
value: "new_york",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
value: "tokyo",
|
|
},
|
|
]}
|
|
multi={true}
|
|
value={value}
|
|
onChange={(e) => setValue(e.target.value as string[])}
|
|
/>
|
|
</div>
|
|
</CunninghamProvider>
|
|
);
|
|
};
|
|
render(<Wrapper />);
|
|
|
|
const input = screen.getByRole("combobox", {
|
|
name: "Cities",
|
|
});
|
|
|
|
// Make sure value is selected.
|
|
screen.getByText('Value = ["london"]|');
|
|
|
|
// Change value.
|
|
const user = userEvent.setup();
|
|
await user.click(input);
|
|
|
|
// Make sure the option is removed from the menu.
|
|
expect(
|
|
screen.queryByRole("option", { name: "London" }),
|
|
).not.toBeInTheDocument();
|
|
|
|
// Select an option.
|
|
await user.click(
|
|
screen.getByRole("option", {
|
|
name: "New York",
|
|
}),
|
|
);
|
|
|
|
// Make sure value is selected.
|
|
screen.getByText('Value = ["london","new_york"]|');
|
|
|
|
// clear value.
|
|
const button = screen.getByRole("button", {
|
|
name: "Clear",
|
|
});
|
|
await user.click(button);
|
|
|
|
// Make sure value is cleared.
|
|
screen.getByText("Value = []|");
|
|
});
|
|
|
|
it("renders disabled", async () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
value: "paris",
|
|
},
|
|
{
|
|
label: "London",
|
|
value: "london",
|
|
},
|
|
{
|
|
label: "New York",
|
|
value: "new_york",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
value: "tokyo",
|
|
},
|
|
]}
|
|
disabled={true}
|
|
multi={true}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
const input = screen.getByRole("combobox", {
|
|
name: "Cities",
|
|
});
|
|
expect(input).toHaveAttribute("disabled");
|
|
|
|
const button: HTMLButtonElement = document.querySelector(
|
|
".c__select__inner__actions__open",
|
|
)!;
|
|
expect(button).toBeDisabled();
|
|
|
|
const menu: HTMLDivElement = screen.getByRole("listbox", {
|
|
name: "Cities",
|
|
});
|
|
expectMenuToBeClosed(menu);
|
|
|
|
const user = userEvent.setup();
|
|
|
|
// Try to open the menu.
|
|
await user.click(input);
|
|
|
|
// Make sure menu is still closed.
|
|
expectMenuToBeClosed(menu);
|
|
|
|
// Make sure no value is rendered
|
|
expectSelectedOptions([]);
|
|
});
|
|
|
|
it("renders with text", async () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
value: "paris",
|
|
},
|
|
{
|
|
label: "London",
|
|
value: "london",
|
|
},
|
|
{
|
|
label: "New York",
|
|
value: "new_york",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
value: "tokyo",
|
|
},
|
|
]}
|
|
text="This is a text"
|
|
multi={true}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
screen.getByText("This is a text");
|
|
});
|
|
|
|
it("renders with state=error", async () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
value: "paris",
|
|
},
|
|
{
|
|
label: "London",
|
|
value: "london",
|
|
},
|
|
{
|
|
label: "New York",
|
|
value: "new_york",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
value: "tokyo",
|
|
},
|
|
]}
|
|
text="This is a text"
|
|
state="error"
|
|
multi={true}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
expect(
|
|
document.querySelector(".c__select.c__select--error"),
|
|
).toBeInTheDocument();
|
|
});
|
|
|
|
it("renders with state=success", async () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
value: "paris",
|
|
},
|
|
{
|
|
label: "London",
|
|
value: "london",
|
|
},
|
|
{
|
|
label: "New York",
|
|
value: "new_york",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
value: "tokyo",
|
|
},
|
|
]}
|
|
text="This is a text"
|
|
state="success"
|
|
multi={true}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
expect(
|
|
document.querySelector(".c__select.c__select--success"),
|
|
).toBeInTheDocument();
|
|
});
|
|
|
|
it("submits form data", async () => {
|
|
let formData: any;
|
|
const Wrapper = () => {
|
|
const onSubmit = (e: FormEvent<HTMLFormElement>) => {
|
|
e.preventDefault();
|
|
const data = new FormData(e.currentTarget);
|
|
formData = {
|
|
cities: data.getAll("cities"),
|
|
};
|
|
};
|
|
|
|
return (
|
|
<CunninghamProvider>
|
|
<div>
|
|
<form onSubmit={onSubmit}>
|
|
<Select
|
|
label="Cities"
|
|
name="cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
value: "paris",
|
|
},
|
|
{
|
|
label: "London",
|
|
value: "london",
|
|
},
|
|
{
|
|
label: "New York",
|
|
value: "new_york",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
value: "tokyo",
|
|
},
|
|
]}
|
|
multi={true}
|
|
/>
|
|
<Button>Submit</Button>
|
|
</form>
|
|
</div>
|
|
</CunninghamProvider>
|
|
);
|
|
};
|
|
render(<Wrapper />);
|
|
|
|
const user = userEvent.setup();
|
|
const input = screen.getByRole("combobox", {
|
|
name: "Cities",
|
|
});
|
|
const menu: HTMLDivElement = screen.getByRole("listbox", {
|
|
name: "Cities",
|
|
});
|
|
const button = screen.getByRole("button", {
|
|
name: "Submit",
|
|
});
|
|
|
|
// Submit the form being empty.
|
|
expectSelectedOptions([]);
|
|
await user.click(button);
|
|
expect(formData).toEqual({
|
|
cities: [],
|
|
});
|
|
|
|
// Select an option
|
|
await user.click(input);
|
|
expectMenuToBeOpen(menu);
|
|
|
|
await user.click(
|
|
screen.getByRole("option", {
|
|
name: "New York",
|
|
}),
|
|
);
|
|
expectSelectedOptions(["New York"]);
|
|
|
|
// Close the menu.
|
|
await user.click(input);
|
|
expectMenuToBeClosed(menu);
|
|
|
|
// Submit the form being fulfilled.
|
|
await user.click(button);
|
|
expect(formData).toEqual({
|
|
cities: ["new_york"],
|
|
});
|
|
expectSelectedOptions(["New York"]);
|
|
|
|
// Select another option.
|
|
await user.click(input);
|
|
await user.click(
|
|
await screen.findByRole("option", {
|
|
name: "Paris",
|
|
}),
|
|
);
|
|
expectSelectedOptions(["New York", "Paris"]);
|
|
|
|
// Close the menu.
|
|
await user.click(input);
|
|
expectMenuToBeClosed(menu);
|
|
|
|
// Submit the form being fulfilled.
|
|
await user.click(button);
|
|
expect(formData).toEqual({
|
|
cities: ["new_york", "paris"],
|
|
});
|
|
|
|
// Clear selection.
|
|
const clearButton = screen.getByRole("button", {
|
|
name: "Clear all selections",
|
|
});
|
|
await userEvent.click(clearButton);
|
|
|
|
// Submit again.
|
|
await user.click(button);
|
|
expect(formData).toEqual({
|
|
cities: [],
|
|
});
|
|
});
|
|
|
|
it("should not be clearable", async () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
},
|
|
{
|
|
label: "London",
|
|
},
|
|
{
|
|
label: "New York",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
},
|
|
]}
|
|
clearable={false}
|
|
defaultValue={["Paris"]}
|
|
multi={true}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
screen.getByRole("combobox", {
|
|
name: "Cities",
|
|
});
|
|
|
|
// Make sure default value is rendered.
|
|
expectSelectedOptions(["Paris"]);
|
|
// Make sure the clear button is not rendered.
|
|
expect(
|
|
screen.queryByRole("button", {
|
|
name: "Clear all selections",
|
|
}),
|
|
).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("is not possible to select disabled options", async () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
multi={true}
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
},
|
|
{
|
|
label: "London",
|
|
},
|
|
{
|
|
label: "New York",
|
|
disabled: true,
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
},
|
|
]}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
const input = screen.getByRole("combobox", {
|
|
name: "Cities",
|
|
});
|
|
const menu: HTMLDivElement = screen.getByRole("listbox", {
|
|
name: "Cities",
|
|
});
|
|
|
|
// Make sure the select is empty.
|
|
expectSelectedOptions([]);
|
|
|
|
const user = userEvent.setup();
|
|
await user.click(input);
|
|
expectMenuToBeOpen(menu);
|
|
|
|
// Make sure the disabled option is not selectable.
|
|
let option: HTMLLIElement = screen.getByRole("option", {
|
|
name: "New York",
|
|
});
|
|
expectOptionToBeDisabled(option);
|
|
|
|
// Try to click on the disabled option.
|
|
await user.click(option);
|
|
|
|
expectMenuToBeOpen(menu);
|
|
// Make sure the select is still empty.
|
|
expectSelectedOptions([]);
|
|
|
|
// Select a not disabled option.
|
|
option = screen.getByRole("option", {
|
|
name: "Tokyo",
|
|
});
|
|
|
|
await user.click(option);
|
|
await waitFor(() => expectSelectedOptions(["Tokyo"]));
|
|
});
|
|
|
|
it("is accessible if the the label is not shown", async () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
},
|
|
{
|
|
label: "London",
|
|
},
|
|
{
|
|
label: "New York",
|
|
disabled: true,
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
},
|
|
]}
|
|
hideLabel={true}
|
|
multi={true}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
// Make sure the input is accessible.
|
|
screen.getByRole("combobox", {
|
|
name: "Cities",
|
|
});
|
|
const label = screen.getByText("Cities");
|
|
expect(Array.from(label.classList)).toContain("offscreen");
|
|
});
|
|
});
|
|
|
|
describe("Searchable", async () => {
|
|
it("shows all options when clicking on the input", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
},
|
|
{
|
|
label: "Panama",
|
|
},
|
|
{
|
|
label: "London",
|
|
},
|
|
{
|
|
label: "New York",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
},
|
|
]}
|
|
searchable={true}
|
|
multi={true}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
|
|
const input = screen.getByRole("combobox", {
|
|
name: "Cities",
|
|
});
|
|
// It returns the input.
|
|
expect(input.tagName).toEqual("INPUT");
|
|
|
|
const menu: HTMLDivElement = screen.getByRole("listbox", {
|
|
name: "Cities",
|
|
});
|
|
|
|
expectMenuToBeClosed(menu);
|
|
|
|
// Click on the input.
|
|
await user.click(input);
|
|
expectMenuToBeOpen(menu);
|
|
expectOptions(["Paris", "Panama", "London", "New York", "Tokyo"]);
|
|
|
|
// Select an option.
|
|
const option: HTMLLIElement = screen.getByRole("option", {
|
|
name: "New York",
|
|
});
|
|
await user.click(option);
|
|
|
|
expectMenuToBeOpen(menu);
|
|
|
|
expectSelectedOptions(["New York"]);
|
|
});
|
|
|
|
it("filters options when typing", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
value: "paris",
|
|
},
|
|
{
|
|
label: "Panama",
|
|
value: "panama",
|
|
},
|
|
{
|
|
label: "London",
|
|
value: "london",
|
|
},
|
|
{
|
|
label: "New York",
|
|
value: "new_york",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
value: "tokyo",
|
|
},
|
|
]}
|
|
searchable={true}
|
|
multi={true}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
|
|
const input = screen.getByRole("combobox", {
|
|
name: "Cities",
|
|
});
|
|
// It returns the input.
|
|
expect(input.tagName).toEqual("INPUT");
|
|
|
|
const menu: HTMLDivElement = screen.getByRole("listbox", {
|
|
name: "Cities",
|
|
});
|
|
|
|
expectMenuToBeClosed(menu);
|
|
|
|
// Click on the input.
|
|
await user.click(input);
|
|
expectMenuToBeOpen(menu);
|
|
expectOptions(["Paris", "Panama", "London", "New York", "Tokyo"]);
|
|
|
|
// Verify that filtering works.
|
|
await user.type(input, "Pa");
|
|
expectMenuToBeOpen(menu);
|
|
expectOptions(["Paris", "Panama"]);
|
|
|
|
await user.type(input, "r");
|
|
expectOptions(["Paris"]);
|
|
});
|
|
|
|
it("selects the option on enter", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
value: "paris",
|
|
},
|
|
{
|
|
label: "Panama",
|
|
value: "panama",
|
|
},
|
|
{
|
|
label: "London",
|
|
value: "london",
|
|
},
|
|
{
|
|
label: "New York",
|
|
value: "new_york",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
value: "tokyo",
|
|
},
|
|
]}
|
|
searchable={true}
|
|
multi={true}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
|
|
const input = screen.getByRole("combobox", {
|
|
name: "Cities",
|
|
});
|
|
// It returns the input.
|
|
expect(input.tagName).toEqual("INPUT");
|
|
|
|
const menu: HTMLDivElement = screen.getByRole("listbox", {
|
|
name: "Cities",
|
|
});
|
|
|
|
expectMenuToBeClosed(menu);
|
|
|
|
// Click on the input.
|
|
await user.click(input);
|
|
expectMenuToBeOpen(menu);
|
|
expectOptions(["Paris", "Panama", "London", "New York", "Tokyo"]);
|
|
|
|
// Verify that filtering works.
|
|
await user.type(input, "Par");
|
|
expectOptions(["Paris"]);
|
|
|
|
await user.type(input, "{enter}");
|
|
|
|
await waitFor(() => expectSelectedOptions(["Paris"]));
|
|
});
|
|
|
|
it("remove last selected value on backspace", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
value: "paris",
|
|
},
|
|
{
|
|
label: "Panama",
|
|
value: "panama",
|
|
},
|
|
{
|
|
label: "London",
|
|
value: "london",
|
|
},
|
|
{
|
|
label: "New York",
|
|
value: "new_york",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
value: "tokyo",
|
|
},
|
|
]}
|
|
searchable={true}
|
|
multi={true}
|
|
defaultValue={["panama", "tokyo", "new_york"]}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
|
|
expectSelectedOptions(["Panama", "New York", "Tokyo"]);
|
|
|
|
const input = screen.getByRole("combobox", {
|
|
name: "Cities",
|
|
});
|
|
|
|
// Type few characters.
|
|
await user.type(input, "Par");
|
|
expectOptions(["Paris"]);
|
|
|
|
// Remove "Par" characters.
|
|
await user.type(input, "{backspace}");
|
|
await user.type(input, "{backspace}");
|
|
await user.type(input, "{backspace}");
|
|
expectSelectedOptions(["Panama", "New York", "Tokyo"]);
|
|
|
|
await user.type(input, "{backspace}");
|
|
expectSelectedOptions(["Panama", "New York"]);
|
|
|
|
await user.type(input, "{backspace}");
|
|
expectSelectedOptions(["Panama"]);
|
|
|
|
await user.type(input, "{backspace}");
|
|
expectSelectedOptions([]);
|
|
|
|
// One last to be sure it works when already empty.
|
|
await user.type(input, "{backspace}");
|
|
expectSelectedOptions([]);
|
|
});
|
|
|
|
it("clears value", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
},
|
|
{
|
|
label: "Panama",
|
|
},
|
|
{
|
|
label: "London",
|
|
},
|
|
{
|
|
label: "New York",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
},
|
|
]}
|
|
multi={true}
|
|
searchable={true}
|
|
defaultValue={["London", "Tokyo", "Panama"]}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
|
|
expectSelectedOptions(["Panama", "London", "Tokyo"]);
|
|
|
|
// Clear selection.
|
|
const clearButton = screen.getByRole("button", {
|
|
name: "Clear all selections",
|
|
});
|
|
await user.click(clearButton);
|
|
expectSelectedOptions([]);
|
|
});
|
|
|
|
it("should select with defaultValue using label", async () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
},
|
|
{
|
|
label: "Panama",
|
|
},
|
|
{
|
|
label: "London",
|
|
},
|
|
{
|
|
label: "New York",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
},
|
|
]}
|
|
multi={true}
|
|
searchable={true}
|
|
defaultValue={["London", "Tokyo", "Panama"]}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
|
|
expectSelectedOptions(["Panama", "London", "Tokyo"]);
|
|
});
|
|
|
|
it("should select with defaultValue using value", async () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
value: "paris",
|
|
},
|
|
{
|
|
label: "London",
|
|
value: "london",
|
|
},
|
|
{
|
|
label: "New York",
|
|
value: "new_york",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
value: "tokyo",
|
|
},
|
|
]}
|
|
defaultValue={["tokyo", "new_york"]}
|
|
searchable={true}
|
|
multi={true}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
expectSelectedOptions(["New York", "Tokyo"]);
|
|
});
|
|
|
|
it("should not select any value if there is not match", async () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
value: "paris",
|
|
},
|
|
{
|
|
label: "London",
|
|
value: "london",
|
|
},
|
|
{
|
|
label: "New York",
|
|
value: "new_york",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
value: "tokyo",
|
|
},
|
|
]}
|
|
defaultValue={["not_found", "new_york"]}
|
|
searchable={true}
|
|
multi={true}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
expectSelectedOptions(["New York"]);
|
|
});
|
|
|
|
it("works controlled", async () => {
|
|
const Wrapper = () => {
|
|
const [value, setValue] = useState<string[]>(["london"]);
|
|
return (
|
|
<CunninghamProvider>
|
|
<div>
|
|
<div>Value = {JSON.stringify(value)}|</div>
|
|
<Button onClick={() => setValue([])}>Clear</Button>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
value: "paris",
|
|
},
|
|
{
|
|
label: "London",
|
|
value: "london",
|
|
},
|
|
{
|
|
label: "New York",
|
|
value: "new_york",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
value: "tokyo",
|
|
},
|
|
]}
|
|
multi={true}
|
|
searchable={true}
|
|
value={value}
|
|
onChange={(e) => setValue(e.target.value as string[])}
|
|
/>
|
|
</div>
|
|
</CunninghamProvider>
|
|
);
|
|
};
|
|
render(<Wrapper />);
|
|
|
|
const input = screen.getByRole("combobox", {
|
|
name: "Cities",
|
|
});
|
|
|
|
// Make sure value is selected.
|
|
screen.getByText('Value = ["london"]|');
|
|
expectSelectedOptions(["London"]);
|
|
|
|
// Select one more value.
|
|
const user = userEvent.setup();
|
|
await user.click(input);
|
|
await waitFor(() => expectOptions(["Paris", "New York", "Tokyo"]));
|
|
await user.type(input, "New");
|
|
await waitFor(() => expectOptions(["New York"]));
|
|
await user.type(input, "{enter}}");
|
|
expectSelectedOptions(["London", "New York"]);
|
|
screen.getByText('Value = ["london","new_york"]|');
|
|
|
|
// clear value.
|
|
const button = screen.getByRole("button", {
|
|
name: "Clear",
|
|
});
|
|
await user.click(button);
|
|
|
|
// Make sure value is cleared.
|
|
screen.getByText("Value = []|");
|
|
});
|
|
|
|
it("renders disabled", async () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
value: "paris",
|
|
},
|
|
{
|
|
label: "London",
|
|
value: "london",
|
|
},
|
|
{
|
|
label: "New York",
|
|
value: "new_york",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
value: "tokyo",
|
|
},
|
|
]}
|
|
disabled={true}
|
|
searchable={true}
|
|
multi={true}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
const input = screen.getByRole("combobox", {
|
|
name: "Cities",
|
|
});
|
|
expect(input).toHaveAttribute("disabled");
|
|
|
|
const button: HTMLButtonElement = document.querySelector(
|
|
".c__select__inner__actions__open",
|
|
)!;
|
|
expect(button).toBeDisabled();
|
|
|
|
const menu: HTMLDivElement = screen.getByRole("listbox", {
|
|
name: "Cities",
|
|
});
|
|
expectMenuToBeClosed(menu);
|
|
|
|
const user = userEvent.setup();
|
|
|
|
// Try to open the menu.
|
|
await user.click(input);
|
|
|
|
// Make sure menu is still closed.
|
|
expectMenuToBeClosed(menu);
|
|
|
|
// Make sure no value is rendered
|
|
const valueRendered = document.querySelector(".c__select__inner__value");
|
|
expect(valueRendered).toHaveTextContent("");
|
|
|
|
// Try to type
|
|
await user.type(input, "Pa");
|
|
expectMenuToBeClosed(menu);
|
|
});
|
|
});
|
|
});
|