From fe86570f457122f475c704c0466eabaac55f22f0 Mon Sep 17 00:00:00 2001 From: Ajay Ghanwat Date: Tue, 25 Aug 2026 17:59:10 +0530 Subject: [PATCH] fix(ui): render dynamic tab panels and ids --- packages/ui/components/Tabs.wrn | 22 +++++++++++++++++----- packages/ui/test/ui.test.ts | 7 ++++++- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/packages/ui/components/Tabs.wrn b/packages/ui/components/Tabs.wrn index 0fd4d601..236fb307 100644 --- a/packages/ui/components/Tabs.wrn +++ b/packages/ui/components/Tabs.wrn @@ -41,6 +41,18 @@ component Tabs { return String(item.value || item.id || index) } + shared function tabId(item, index) { + return "wrn-tab-" + valueOf(item, index) + } + + shared function panelId(item, index) { + return "wrn-panel-" + valueOf(item, index) + } + + shared function panelSlot(item, index) { + return "panel-" + valueOf(item, index) + } + /* * In url mode the query parameter is the source of truth, not local state. * @@ -130,8 +142,8 @@ component Tabs { role="tab" data-value='{valueOf(item, index)}' data-wrn-roving-item="true" - id='wrn-tab-{valueOf(item, index)}' - aria-controls='wrn-panel-{valueOf(item, index)}' + id='{tabId(item, index)}' + aria-controls='{panelId(item, index)}' aria-selected='{isSelected(item, index) ? "true" : "false"}' aria-disabled='{item.disabled ? "true" : "false"}' @click='selectTab(item, index, event)' @@ -148,8 +160,8 @@ component Tabs {
@@ -158,7 +170,7 @@ component Tabs {

{item.description}

{item.content}
- +
{/each} diff --git a/packages/ui/test/ui.test.ts b/packages/ui/test/ui.test.ts index 80eb8582..9f724cb4 100644 --- a/packages/ui/test/ui.test.ts +++ b/packages/ui/test/ui.test.ts @@ -2879,8 +2879,13 @@ test("tabs use wrn classes and declare real outputs instead of raw events", asyn expect(tabs).toHaveLength(2); expect(tabs[0]!.getAttribute("aria-selected")).toBe("true"); expect(tabs[1]!.getAttribute("aria-selected")).toBe("false"); + expect(tabs[0]!.id).toBe("wrn-tab-overview"); + expect(tabs[0]!.getAttribute("aria-controls")).toBe("wrn-panel-overview"); expect(dom.querySelector(".wrn-tabs")).not.toBeNull(); - expect(dom.querySelectorAll('[role="tabpanel"]')).toHaveLength(2); + const panels = dom.querySelectorAll('[role="tabpanel"]'); + expect(panels).toHaveLength(2); + expect(panels[0]!.id).toBe("wrn-panel-overview"); + expect(panels[0]!.getAttribute("aria-labelledby")).toBe("wrn-tab-overview"); }); test("tabs in url mode declare the query parameter they sync to", async () => {