mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
* chore(root): menu and select components in progress * chore(root): merged with main * feat(menu): stories created * feat(dropdown): menu migrated to menu component * feat(select): base implementation in progress * feat(select): listbox implemented * feat(select): scroll overflow and auto scroll added * feat(select): colors & variants improved, inside placement complete, outside pending * feat(scroll-shadow): new component in progress * feat(select): scroll shadow integrated * fix(select): popover implementation, more scroll shadow stories added * feat(listbox): custom styles example added to stories * chore(listbox): overflow hidden removed * feat(select): variants and styles completed * chore(select): description story added * feat(select): more stories added * fix(select): dynamic collections inference, styles improved, more stories added * fix(select): auto scroll to selected item, scroll padding added * chore(select): scroll behavior changed to nearest * feat(select): custom item story added * fix(select): autoscroll fixed * feat(select): multi select support added * feat(select): more examples added, clean-package modified to remove dev packages * chore(modal): useImperativeHandle removed * feat(select): render value story improved * feat(docs): listbox & scroll shadow docs done * feat(docs): select documentation in progress * fix: select aria label (#1425) * feat(docs): more examples added * feat(docs): select multiple added, storybook favicon changed * fix: added value to aria label of select (#1431) * feat(select): more examples added, storybook favicon changed * fix(popover): dialog role removed * feat(select): api added, async loading exampled added * fix: fixed list box on click not working with sr click (#1439) * feat(select): async items loading support added and documented * feat(root): input styles updated to be as the select ones * chore(root): listbox and scroll shadow readme updated * feat(select): ts examples added, banner updated * fix(popover): voiceover closes when focusing an item fixed * chore(select): focus scope removed * fix(popover): free solo popover added to use without a trigger * feat(select): blog post added * chore(docs): search meta generated, multi controlled onchange example fixed * chore(root): changeset added --------- Co-authored-by: Jakob Guddas <github@jguddas.de>
277 lines
8.5 KiB
TypeScript
277 lines
8.5 KiB
TypeScript
import * as React from "react";
|
|
import {act, render} from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
|
|
import {Listbox, ListboxItem, ListboxSection} from "../src";
|
|
|
|
describe("Listbox", () => {
|
|
it("should render correctly", () => {
|
|
const wrapper = render(
|
|
<Listbox aria-label="Actions" onAction={alert}>
|
|
<ListboxItem key="new">New file</ListboxItem>
|
|
<ListboxItem key="copy">Copy link</ListboxItem>
|
|
<ListboxItem key="edit">Edit file</ListboxItem>
|
|
<ListboxItem key="delete" color="danger">
|
|
Delete file
|
|
</ListboxItem>
|
|
</Listbox>,
|
|
);
|
|
|
|
expect(() => wrapper.unmount()).not.toThrow();
|
|
});
|
|
|
|
it("ref should be forwarded", () => {
|
|
const ref = React.createRef<HTMLUListElement>();
|
|
|
|
render(
|
|
<Listbox ref={ref} aria-label="Actions" onAction={alert}>
|
|
<ListboxItem key="new">New file</ListboxItem>
|
|
<ListboxItem key="copy">Copy link</ListboxItem>
|
|
<ListboxItem key="edit">Edit file</ListboxItem>
|
|
<ListboxItem key="delete" color="danger">
|
|
Delete file
|
|
</ListboxItem>
|
|
</Listbox>,
|
|
);
|
|
expect(ref.current).not.toBeNull();
|
|
});
|
|
|
|
it("should render correctly (static)", () => {
|
|
const wrapper = render(
|
|
<Listbox aria-label="Actions" onAction={alert}>
|
|
<ListboxItem key="new">New file</ListboxItem>
|
|
<ListboxItem key="copy">Copy link</ListboxItem>
|
|
<ListboxItem key="edit">Edit file</ListboxItem>
|
|
<ListboxItem key="delete" color="danger">
|
|
Delete file
|
|
</ListboxItem>
|
|
</Listbox>,
|
|
);
|
|
|
|
expect(() => wrapper.unmount()).not.toThrow();
|
|
});
|
|
|
|
it("should render correctly (dynamic)", () => {
|
|
const listboxItems = [
|
|
{key: "new", name: "New File"},
|
|
{key: "copy", name: "Copy Link"},
|
|
{key: "edit", name: "Edit File"},
|
|
{key: "delete", name: "Delete File"},
|
|
];
|
|
|
|
const wrapper = render(
|
|
<Listbox aria-label="Actions" items={listboxItems}>
|
|
{(item: any) => <ListboxItem key={item.key}>{item.name}</ListboxItem>}
|
|
</Listbox>,
|
|
);
|
|
|
|
expect(() => wrapper.unmount()).not.toThrow();
|
|
});
|
|
|
|
it("should render correctly with section (static)", () => {
|
|
const wrapper = render(
|
|
<Listbox aria-label="Actions" onAction={alert}>
|
|
<ListboxSection title="Actions">
|
|
<ListboxItem key="new">New file</ListboxItem>
|
|
<ListboxItem key="copy">Copy link</ListboxItem>
|
|
</ListboxSection>
|
|
<ListboxSection title="Danger Zone">
|
|
<ListboxItem key="edit">Edit file</ListboxItem>
|
|
<ListboxItem key="delete" color="danger">
|
|
Delete file
|
|
</ListboxItem>
|
|
</ListboxSection>
|
|
</Listbox>,
|
|
);
|
|
|
|
expect(() => wrapper.unmount()).not.toThrow();
|
|
});
|
|
|
|
it("should render correctly with section (dynamic)", () => {
|
|
const listboxItems = [
|
|
{
|
|
key: "actions",
|
|
title: "Actions",
|
|
children: [
|
|
{key: "new", name: "New File"},
|
|
{key: "copy", name: "Copy Link"},
|
|
{key: "edit", name: "Edit File"},
|
|
],
|
|
},
|
|
{
|
|
key: "danger",
|
|
title: "Danger Zone",
|
|
children: [{key: "delete", name: "Delete File"}],
|
|
},
|
|
];
|
|
|
|
const wrapper = render(
|
|
<Listbox aria-label="Actions" items={listboxItems}>
|
|
{(section: any) => (
|
|
<ListboxSection aria-label={section.title} items={section.children} title={section.title}>
|
|
{(item: any) => <ListboxItem key={item.key}>{item.name}</ListboxItem>}
|
|
</ListboxSection>
|
|
)}
|
|
</Listbox>,
|
|
);
|
|
|
|
expect(() => wrapper.unmount()).not.toThrow();
|
|
});
|
|
|
|
it("should work with single selection (controlled)", async () => {
|
|
let onSelectionChange = jest.fn();
|
|
|
|
const wrapper = render(
|
|
<Listbox
|
|
disallowEmptySelection
|
|
aria-label="Actions"
|
|
selectionMode="single"
|
|
onSelectionChange={onSelectionChange}
|
|
>
|
|
<ListboxItem key="new">New file</ListboxItem>
|
|
<ListboxItem key="copy">Copy link</ListboxItem>
|
|
<ListboxItem key="edit">Edit file</ListboxItem>
|
|
<ListboxItem key="delete" color="danger">
|
|
Delete file
|
|
</ListboxItem>
|
|
</Listbox>,
|
|
);
|
|
|
|
let listbox = wrapper.getByRole("listbox");
|
|
|
|
expect(listbox).toBeTruthy();
|
|
|
|
let listboxItems = wrapper.getAllByRole("option");
|
|
|
|
expect(listboxItems.length).toBe(4);
|
|
|
|
await act(async () => {
|
|
await userEvent.click(listboxItems[1]);
|
|
|
|
expect(onSelectionChange).toBeCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
it("should work with multiple selection (controlled)", async () => {
|
|
let onSelectionChange = jest.fn();
|
|
|
|
const wrapper = render(
|
|
<Listbox
|
|
disallowEmptySelection
|
|
aria-label="Actions"
|
|
selectionMode="multiple"
|
|
onSelectionChange={onSelectionChange}
|
|
>
|
|
<ListboxItem key="new">New file</ListboxItem>
|
|
<ListboxItem key="copy">Copy link</ListboxItem>
|
|
<ListboxItem key="edit">Edit file</ListboxItem>
|
|
<ListboxItem key="delete" color="danger">
|
|
Delete file
|
|
</ListboxItem>
|
|
</Listbox>,
|
|
);
|
|
|
|
let listbox = wrapper.getByRole("listbox");
|
|
|
|
expect(listbox).toBeTruthy();
|
|
|
|
let listboxItems = wrapper.getAllByRole("option");
|
|
|
|
expect(listboxItems.length).toBe(4);
|
|
|
|
await act(async () => {
|
|
await userEvent.click(listboxItems[0]);
|
|
|
|
expect(onSelectionChange).toBeCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
it("should show checkmarks if selectionMode is single and has a selected item", () => {
|
|
const wrapper = render(
|
|
<Listbox aria-label="Actions" selectedKeys={["new"]} selectionMode="single">
|
|
<ListboxItem key="new">New file</ListboxItem>
|
|
<ListboxItem key="copy">Copy link</ListboxItem>
|
|
<ListboxItem key="edit">Edit file</ListboxItem>
|
|
<ListboxItem key="delete" color="danger">
|
|
Delete file
|
|
</ListboxItem>
|
|
</Listbox>,
|
|
);
|
|
|
|
let listboxItems = wrapper.getAllByRole("option");
|
|
|
|
expect(listboxItems.length).toBe(4);
|
|
|
|
expect(listboxItems[0].getAttribute("aria-selected")).toBe("true");
|
|
expect(listboxItems[1].getAttribute("aria-selected")).toBe("false");
|
|
expect(listboxItems[2].getAttribute("aria-selected")).toBe("false");
|
|
expect(listboxItems[3].getAttribute("aria-selected")).toBe("false");
|
|
|
|
let svg = listboxItems[0].querySelector("svg");
|
|
|
|
expect(svg).toBeTruthy();
|
|
|
|
expect(svg?.getAttribute("data-selected")).toBe("true");
|
|
});
|
|
|
|
it("should show multiple checkmarks if selectionMode is multiple and has selected items", () => {
|
|
const wrapper = render(
|
|
<Listbox aria-label="Actions" selectedKeys={["new", "copy"]} selectionMode="multiple">
|
|
<ListboxItem key="new">New file</ListboxItem>
|
|
<ListboxItem key="copy">Copy link</ListboxItem>
|
|
<ListboxItem key="edit">Edit file</ListboxItem>
|
|
<ListboxItem key="delete" color="danger">
|
|
Delete file
|
|
</ListboxItem>
|
|
</Listbox>,
|
|
);
|
|
|
|
let listboxItems = wrapper.getAllByRole("option");
|
|
|
|
expect(listboxItems.length).toBe(4);
|
|
|
|
expect(listboxItems[0].getAttribute("aria-selected")).toBe("true");
|
|
expect(listboxItems[1].getAttribute("aria-selected")).toBe("true");
|
|
expect(listboxItems[2].getAttribute("aria-selected")).toBe("false");
|
|
expect(listboxItems[3].getAttribute("aria-selected")).toBe("false");
|
|
|
|
let checkmark1 = listboxItems[0].querySelector("svg");
|
|
|
|
expect(checkmark1).toBeTruthy();
|
|
|
|
expect(checkmark1?.getAttribute("data-selected")).toBe("true");
|
|
|
|
let checkmark2 = listboxItems[1].querySelector("svg");
|
|
|
|
expect(checkmark2).toBeTruthy();
|
|
|
|
expect(checkmark2?.getAttribute("data-selected")).toBe("true");
|
|
});
|
|
|
|
it("should not show checkmarks if selectionMode not defined", () => {
|
|
const wrapper = render(
|
|
<Listbox aria-label="Actions" selectedKeys={["new", "copy"]}>
|
|
<ListboxItem key="new">New file</ListboxItem>
|
|
<ListboxItem key="copy">Copy link</ListboxItem>
|
|
<ListboxItem key="edit">Edit file</ListboxItem>
|
|
<ListboxItem key="delete" color="danger">
|
|
Delete file
|
|
</ListboxItem>
|
|
</Listbox>,
|
|
);
|
|
|
|
let listboxItems = wrapper.getAllByRole("option");
|
|
|
|
expect(listboxItems.length).toBe(4);
|
|
|
|
expect(listboxItems[0].getAttribute("aria-selected")).toBeFalsy();
|
|
expect(listboxItems[1].getAttribute("aria-selected")).toBeFalsy();
|
|
expect(listboxItems[2].getAttribute("aria-selected")).toBeFalsy();
|
|
expect(listboxItems[3].getAttribute("aria-selected")).toBeFalsy();
|
|
|
|
let checkmark1 = listboxItems[0].querySelector("svg");
|
|
|
|
expect(checkmark1).toBeFalsy();
|
|
});
|
|
});
|