We want to be able to notify an event when the search term of the searchable select gets updated.
1875 lines
48 KiB
TypeScript
1875 lines
48 KiB
TypeScript
import userEvent from "@testing-library/user-event";
|
|
import { render, screen, waitFor } from "@testing-library/react";
|
|
import React, { createRef, FormEvent, useState } from "react";
|
|
import { expect } from "vitest";
|
|
import { within } from "@testing-library/dom";
|
|
import { CunninghamProvider } from ":/components/Provider";
|
|
import {
|
|
Select,
|
|
SelectHandle,
|
|
SelectProps,
|
|
} from ":/components/Forms/Select/index";
|
|
import {
|
|
expectMenuToBeClosed,
|
|
expectMenuToBeOpen,
|
|
expectOptions,
|
|
expectOptionToBeDisabled,
|
|
expectOptionToBeUnselected,
|
|
expectSelectedOptions,
|
|
expectSelectedOptionsText,
|
|
} 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")!.parentElement!;
|
|
|
|
// 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")!.parentElement!;
|
|
expect(Array.from(label.classList)).toContain("c__offscreen");
|
|
});
|
|
|
|
it("is possible to select again the last deleted item", 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",
|
|
},
|
|
]}
|
|
multi={true}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
|
|
const input = screen.getByRole("combobox", {
|
|
name: "Cities",
|
|
});
|
|
expectSelectedOptions([]);
|
|
|
|
const user = userEvent.setup();
|
|
await user.click(input);
|
|
|
|
// Select options.
|
|
await user.click(
|
|
screen.getByRole("option", {
|
|
name: "Paris",
|
|
}),
|
|
);
|
|
expectSelectedOptions(["Paris"]);
|
|
|
|
// Clear selection.
|
|
const clearButton = screen.getByRole("button", {
|
|
name: "Clear all selections",
|
|
});
|
|
await user.click(clearButton);
|
|
expectSelectedOptions([]);
|
|
|
|
// Close and re-open the menu. ( This is needed in this tests but in real life it works, I don't know exactly why .. )
|
|
await user.click(input);
|
|
await user.click(input);
|
|
|
|
// Select again Paris.
|
|
await user.click(
|
|
screen.getByRole("option", {
|
|
name: "Paris",
|
|
}),
|
|
);
|
|
expectSelectedOptions(["Paris"]);
|
|
});
|
|
|
|
it("blurs from ref", async () => {
|
|
const ref = createRef<SelectHandle>();
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="City"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
value: "paris",
|
|
},
|
|
{
|
|
label: "Panama",
|
|
value: "panama",
|
|
},
|
|
{
|
|
label: "London",
|
|
value: "london",
|
|
},
|
|
]}
|
|
multi={true}
|
|
ref={ref}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
const input = screen.getByRole("combobox", {
|
|
name: "City",
|
|
});
|
|
const menu: HTMLDivElement = screen.getByRole("listbox", {
|
|
name: "City",
|
|
});
|
|
const user = userEvent.setup();
|
|
|
|
// Make sure the select is not focused.
|
|
expect(document.activeElement?.className).toEqual("");
|
|
|
|
// Focus the select.
|
|
await user.click(input);
|
|
expectMenuToBeOpen(menu);
|
|
expect(document.activeElement?.className).toContain("c__select__wrapper");
|
|
|
|
// Blur the select.
|
|
ref.current?.blur();
|
|
|
|
// Make sure the select is blured.
|
|
await waitFor(() => expectMenuToBeClosed(menu));
|
|
expect(document.activeElement?.className).toEqual("");
|
|
});
|
|
|
|
it("renders custom options", async () => {
|
|
const Wrapper = (props: SelectProps) => {
|
|
return (
|
|
<CunninghamProvider>
|
|
<Select {...props} />
|
|
</CunninghamProvider>
|
|
);
|
|
};
|
|
|
|
const props: SelectProps = {
|
|
label: "City",
|
|
multi: true,
|
|
options: [
|
|
{
|
|
label: "Paris",
|
|
value: "paris",
|
|
render: () => (
|
|
<div>
|
|
<img src="paris.png" alt="Paris flag" />
|
|
Paris
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
label: "Panama",
|
|
value: "panama",
|
|
render: () => (
|
|
<div>
|
|
<img src="panama.png" alt="Panama flag" />
|
|
Panama
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
label: "London",
|
|
value: "london",
|
|
render: () => (
|
|
<div>
|
|
<img src="london.png" alt="London flag" />
|
|
London
|
|
</div>
|
|
),
|
|
},
|
|
],
|
|
};
|
|
|
|
const { rerender } = render(<Wrapper {...props} />);
|
|
const input = screen.getByRole("combobox", {
|
|
name: "City",
|
|
});
|
|
const menu: HTMLDivElement = screen.getByRole("listbox", {
|
|
name: "City",
|
|
});
|
|
const valueRendered = document.querySelector(
|
|
".c__select__inner__value",
|
|
) as HTMLElement;
|
|
|
|
const user = userEvent.setup();
|
|
expectSelectedOptions([]);
|
|
|
|
await user.click(input);
|
|
expectMenuToBeOpen(menu);
|
|
screen.getByRole("img", { name: "Paris flag" });
|
|
screen.getByRole("img", { name: "Panama flag" });
|
|
screen.getByRole("img", { name: "London flag" });
|
|
|
|
// Select Paris
|
|
await user.click(
|
|
screen.getByRole("option", { name: "Paris flag Paris" }),
|
|
);
|
|
await user.click(
|
|
screen.getByRole("option", { name: "London flag London" }),
|
|
);
|
|
|
|
// Make sure only the label is rendered by default.
|
|
expectSelectedOptions(["Paris", "London"]);
|
|
expect(
|
|
within(valueRendered).queryByRole("img", {
|
|
name: "Paris flag",
|
|
}),
|
|
).not.toBeInTheDocument();
|
|
expect(
|
|
within(valueRendered).queryByRole("img", {
|
|
name: "London flag",
|
|
}),
|
|
).not.toBeInTheDocument();
|
|
|
|
// Now showLabelWhenSelected to false.
|
|
rerender(<Wrapper {...props} showLabelWhenSelected={false} />);
|
|
|
|
// Make sure the HTML content of the options is rendered.
|
|
expectSelectedOptions(["Paris", "London"]);
|
|
within(valueRendered).getByRole("img", {
|
|
name: "Paris flag",
|
|
});
|
|
within(valueRendered).getByRole("img", {
|
|
name: "London flag",
|
|
});
|
|
});
|
|
|
|
it("renders checkboxes in the menu and selected options as text when using monoline mode", async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
},
|
|
{
|
|
label: "Panama",
|
|
},
|
|
{
|
|
label: "London",
|
|
},
|
|
{
|
|
label: "New York",
|
|
},
|
|
{
|
|
label: "Tokyo",
|
|
},
|
|
]}
|
|
multi={true}
|
|
monoline={true}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
const input = screen.getByRole("combobox", {
|
|
name: "Cities",
|
|
});
|
|
const menu: HTMLDivElement = screen.getByRole("listbox", {
|
|
name: "Cities",
|
|
});
|
|
const label = screen.getByText("Cities")!.parentElement!;
|
|
|
|
const expectCheckedOptions = ({
|
|
checked,
|
|
notChecked,
|
|
}: {
|
|
checked: string[];
|
|
notChecked: string[];
|
|
}) => {
|
|
checked.forEach((option) => {
|
|
const optionElement = screen.getByRole("option", { name: option });
|
|
const checkbox: HTMLInputElement =
|
|
within(optionElement).getByRole("checkbox");
|
|
expect(checkbox.checked).toEqual(true);
|
|
});
|
|
notChecked.forEach((option) => {
|
|
const optionElement = screen.getByRole("option", { name: option });
|
|
const checkbox: HTMLInputElement =
|
|
within(optionElement).getByRole("checkbox");
|
|
expect(checkbox.checked).toEqual(false);
|
|
});
|
|
};
|
|
|
|
// Expect no options to be selected.
|
|
expectSelectedOptionsText([]);
|
|
|
|
// 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);
|
|
expectOptions(["Paris", "Panama", "London", "New York", "Tokyo"]);
|
|
|
|
expectCheckedOptions({
|
|
checked: [],
|
|
notChecked: ["Paris", "Panama", "London", "New York", "Tokyo"],
|
|
});
|
|
|
|
// 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.
|
|
expectSelectedOptionsText(["London"]);
|
|
|
|
// Make sure the menu stays open.
|
|
expectMenuToBeOpen(menu);
|
|
|
|
// Make sure the option is still present in the menu and is highlighted.
|
|
expectCheckedOptions({
|
|
checked: ["London"],
|
|
notChecked: ["Paris", "Panama", "New York", "Tokyo"],
|
|
});
|
|
|
|
// Select Paris
|
|
await user.click(screen.getByRole("option", { name: "Paris" }));
|
|
|
|
// Make sure the option is selected.
|
|
expectSelectedOptionsText(["London", "Paris"]);
|
|
|
|
// Make sure the menu stays open.
|
|
expectMenuToBeOpen(menu);
|
|
|
|
// Make sure the option is still present in the menu and is highlighted.
|
|
expectCheckedOptions({
|
|
checked: ["London", "Paris"],
|
|
notChecked: ["Panama", "New York", "Tokyo"],
|
|
});
|
|
|
|
// Click on London and make sure it is removed from the selected.
|
|
await user.click(screen.getByRole("option", { name: "London" }));
|
|
|
|
// We need to move the cursor away from London otherwise the option will be highlighted.
|
|
await user.hover(screen.getByRole("option", { name: "Paris" }));
|
|
|
|
// Make sure the option is not selected anymore.
|
|
expectSelectedOptionsText(["Paris"]);
|
|
|
|
// Make sure the menu stays open.
|
|
expectMenuToBeOpen(menu);
|
|
|
|
// Make sure the option is still present in the menu and is not highlighted anymore.
|
|
await waitFor(() =>
|
|
expectCheckedOptions({
|
|
checked: ["Paris"],
|
|
notChecked: ["London", "Panama", "New York", "Tokyo"],
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
|
|
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("get the search term using onSearchInputChange", async () => {
|
|
const user = userEvent.setup();
|
|
let searchTerm: string | undefined;
|
|
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}
|
|
onSearchInputChange={(e) => {
|
|
searchTerm = e.target.value;
|
|
}}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
|
|
const input = screen.getByRole("combobox", {
|
|
name: "Cities",
|
|
});
|
|
expect(searchTerm).toBeUndefined();
|
|
|
|
await user.click(input);
|
|
expect(searchTerm).toBeUndefined();
|
|
|
|
await user.type(input, "Pa");
|
|
expect(searchTerm).toEqual("Pa");
|
|
|
|
await user.type(input, "r");
|
|
expect(searchTerm).toEqual("Par");
|
|
expectOptions(["Paris"]);
|
|
|
|
const option: HTMLLIElement = screen.getByRole("option", {
|
|
name: "Paris",
|
|
});
|
|
await user.click(option);
|
|
|
|
expect(searchTerm).toBeUndefined();
|
|
});
|
|
|
|
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}", { skipClick: true });
|
|
|
|
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", { skipClick: true });
|
|
await waitFor(() => expectOptions(["New York"]));
|
|
await user.type(input, "{enter}}", { skipClick: true });
|
|
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);
|
|
});
|
|
|
|
it("renders menu empty placeholder when there is no more options to display", async () => {
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="Cities"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
value: "paris",
|
|
},
|
|
{
|
|
label: "London",
|
|
value: "london",
|
|
},
|
|
]}
|
|
multi={true}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
const input = screen.getByRole("combobox", {
|
|
name: "Cities",
|
|
});
|
|
|
|
// Expect no options to be selected.
|
|
expectSelectedOptions([]);
|
|
|
|
const user = userEvent.setup();
|
|
await user.click(input);
|
|
|
|
await user.click(
|
|
screen.getByRole("option", {
|
|
name: "Paris",
|
|
}),
|
|
);
|
|
expectSelectedOptions(["Paris"]);
|
|
|
|
expect(
|
|
screen.queryByText("No options available"),
|
|
).not.toBeInTheDocument();
|
|
|
|
await user.click(
|
|
screen.getByRole("option", {
|
|
name: "London",
|
|
}),
|
|
);
|
|
expectSelectedOptions(["Paris", "London"]);
|
|
|
|
screen.getByText("No options available");
|
|
});
|
|
|
|
it("blurs from ref", async () => {
|
|
const ref = createRef<SelectHandle>();
|
|
render(
|
|
<CunninghamProvider>
|
|
<Select
|
|
label="City"
|
|
options={[
|
|
{
|
|
label: "Paris",
|
|
value: "paris",
|
|
},
|
|
{
|
|
label: "Panama",
|
|
value: "panama",
|
|
},
|
|
{
|
|
label: "London",
|
|
value: "london",
|
|
},
|
|
]}
|
|
multi={true}
|
|
searchable={true}
|
|
ref={ref}
|
|
/>
|
|
</CunninghamProvider>,
|
|
);
|
|
const input = screen.getByRole("combobox", {
|
|
name: "City",
|
|
});
|
|
const menu: HTMLDivElement = screen.getByRole("listbox", {
|
|
name: "City",
|
|
});
|
|
const user = userEvent.setup();
|
|
|
|
// Make sure the select is not focused.
|
|
expect(document.activeElement?.tagName).toEqual("BODY");
|
|
|
|
// Focus the select by focusing input.
|
|
await user.click(input);
|
|
expectMenuToBeOpen(menu);
|
|
expect(document.activeElement?.tagName).toEqual("INPUT");
|
|
|
|
// Blur the select.
|
|
ref.current?.blur();
|
|
|
|
// Make sure the select is blured.
|
|
await waitFor(() => expectMenuToBeClosed(menu));
|
|
expect(document.activeElement?.tagName).toEqual("BODY");
|
|
});
|
|
|
|
it("renders custom options", async () => {
|
|
const Wrapper = (props: SelectProps) => {
|
|
return (
|
|
<CunninghamProvider>
|
|
<Select {...props} />
|
|
</CunninghamProvider>
|
|
);
|
|
};
|
|
|
|
const props: SelectProps = {
|
|
label: "City",
|
|
multi: true,
|
|
searchable: true,
|
|
options: [
|
|
{
|
|
label: "Paris",
|
|
value: "paris",
|
|
render: () => (
|
|
<div>
|
|
<img src="paris.png" alt="Paris flag" />
|
|
Paris
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
label: "Panama",
|
|
value: "panama",
|
|
render: () => (
|
|
<div>
|
|
<img src="panama.png" alt="Panama flag" />
|
|
Panama
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
label: "London",
|
|
value: "london",
|
|
render: () => (
|
|
<div>
|
|
<img src="london.png" alt="London flag" />
|
|
London
|
|
</div>
|
|
),
|
|
},
|
|
],
|
|
};
|
|
|
|
const { rerender } = render(<Wrapper {...props} />);
|
|
const input = screen.getByRole("combobox", {
|
|
name: "City",
|
|
});
|
|
const menu: HTMLDivElement = screen.getByRole("listbox", {
|
|
name: "City",
|
|
});
|
|
const valueRendered = document.querySelector(
|
|
".c__select__inner__value",
|
|
) as HTMLElement;
|
|
|
|
const user = userEvent.setup();
|
|
expectSelectedOptions([]);
|
|
|
|
await user.click(input);
|
|
expectMenuToBeOpen(menu);
|
|
screen.getByRole("img", { name: "Paris flag" });
|
|
screen.getByRole("img", { name: "Panama flag" });
|
|
screen.getByRole("img", { name: "London flag" });
|
|
|
|
// Filter options.
|
|
await user.type(input, "Pa");
|
|
screen.getByRole("img", { name: "Paris flag" });
|
|
screen.getByRole("img", { name: "Panama flag" });
|
|
expect(
|
|
screen.queryByRole("img", { name: "London flag" }),
|
|
).not.toBeInTheDocument();
|
|
|
|
// Select Paris
|
|
await user.click(
|
|
screen.getByRole("option", { name: "Paris flag Paris" }),
|
|
);
|
|
|
|
// Filter to find London.
|
|
await user.clear(input);
|
|
expect(
|
|
screen.queryByRole("img", { name: "Paris flag" }),
|
|
).not.toBeInTheDocument();
|
|
screen.getByRole("img", { name: "Panama flag" });
|
|
screen.getByRole("img", { name: "London flag" });
|
|
|
|
await user.type(input, "Lo");
|
|
expect(
|
|
screen.queryByRole("img", { name: "Paris flag" }),
|
|
).not.toBeInTheDocument();
|
|
expect(
|
|
screen.queryByRole("img", { name: "Panama flag" }),
|
|
).not.toBeInTheDocument();
|
|
screen.getByRole("img", { name: "London flag" });
|
|
|
|
// Select London.
|
|
await user.click(
|
|
screen.getByRole("option", { name: "London flag London" }),
|
|
);
|
|
|
|
// Make sure only the label is rendered by default.
|
|
expectSelectedOptions(["Paris", "London"]);
|
|
expect(
|
|
within(valueRendered).queryByRole("img", {
|
|
name: "Paris flag",
|
|
}),
|
|
).not.toBeInTheDocument();
|
|
expect(
|
|
within(valueRendered).queryByRole("img", {
|
|
name: "London flag",
|
|
}),
|
|
).not.toBeInTheDocument();
|
|
|
|
// Now showLabelWhenSelected to false.
|
|
rerender(<Wrapper {...props} showLabelWhenSelected={false} />);
|
|
|
|
// Make sure the HTML content of the options is rendered.
|
|
expectSelectedOptions(["Paris", "London"]);
|
|
within(valueRendered).getByRole("img", {
|
|
name: "Paris flag",
|
|
});
|
|
within(valueRendered).getByRole("img", {
|
|
name: "London flag",
|
|
});
|
|
});
|
|
});
|
|
});
|