(react) add select mono option custom render

We want to be able to render the options in a customized manner.
This commit is contained in:
Nathan Vasse
2023-10-06 16:54:55 +02:00
committed by NathanVss
parent 611eebf0a4
commit 48e4e56a44
11 changed files with 358 additions and 21 deletions

View File

@@ -2,7 +2,13 @@ import userEvent from "@testing-library/user-event";
import { render, screen, waitFor } from "@testing-library/react";
import { expect } from "vitest";
import React, { createRef, FormEvent, useState } from "react";
import { Select, Option, SelectHandle } from ":/components/Forms/Select/index";
import { within } from "@testing-library/dom";
import {
Select,
Option,
SelectHandle,
SelectProps,
} from ":/components/Forms/Select/index";
import { Button } from ":/components/Button";
import { CunninghamProvider } from ":/components/Provider";
import {
@@ -836,6 +842,123 @@ describe("<Select/>", () => {
await waitFor(() => expectMenuToBeClosed(menu));
expect(document.activeElement?.tagName).toEqual("BODY");
});
it("renders custom options", async () => {
const Wrapper = (props: SelectProps) => {
return (
<CunninghamProvider>
<Select {...props} />
<Button>Blur</Button>
</CunninghamProvider>
);
};
const props: SelectProps = {
label: "City",
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 blurButton = screen.getByRole("button", { name: "Blur" });
const user = userEvent.setup();
const valueRendered = document.querySelector(
".c__select__inner__value",
) as HTMLElement;
await user.click(input);
expectMenuToBeOpen(menu);
screen.getByRole("img", { name: "Paris flag" });
screen.getByRole("img", { name: "Panama flag" });
screen.getByRole("img", { name: "London flag" });
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();
await user.click(
screen.getByRole("option", { name: "Paris flag Paris" }),
);
await user.click(blurButton);
// Make sure only the label is rendered by default.
expect(input).toHaveValue("Paris");
expect(input).not.toHaveClass("c__select__inner__value__input--hidden");
expect(
within(valueRendered).queryByRole("img", {
name: "Paris flag",
}),
).not.toBeInTheDocument();
// Now showLabelWhenSelected to false.
rerender(<Wrapper {...props} showLabelWhenSelected={false} />);
// Make sure the HTML content of the option is rendered.
// The input is still present in the DOM ( but hidden for users ).
expect(input).toHaveValue("Paris");
expect(input).toHaveClass("c__select__inner__value__input--hidden");
within(valueRendered).getByRole("img", {
name: "Paris flag",
});
// Focus on the input and make sure the custom HTML is removed.
await user.click(input);
expect(input).toHaveValue("Paris");
expect(input).not.toHaveClass("c__select__inner__value__input--hidden");
expect(
within(valueRendered).queryByRole("img", {
name: "Paris flag",
}),
).not.toBeInTheDocument();
// Blur the input and make sure the custom HTML is rendered.
await user.click(blurButton);
expect(input).toHaveValue("Paris");
expect(input).toHaveClass("c__select__inner__value__input--hidden");
within(valueRendered).getByRole("img", {
name: "Paris flag",
});
});
});
describe("Simple", () => {
@@ -1644,5 +1767,91 @@ describe("<Select/>", () => {
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",
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 user = userEvent.setup();
const valueRendered = document.querySelector(
".c__select__inner__value",
) as HTMLElement;
await user.click(input);
expectMenuToBeOpen(menu);
screen.getByRole("img", { name: "Paris flag" });
screen.getByRole("img", { name: "Panama flag" });
screen.getByRole("img", { name: "London flag" });
await user.click(
screen.getByRole("option", { name: "London flag London" }),
);
// Make sure only the label is rendered by default.
expect(valueRendered).toHaveTextContent("London");
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 option is rendered.
expect(valueRendered).toHaveTextContent("London");
within(valueRendered).getByRole("img", {
name: "London flag",
});
});
});
});