import { describe, expect, it } from "vitest"; import { resolvePanelTabKey, tabControlId, tabPanelId, tabPanelProps, } from "./panelTabsUtils"; const TABS = ["button", "notification", "advanced"] as const; describe("panel tab ids", () => { it("derives distinct, stable ids for the tab and its panel", () => { expect(tabControlId("cart", "button")).toBe("cart-tab-button"); expect(tabPanelId("cart", "button")).toBe("cart-panel-button"); expect(tabControlId("cart", "button")).not.toBe( tabPanelId("cart", "button"), ); }); it("namespaces ids per tablist so nested tablists never collide", () => { expect(tabControlId("cart", "backend")).not.toBe( tabControlId("cart-ajax", "backend"), ); }); it("wires the panel back to the tab that labels it", () => { expect(tabPanelProps("cart", "advanced")).toEqual({ role: "tabpanel", id: "cart-panel-advanced", "aria-labelledby": "cart-tab-advanced", }); }); }); describe("resolvePanelTabKey", () => { it("moves right and left along the tablist", () => { expect(resolvePanelTabKey("ArrowRight", "button", TABS)).toBe( "notification", ); expect(resolvePanelTabKey("ArrowLeft", "notification", TABS)).toBe( "button", ); }); it("wraps at both ends", () => { expect(resolvePanelTabKey("ArrowRight", "advanced", TABS)).toBe("button"); expect(resolvePanelTabKey("ArrowLeft", "button", TABS)).toBe("advanced"); }); it("jumps to the first and last tab with Home/End", () => { expect(resolvePanelTabKey("Home", "notification", TABS)).toBe("button"); expect(resolvePanelTabKey("End", "notification", TABS)).toBe("advanced"); }); it("mirrors the horizontal arrows under RTL", () => { expect(resolvePanelTabKey("ArrowRight", "notification", TABS, true)).toBe( "button", ); expect(resolvePanelTabKey("ArrowLeft", "notification", TABS, true)).toBe( "advanced", ); }); it("leaves Home/End unmirrored under RTL (they are positional, not directional)", () => { expect(resolvePanelTabKey("Home", "advanced", TABS, true)).toBe("button"); expect(resolvePanelTabKey("End", "button", TABS, true)).toBe("advanced"); }); it("does not swallow vertical arrows — the panel must stay scrollable", () => { expect(resolvePanelTabKey("ArrowDown", "button", TABS)).toBeNull(); expect(resolvePanelTabKey("ArrowUp", "button", TABS)).toBeNull(); }); it("leaves Enter/Space to the button's native activation", () => { expect(resolvePanelTabKey("Enter", "button", TABS)).toBeNull(); expect(resolvePanelTabKey(" ", "button", TABS)).toBeNull(); }); it("treats an unknown current tab as the first tab rather than throwing", () => { expect(resolvePanelTabKey("ArrowRight", "does-not-exist", TABS)).toBe( "notification", ); }); it("returns null for an empty tablist", () => { expect(resolvePanelTabKey("ArrowRight", "button", [])).toBeNull(); }); });