feat(ui): build the Stepper component
Replaces a scaffold that rendered bare anchors with ordered steps: complete, current and upcoming status derived from the active index, horizontal or vertical, optional icons, and indexed named slots (step-0, step-1, ...) for authoring a step body by hand. Also tightens the roving contract from the previous commit. A template writes data-wrn-roving="" or data-wrn-roving-item="false" to mean not this time, but a bare [attr] selector matches either, so a read-only stepper would still have taken arrow-key focus. The container now requires a named axis, while a bare item marker still counts as opted in. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2493,3 +2493,50 @@ test("pagination survives an empty dataset", async () => {
|
||||
const dom = mountHtml(html);
|
||||
expect(dom.querySelector(".wire-pagination")).not.toBeNull();
|
||||
});
|
||||
|
||||
test("stepper marks complete, current and upcoming steps", async () => {
|
||||
const source = readFileSync(uiComponentPath("Stepper"), "utf8");
|
||||
const html = await renderComponent(source, {
|
||||
steps: [
|
||||
{ label: "Account", description: "Your details" },
|
||||
{ label: "Billing", description: "Payment method" },
|
||||
{ label: "Confirm", description: "Review and submit" },
|
||||
],
|
||||
active: 1,
|
||||
});
|
||||
const dom = mountHtml(html);
|
||||
const items = [...dom.querySelectorAll(".wire-stepper__step")];
|
||||
|
||||
expect(items).toHaveLength(3);
|
||||
expect(items[0]!.getAttribute("data-status")).toBe("complete");
|
||||
expect(items[1]!.getAttribute("data-status")).toBe("current");
|
||||
expect(items[2]!.getAttribute("data-status")).toBe("upcoming");
|
||||
expect(items[1]!.getAttribute("aria-current")).toBe("step");
|
||||
expect(dom.querySelector(".wire-stepper")!.tagName.toLowerCase()).toBe("ol");
|
||||
});
|
||||
|
||||
test("stepper renders vertically and clamps an out-of-range active index", async () => {
|
||||
const source = readFileSync(uiComponentPath("Stepper"), "utf8");
|
||||
const html = await renderComponent(source, {
|
||||
steps: [{ label: "One" }, { label: "Two" }],
|
||||
active: 99,
|
||||
orientation: "vertical",
|
||||
});
|
||||
const dom = mountHtml(html);
|
||||
const root = dom.querySelector(".wire-stepper") as HTMLElement;
|
||||
expect(root.getAttribute("data-orientation")).toBe("vertical");
|
||||
const items = [...dom.querySelectorAll(".wire-stepper__step")];
|
||||
expect(items[1]!.getAttribute("data-status")).toBe("current");
|
||||
});
|
||||
|
||||
test("clickable stepper opts into roving focus", async () => {
|
||||
const source = readFileSync(uiComponentPath("Stepper"), "utf8");
|
||||
const html = await renderComponent(source, {
|
||||
steps: [{ label: "One" }, { label: "Two" }],
|
||||
active: 0,
|
||||
clickable: true,
|
||||
});
|
||||
const dom = mountHtml(html);
|
||||
expect(dom.querySelector('[data-wrn-roving="horizontal"]')).not.toBeNull();
|
||||
expect(dom.querySelectorAll("[data-wrn-roving-item]").length).toBe(2);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user