// @vitest-environment jsdom // // E28 S3c: the field primitives (docs/DESIGN.md A4-A6): one frame per rung, the native controls handing // back values, and the dropdown trigger by context. import { afterEach, describe, expect, it, vi } from "vitest"; import { cleanup, fireEvent, render, screen } from "@testing-library/react"; import { fieldClass } from "./fieldStyles"; import { Input, Textarea } from "./Input"; import { CheckRow, Slider } from "./NativeControls"; import { SelectTrigger } from "./SelectTrigger"; afterEach(cleanup); describe("Input", () => { it("wears the A4 frame at the chosen rung and says when it is invalid", () => { render( <> , ); const name = screen.getByRole("textbox", { name: "name" }); expect(name.className).toContain("edge-strong"); expect(name.className).toContain("h-8"); expect(name.className).toContain("rounded-lg"); expect(name.getAttribute("aria-invalid")).toBeNull(); const search = screen.getByRole("textbox", { name: "search" }); expect(search.className).toContain("h-7"); expect(search.className).toContain("rounded-full"); expect(search.getAttribute("aria-invalid")).toBe("true"); }); it("dims a wrapper frame for its disabled FIELD, never for a disabled button beside it", () => { // The header search holds a Go button that is disabled while the query is empty; a bare // `has-disabled:` faded the whole search box to 50% on the feed. const frame = fieldClass({ size: "free", pill: true, floating: true }); expect(frame).not.toMatch(/(^| )has-disabled:/); expect(frame).not.toMatch(/(^| )has-aria-invalid:/); expect(frame).toContain("has-[input:disabled]:opacity-50"); }); it("lets a caller lay it out but not re-paint its edge", () => { render(