From 9030e7d3a30edb27276d3055415018a7f83011d4 Mon Sep 17 00:00:00 2001 From: Ajay Ghanwat Date: Wed, 30 Sep 2026 16:23:13 +0530 Subject: [PATCH] fix(ui): readable date-time trigger, showcase code panel, release ui 0.8.88 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DatePicker trigger shows the value formatted in the component locale ("Oct 2, 2026 · 9:30 AM", "Jul 15, 2026"); the hidden input still submits the ISO value. - Showcase playground code panel: the reactive runtime read "{...}" in the rewritten code as interpolations and blanked every object in array props. Braces are now written as their own spans, like the server-rendered code. Co-Authored-By: Claude Opus 5.5 --- bun.lock | 2 +- .../component-showcase/public/playground.js | 26 +++++++++++++++++-- packages/ui/components/DatePicker.wrn | 16 +++++++++++- packages/ui/package.json | 2 +- packages/ui/test/ui.test.ts | 2 +- 5 files changed, 42 insertions(+), 6 deletions(-) diff --git a/bun.lock b/bun.lock index cc1f47ee..d86712a9 100644 --- a/bun.lock +++ b/bun.lock @@ -749,7 +749,7 @@ }, "packages/ui": { "name": "@wrnexus/ui", - "version": "0.8.87", + "version": "0.8.88", "dependencies": { "@wrnexus/core": "workspace:*", }, diff --git a/examples/component-showcase/public/playground.js b/examples/component-showcase/public/playground.js index 8118e76c..bed5ce5a 100644 --- a/examples/component-showcase/public/playground.js +++ b/examples/component-showcase/public/playground.js @@ -169,11 +169,33 @@ const mount = publicTag ? [] : [`data-component="${sourceAttribute(component)}"`]; const lines = [...mount, ...attributes].map((attribute) => ` ${attribute}`).join("\n"); const opening = lines ? `<${tag}\n${lines}` : `<${tag}`; - code.textContent = hasSlot + let source = hasSlot ? `${opening}>\n \n` : `${opening}\n/>`; - if (declarations.length) code.textContent = `page ${component.replace(/[^A-Za-z0-9]/g, "")}Example {\n${declarations.join("\n")}\n view {\n${code.textContent}\n }\n}`; + if (declarations.length) source = `page ${component.replace(/[^A-Za-z0-9]/g, "")}Example {\n${declarations.join("\n")}\n view {\n${source}\n }\n}`; + writeCode(code, source); + } + /* + * The reactive runtime treats "{expr}" inside a text node as an + * interpolation, so a sample like { "label": "Mon" } written with + * textContent was evaluated and rendered as an empty string. Putting each + * brace in its own means no single text node ever holds a {...} + * pair -- the same markup the generator uses for the server-rendered code. + */ + function writeCode(code, source) { + const fragment = document.createDocumentFragment(); + for (const part of source.split(/([{}])/)) { + if (!part) continue; + if (part === "{" || part === "}") { + const brace = document.createElement("span"); + brace.textContent = part; + fragment.append(brace); + } else { + fragment.append(document.createTextNode(part)); + } + } + code.replaceChildren(fragment); } function valuesFrom(form) { diff --git a/packages/ui/components/DatePicker.wrn b/packages/ui/components/DatePicker.wrn index 76c71acd..512d5679 100644 --- a/packages/ui/components/DatePicker.wrn +++ b/packages/ui/components/DatePicker.wrn @@ -52,6 +52,20 @@ size: string = "default" output.change({ value: currentValue, name: name, sourceEvent: sourceEvent }) } + // Human-readable trigger text in the component locale. The hidden input + // keeps the ISO value, so forms still submit 2026-10-02 / 2026-10-02T09:30. + shared function displayValue(parts, label, time, clock) { + if (!currentValue) { return placeholder || "Select date" } + parts = currentValue.slice(0, 10).split("-") + if (parts.length !== 3 || !Number(parts[0]) || !Number(parts[1]) || !Number(parts[2])) { return currentValue } + label = Intl.DateTimeFormat(locale, { dateStyle: "medium", timeZone: "UTC" }).format(Date.UTC(Number(parts[0]), Number(parts[1]) - 1, Number(parts[2]))) + if (currentValue.charAt(10) === "T") { + time = currentValue.slice(11, 16).split(":") + clock = Intl.DateTimeFormat(locale, { timeStyle: "short", timeZone: "UTC" }).format(Date.UTC(1970, 0, 1, Number(time[0]) || 0, Number(time[1]) || 0)) + return label + " · " + clock + } + return label + } shared function calendarYears() { if (years.includes(Number(selectedYear))) { return years } return years.concat(Number(selectedYear)) @@ -142,7 +156,7 @@ size: string = "default" view {
{#if cornerHint}{cornerHint}{/if}
-
{#if currentValue && !readonly && !disabled}{/if}
+
{#if currentValue && !readonly && !disabled}{/if}
{#if helperText}{helperText}{/if}{error}
diff --git a/packages/ui/package.json b/packages/ui/package.json index 93463a30..58acb96a 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ui", - "version": "0.8.87", + "version": "0.8.88", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/ui/test/ui.test.ts b/packages/ui/test/ui.test.ts index 91928a39..b35e6136 100644 --- a/packages/ui/test/ui.test.ts +++ b/packages/ui/test/ui.test.ts @@ -1687,7 +1687,7 @@ test("date picker renders a custom calendar trigger with accessible field state )[14] as HTMLButtonElement; fifteenth.dispatchEvent(new (dom.window as any).Event("click", { bubbles: true })); expect(input.value).toBe("2026-07-15"); - expect(dom.querySelector(".wrn-next__date-trigger span")?.textContent).toBe("2026-07-15"); + expect(dom.querySelector(".wrn-next__date-trigger span")?.textContent).toBe("Jul 15, 2026"); }); test("time picker uses a custom reactive panel instead of the browser time control", async () => {