feat: add core UI components and baseline tests
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
import { useState } from "react";
|
||||
|
||||
import { render, screen, within } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { Field, FieldDescription, FieldError } from "./field";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectSeparator,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from "./select";
|
||||
|
||||
function ReviewLaneSelect(props?: React.ComponentProps<typeof Select>) {
|
||||
return (
|
||||
<Select {...props}>
|
||||
<SelectTrigger aria-label="Review lane">
|
||||
<SelectValue placeholder="Choose a review lane" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>Review lane</SelectLabel>
|
||||
<SelectItem value="editorial">Editorial review</SelectItem>
|
||||
<SelectItem value="design">Design review</SelectItem>
|
||||
<SelectSeparator />
|
||||
<SelectItem value="legal">Legal review</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
|
||||
describe("Select", () => {
|
||||
it("renders default value and opens selectable content", async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<ReviewLaneSelect defaultValue="design" />);
|
||||
|
||||
const trigger = screen.getByRole("combobox", { name: "Review lane" });
|
||||
|
||||
expect(trigger).toHaveTextContent("Design review");
|
||||
expect(trigger).toHaveAttribute("data-slot", "trigger");
|
||||
|
||||
await user.click(trigger);
|
||||
|
||||
const listbox = await screen.findByRole("listbox");
|
||||
const designOption = within(listbox).getByRole("option", { name: "Design review" });
|
||||
|
||||
expect(listbox).toHaveAttribute("data-slot", "content");
|
||||
expect(designOption).toHaveAttribute("data-slot", "item");
|
||||
});
|
||||
|
||||
it("updates controlled value after selecting an option", async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
function ControlledSelect() {
|
||||
const [value, setValue] = useState("editorial");
|
||||
|
||||
return <ReviewLaneSelect value={value} onValueChange={setValue} />;
|
||||
}
|
||||
|
||||
render(<ControlledSelect />);
|
||||
|
||||
const trigger = screen.getByRole("combobox", { name: "Review lane" });
|
||||
expect(trigger).toHaveTextContent("Editorial review");
|
||||
|
||||
await user.click(trigger);
|
||||
await user.click(await screen.findByRole("option", { name: "Legal review" }));
|
||||
|
||||
expect(trigger).toHaveTextContent("Legal review");
|
||||
expect(screen.queryByRole("listbox")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("supports field invalid state and described-by wiring", async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(
|
||||
<Field id="routing" invalid>
|
||||
<Select>
|
||||
<SelectTrigger aria-label="Routing team">
|
||||
<SelectValue placeholder="Choose a team" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="product">Product</SelectItem>
|
||||
<SelectItem value="design">Design</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FieldDescription>Choose the primary owner.</FieldDescription>
|
||||
<FieldError>Select a team before publishing.</FieldError>
|
||||
</Field>
|
||||
);
|
||||
|
||||
const trigger = screen.getByRole("combobox", { name: "Routing team" });
|
||||
|
||||
expect(trigger).toHaveAttribute("aria-invalid", "true");
|
||||
expect(trigger).toHaveAttribute(
|
||||
"aria-describedby",
|
||||
expect.stringContaining("routing-description")
|
||||
);
|
||||
expect(trigger).toHaveAttribute(
|
||||
"aria-describedby",
|
||||
expect.stringContaining("routing-error")
|
||||
);
|
||||
|
||||
await user.click(trigger);
|
||||
expect(await screen.findByRole("option", { name: "Product" })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("exposes disabled state on the trigger", () => {
|
||||
render(
|
||||
<Select disabled>
|
||||
<SelectTrigger aria-label="Disabled select">
|
||||
<SelectValue placeholder="Disabled" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="one">One</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
|
||||
const trigger = screen.getByRole("combobox", { name: "Disabled select" });
|
||||
|
||||
expect(trigger).toBeDisabled();
|
||||
expect(trigger).toHaveAttribute("data-disabled", "");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user