release: WRNexusJS 0.5.0

This commit is contained in:
2026-07-29 12:51:10 +05:30
parent 76c768099d
commit 6afe32f63f
456 changed files with 40879 additions and 8850 deletions
+282 -17
View File
@@ -10,8 +10,9 @@ const reference = JSON.parse(
readFileSync(join(root, "..", "..", "packages", "ui", "component-reference.json"), "utf8"),
) as {
count: number;
components: Array<{ mount: string; category: string }>;
components: Array<{ name: string; mount: string; category: string; events: string[] }>;
};
const slugOf = (name: string) => name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
test("showcase configures local Lucide icon generation", () => {
const packageJson = JSON.parse(readFileSync(join(root, "package.json"), "utf8")) as {
@@ -39,6 +40,66 @@ test("showcase uses a searchable three-column documentation shell", () => {
expect(runtime).toContain('event.key.toLowerCase() === "k"');
});
test("component sidebar shows delivery status and refreshes its active link after navigation", () => {
const layout = readFileSync(join(root, "app", "layouts", "showcase.wrn"), "utf8");
const styles = readFileSync(join(root, "app", "styles", "global.css"), "utf8");
const runtime = readFileSync(join(root, "public", "playground.js"), "utf8");
const completeComponents = [
"Accordion",
"Alert",
"Avatar",
"Avatar Group",
"Badge",
"Blockquote",
"Button",
"Button Group",
"Card",
"Carousel",
"Chat Bubble",
"Collapse",
"Checkbox",
"Date Picker",
"Device Frame",
"File Upload Progress",
"Color Picker",
"File Input",
"Input",
"Input Group",
"Radio",
"Range Slider",
"Select",
"Switch",
"Textarea",
"Time Picker",
"Advanced Select",
"Combo Box",
"Input Number",
"Pin Input",
"Strong Password",
"Toggle Count",
"Toggle Password",
];
expect(layout.match(/data-component-status="complete"/g)).toHaveLength(completeComponents.length);
expect(layout.match(/data-component-status="pending"/g)).toHaveLength(
reference.count - completeComponents.length,
);
for (const component of completeComponents) {
expect(layout).toContain(
`<span data-docs-component-name>${component}</span><small data-component-status="complete">Complete</small>`,
);
}
expect(styles).toContain('[data-component-status="working"]');
expect(runtime).toContain('window.addEventListener("wrnexus:navigated", updateActiveNavigation)');
expect(runtime).toContain('link.removeAttribute("aria-current")');
});
test("accordion examples do not include the generic composable slot placeholder", () => {
const page = readFileSync(join(detailPagesDir, "accordion.wrn"), "utf8");
expect(page).not.toContain("Composable content area.");
});
test("showcase SSR document layout maps persisted design cookies to html attributes", () => {
const source = readFileSync(join(root, "app", "layouts", "document.wrn"), "utf8");
expect(source).toContain("<html");
@@ -87,26 +148,81 @@ test("every component has a detail page with its expected live use cases", () =>
// Every detail page has one playground plus three standard demos. Interactive
// components add specialized capability demos beyond that shared baseline.
expect(detailMounts).toHaveLength(reference.count * 4 + 95);
expect(detailMounts).toHaveLength(reference.count * 4 + 249);
for (const component of reference.components) {
const expectedMounts =
component.mount === "Button"
? 9
: component.mount === "AdvancedSelect"
? 42
: component.mount === "ComboBox"
? 22
: component.mount === "InputNumber"
? 13
: component.mount === "PinInput"
? 19
: component.mount === "StrongPassword"
? 7
: component.mount === "TogglePassword"
? 11
: 4;
component.mount === "Accordion"
? 10
: component.mount === "Alert"
? 16
: component.mount === "Avatar"
? 18
: component.mount === "AvatarGroup"
? 6
: component.mount === "Badge"
? 16
: component.mount === "Button"
? 9
: component.mount === "ButtonGroup"
? 8
: component.mount === "Card"
? 24
: component.mount === "Carousel"
? 14
: component.mount === "ChatBubble"
? 5
: component.mount === "Collapse"
? 3
: component.mount === "FileUploadProgress"
? 7
: [
"Checkbox",
"ColorPicker",
"FileInput",
"Input",
"InputGroup",
"Radio",
"RangeSlider",
"Select",
"Switch",
"Textarea",
"TimePicker",
].includes(component.mount)
? component.mount === "Radio" || component.mount === "Checkbox"
? 12
: 10
: component.mount === "AdvancedSelect"
? 42
: component.mount === "ComboBox"
? 22
: component.mount === "InputNumber"
? 13
: component.mount === "PinInput"
? 19
: component.mount === "StrongPassword"
? 7
: component.mount === "ToggleCount"
? 5
: component.mount === "TogglePassword"
? 11
: 4;
expect(detailMounts.filter((mount) => mount === component.mount)).toHaveLength(expectedMounts);
}
const uploadProgressPage = readFileSync(join(detailPagesDir, "file-upload-progress.wrn"), "utf8");
for (const fileName of [
"design-system.zip",
"product-images.tar",
"launch-video.mp4",
"quarterly-report.pdf",
"customer-export.csv",
"brand-assets.fig",
]) {
expect(uploadProgressPage).toContain(fileName);
}
expect(uploadProgressPage).toContain('status="complete"');
expect(uploadProgressPage).toContain('status="error"');
expect(uploadProgressPage).toContain('status="cancelled"');
}, 15_000);
test("every detail page has a type-aware realtime playground", () => {
@@ -149,6 +265,155 @@ test("detail pages show component syntax beside each live preview", () => {
expect(buttonPage).not.toContain('<details class="demo-code">');
});
test("every detail page lists all public events immediately before props", () => {
for (const component of reference.components) {
const source = readFileSync(join(detailPagesDir, `${slugOf(component.name)}.wrn`), "utf8");
const eventsIndex = source.indexOf('<section id="events"');
const propsIndex = source.indexOf('<section id="api"');
expect(eventsIndex).toBeGreaterThan(-1);
expect(propsIndex).toBeGreaterThan(eventsIndex);
expect(source.slice(eventsIndex, propsIndex)).toContain("Component events");
expect(source).toContain('<a href="#events">Events</a>');
if (component.events.length === 0) {
expect(source.slice(eventsIndex, propsIndex)).toContain("No public component events.");
} else {
for (const event of component.events) {
expect(source.slice(eventsIndex, propsIndex)).toContain(`<code>@${event}</code>`);
}
}
}
});
test("button documents click, focus, and blur events", () => {
const source = readFileSync(join(detailPagesDir, "button.wrn"), "utf8");
for (const event of ["click", "focus", "blur"]) {
expect(source).toContain(`<code>@${event}</code>`);
}
});
test("button group documents every requested layout and size", () => {
const source = readFileSync(join(detailPagesDir, "button-group.wrn"), "utf8");
for (const heading of [
"Example",
"Small",
"Medium",
"Large",
"Responsive stack",
"Vertical stack",
"Button toolbar",
]) {
expect(source).toContain(`<h2>${heading}</h2>`);
}
expect(source.match(/class="demo-workbench"/g)).toHaveLength(7);
expect(source).toContain('<span class="detail-toc-group">Basic usage</span>');
expect(source).toContain('<span class="detail-toc-group">Sizes</span>');
expect(source).toContain('responsive="true"');
expect(source).toContain('orientation="vertical"');
expect(source).toContain('toolbar="true"');
expect(source).toContain('ariaLabel="Text formatting"');
for (const event of ["click", "select", "change"]) {
expect(source).toContain(`<code>@${event}</code>`);
}
});
test("radio documents single, grouped, card, list, aligned, and validation layouts", () => {
const source = readFileSync(join(detailPagesDir, "radio.wrn"), "utf8");
for (const heading of [
"Default radio",
"Disabled",
"Inline radio group",
"Vertical radio group",
"Radios with descriptions",
"Radio cards",
"Vertical radio cards",
"Right-aligned radio",
"Radio list group",
"Horizontal radio list group",
"Validation states",
]) {
expect(source).toContain(`<h2>${heading}</h2>`);
}
});
test("checkbox documents mixed, grouped, card, list, aligned, and validation layouts", () => {
const source = readFileSync(join(detailPagesDir, "checkbox.wrn"), "utf8");
for (const heading of [
"Default checkbox",
"Indeterminate",
"Disabled",
"Checkbox group options",
"Checkboxes with descriptions",
"Checkbox cards",
"Vertical checkbox cards",
"Right-aligned checkbox",
"Checkbox list group",
"Horizontal checkbox list group",
"Validation states",
]) {
expect(source).toContain(`<h2>${heading}</h2>`);
}
});
test("card documents content, media, animation, layout, alert, and panel capabilities", () => {
const source = readFileSync(join(detailPagesDir, "card.wrn"), "utf8");
const headings = [
"Default card",
"Body",
"Simple card",
"Header and footer",
"Footer only",
"Small",
"Default",
"Large",
"Navigation",
"Navigation with select on mobile",
"Top image",
"Bottom image",
"Image overlays",
"Image scaling animation on hover",
"Transition on hover",
"Horizontal",
"Card group",
"Top bordered card",
"Card actions",
"Card with alert",
"Centered body content",
"Empty state",
"Scrollable body",
];
for (const heading of headings) {
expect(source).toContain(`<h2>${heading}</h2>`);
}
expect(source.match(/class="demo-workbench"/g)).toHaveLength(23);
for (const section of [
"Basic usage",
"Content",
"Structure",
"Sizes",
"Navigation",
"Images",
"Animations",
"Card layout",
"Panels",
]) {
expect(source).toContain(`<span class="detail-toc-group">${section}</span>`);
}
expect(source).toContain('imagePosition="overlay"');
expect(source).toContain('hover="image"');
expect(source).toContain('hover="raise"');
expect(source).toContain('layout="horizontal"');
expect(source).toContain('mobileNavigation="true"');
expect(source).toContain('scrollable="true"');
for (const event of ["click", "action", "navigate", "dismiss", "load", "error"]) {
expect(source).toContain(`<code>@${event}</code>`);
}
});
test("advanced select and combobox document their complete event contract", () => {
for (const slug of ["advanced-select", "combo-box"]) {
const source = readFileSync(join(detailPagesDir, `${slug}.wrn`), "utf8");