feat(ui): build remaining component scaffolds
This commit is contained in:
@@ -30,11 +30,11 @@ test("global UI CSS stays below its migration ratchet", () => {
|
||||
expect(gzipSync(new TextEncoder().encode(uiCss())).length).toBeLessThanOrEqual(12_672);
|
||||
});
|
||||
|
||||
test("only the five scheduled component builds remain without local styles", () => {
|
||||
test("every bundled component carries local styles", () => {
|
||||
const unstyled = uiComponentNames().filter(
|
||||
(name) => !/^\s{2,4}style \{/m.test(readFileSync(uiComponentPath(name), "utf8")),
|
||||
);
|
||||
expect(unstyled.sort()).toEqual(["Chart", "Clipboard", "Confetti", "CopyMarkup", "TreeView"]);
|
||||
expect(unstyled).toEqual([]);
|
||||
});
|
||||
|
||||
test("generated component reference documents every bundled component and its props", () => {
|
||||
@@ -45,7 +45,7 @@ test("generated component reference documents every bundled component and its pr
|
||||
readFileSync(join(uiComponentsDir(), "..", "component-catalog.json"), "utf8"),
|
||||
) as { components: Array<{ name: string }> };
|
||||
expect(reference.count).toBe(uiComponentNames().length);
|
||||
expect(reference.count).toBe(103);
|
||||
expect(reference.count).toBe(102);
|
||||
expect(reference.components.map((component) => component.name)).toEqual(
|
||||
expect.arrayContaining(uiComponentNames()),
|
||||
);
|
||||
@@ -146,7 +146,7 @@ test("removed duplicate aliases have valid canonical migration targets", () => {
|
||||
const names = new Set(uiComponentNames());
|
||||
|
||||
expect(Object.keys(migration.replacements)).toHaveLength(migration.removedCount);
|
||||
expect(migration.removedCount).toBe(5);
|
||||
expect(migration.removedCount).toBe(6);
|
||||
for (const [removed, replacement] of Object.entries(migration.replacements)) {
|
||||
expect(names.has(removed)).toBe(false);
|
||||
expect(names.has(replacement)).toBe(true);
|
||||
@@ -3200,7 +3200,7 @@ test("no component gains an output that nothing ever emits", () => {
|
||||
* A ceiling, not a target. It only ever moves down: rebuilding one of these
|
||||
* components should tighten it.
|
||||
*/
|
||||
expect(offenders.length).toBeLessThanOrEqual(7);
|
||||
expect(offenders.length).toBe(0);
|
||||
expect(offenders).not.toContain("LayoutSplitter.sizeChange");
|
||||
expect(offenders).not.toContain("CustomScrollbar.scroll");
|
||||
});
|
||||
@@ -3224,6 +3224,64 @@ test("list selects items without hrefs as well as navigation items", async () =>
|
||||
expect(events).toEqual([{ item: { label: "Run report" }, index: 0 }]);
|
||||
});
|
||||
|
||||
test("chart renders accessible SVG bars and emits point and legend outputs", async () => {
|
||||
const source = readFileSync(uiComponentPath("Chart"), "utf8");
|
||||
const dom = mountHtml(
|
||||
await renderComponent(source, { items: [{ label: "Alpha", value: 12 }, { label: "Beta", value: 6 }] }),
|
||||
);
|
||||
const root = dom.querySelector('[data-ui-component="Chart"]') as HTMLElement;
|
||||
const events: string[] = [];
|
||||
for (const name of ["dataPointClick", "select", "legendToggle"]) {
|
||||
root.addEventListener(name, () => events.push(name));
|
||||
}
|
||||
expect(root.querySelectorAll("svg rect")).toHaveLength(2);
|
||||
(root.querySelector("svg rect") as SVGElement).dispatchEvent(
|
||||
new (dom.window as { Event: typeof Event }).Event("click", { bubbles: true }),
|
||||
);
|
||||
(root.querySelector(".wire-chart__legend button") as HTMLButtonElement).click();
|
||||
expect(events).toEqual(["dataPointClick", "select", "legendToggle"]);
|
||||
});
|
||||
|
||||
test("tree view expands branches, selects nodes, and emits public outputs", async () => {
|
||||
const source = readFileSync(uiComponentPath("TreeView"), "utf8");
|
||||
const dom = mountHtml(
|
||||
await renderComponent(source, {
|
||||
items: [{ value: "docs", label: "Docs", children: [{ value: "guide", label: "Guide" }] }],
|
||||
}),
|
||||
);
|
||||
const root = dom.querySelector('[data-ui-component="TreeView"]') as HTMLElement;
|
||||
const events: string[] = [];
|
||||
for (const name of ["toggle", "expand", "select"]) root.addEventListener(name, () => events.push(name));
|
||||
(root.querySelector(".wire-tree-view__toggle") as HTMLButtonElement).click();
|
||||
expect(root.querySelector(".wire-tree-view__group")?.getAttribute("data-show")).toBe("true");
|
||||
(root.querySelector('[aria-level="2"] .wire-tree-view__node') as HTMLButtonElement).click();
|
||||
expect(events).toEqual(["toggle", "expand", "select"]);
|
||||
});
|
||||
|
||||
test("confetti completes deferred bursts and clipboard reports successful copies", async () => {
|
||||
const confetti = mountHtml(await renderComponent(readFileSync(uiComponentPath("Confetti"), "utf8"), { duration: 5, count: 4 }));
|
||||
const confettiRoot = confetti.querySelector('[data-ui-component="Confetti"]') as HTMLElement;
|
||||
const confettiEvents: string[] = [];
|
||||
confettiRoot.addEventListener("start", () => confettiEvents.push("start"));
|
||||
confettiRoot.addEventListener("complete", () => confettiEvents.push("complete"));
|
||||
(confettiRoot.querySelector("button") as HTMLButtonElement).click();
|
||||
await new Promise((resolve) => setTimeout(resolve, 15));
|
||||
expect(confettiEvents).toEqual(["start", "complete"]);
|
||||
|
||||
const clipboard = mountHtml(await renderComponent(readFileSync(uiComponentPath("Clipboard"), "utf8"), { value: "npm add wrnexus" }));
|
||||
Object.defineProperty((clipboard.window as { navigator: object }).navigator, "clipboard", {
|
||||
configurable: true,
|
||||
value: { writeText: () => Promise.resolve() },
|
||||
});
|
||||
const clipboardRoot = clipboard.querySelector('[data-ui-component="Clipboard"]') as HTMLElement;
|
||||
const clipboardEvents: string[] = [];
|
||||
for (const name of ["copy", "success"]) clipboardRoot.addEventListener(name, () => clipboardEvents.push(name));
|
||||
(clipboardRoot.querySelector("button") as HTMLButtonElement).click();
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
expect(clipboardEvents).toEqual(["copy", "success"]);
|
||||
expect(clipboardRoot.dataset.status).toBe("success");
|
||||
});
|
||||
|
||||
test("a declared output is never emitted as a hand-built CustomEvent", () => {
|
||||
/*
|
||||
* The failure this prevents is silent in both directions: the component
|
||||
|
||||
Reference in New Issue
Block a user