Mats Bosson 6382a59eaf Combobox, DropdownMenu, ContextMenu, Toast
Combobox: text input + floating listbox with consumer-driven filtering,
  allowCustomValue, and aria-autocomplete support.
DropdownMenu: activation-mode menu with menuitem, menuitemcheckbox,
  menuitemradio roles, keyboard navigation, and typeahead.
ContextMenu: right-click triggered menu with virtual anchor positioning
  at pointer coordinates.
Toast: imperative API (toast(), toast.success/error/dismiss/clear) with
  reactive signal-based store and Toast.Region declarative renderer.
2026-03-29 19:23:33 +07:00

139 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { fireEvent, render, screen } from "@solidjs/testing-library";
import { createSignal } from "solid-js";
import { describe, expect, it, vi } from "vitest";
import { Combobox } from "../../../src/components/combobox/index";
describe("Combobox roles", () => {
it("input has role=combobox", () => {
render(() => (
<Combobox items={["a", "b"]}>
<Combobox.Input />
<Combobox.Content>
<Combobox.Item value="a">A</Combobox.Item>
<Combobox.Item value="b">B</Combobox.Item>
</Combobox.Content>
</Combobox>
));
expect(screen.getByRole("combobox")).toBeTruthy();
});
it("content has role=listbox when open", () => {
render(() => (
<Combobox items={["a", "b"]} defaultOpen>
<Combobox.Input />
<Combobox.Content>
<Combobox.Item value="a">A</Combobox.Item>
<Combobox.Item value="b">B</Combobox.Item>
</Combobox.Content>
</Combobox>
));
expect(screen.getByRole("listbox")).toBeTruthy();
});
});
describe("Combobox input", () => {
it("typing opens content", () => {
const onInput = vi.fn();
render(() => (
<Combobox items={["a", "b"]} onInputChange={onInput}>
<Combobox.Input />
<Combobox.Content>
<Combobox.Item value="a">A</Combobox.Item>
<Combobox.Item value="b">B</Combobox.Item>
</Combobox.Content>
</Combobox>
));
fireEvent.input(screen.getByRole("combobox"), { target: { value: "a" } });
expect(onInput).toHaveBeenCalled();
});
it("controlled value works", () => {
render(() => (
<Combobox items={["a", "b"]} value="a" onValueChange={() => {}}>
<Combobox.Input />
<Combobox.Content>
<Combobox.Item value="a">A</Combobox.Item>
<Combobox.Item value="b">B</Combobox.Item>
</Combobox.Content>
</Combobox>
));
expect(screen.getByRole("combobox")).toBeTruthy();
});
});
describe("Combobox keyboard", () => {
it("ArrowDown highlights first item", () => {
render(() => (
<Combobox items={["a", "b"]} defaultOpen>
<Combobox.Input />
<Combobox.Content>
<Combobox.Item value="a">A</Combobox.Item>
<Combobox.Item value="b">B</Combobox.Item>
</Combobox.Content>
</Combobox>
));
fireEvent.keyDown(screen.getByRole("combobox"), { key: "ArrowDown" });
const options = screen.getAllByRole("option");
expect(options[0].getAttribute("data-highlighted")).toBe("");
});
it("Enter selects highlighted item", () => {
const onChange = vi.fn();
render(() => (
<Combobox items={["a", "b"]} defaultOpen onValueChange={onChange}>
<Combobox.Input />
<Combobox.Content>
<Combobox.Item value="a">A</Combobox.Item>
<Combobox.Item value="b">B</Combobox.Item>
</Combobox.Content>
</Combobox>
));
fireEvent.keyDown(screen.getByRole("combobox"), { key: "ArrowDown" });
fireEvent.keyDown(screen.getByRole("combobox"), { key: "Enter" });
expect(onChange).toHaveBeenCalledWith("a");
});
it("Escape closes", () => {
render(() => (
<Combobox items={["a", "b"]} defaultOpen>
<Combobox.Input />
<Combobox.Content>
<Combobox.Item value="a">A</Combobox.Item>
</Combobox.Content>
</Combobox>
));
fireEvent.keyDown(screen.getByRole("combobox"), { key: "Escape" });
expect(screen.queryByRole("listbox")).toBeNull();
});
});
describe("Combobox empty and custom", () => {
it("Empty message shown when no items", () => {
render(() => (
<Combobox items={[]} defaultOpen>
<Combobox.Input />
<Combobox.Content>
<Combobox.Empty>No results</Combobox.Empty>
</Combobox.Content>
</Combobox>
));
expect(screen.getByText("No results")).toBeTruthy();
});
it("allowCustomValue works", () => {
const onChange = vi.fn();
render(() => (
<Combobox items={[]} defaultOpen allowCustomValue onValueChange={onChange}>
<Combobox.Input />
<Combobox.Content>
<Combobox.Empty>No results</Combobox.Empty>
</Combobox.Content>
</Combobox>
));
const input = screen.getByRole("combobox") as HTMLInputElement;
fireEvent.input(input, { target: { value: "custom" } });
fireEvent.keyDown(input, { key: "Enter" });
expect(onChange).toHaveBeenCalledWith("custom");
});
});