From 6d0d3b108bb4595d878fda20736c40b9656d14d7 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Fri, 5 Sep 2025 09:01:36 -0700 Subject: [PATCH] fix(data-table): handle dynamic `headers` gracefully (#2195) Fixes #2193 --- src/DataTable/DataTable.svelte | 15 +++--- tests/DataTable/DataTable.test.ts | 86 +++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 7 deletions(-) diff --git a/src/DataTable/DataTable.svelte b/src/DataTable/DataTable.svelte index 11456305..2614edb0 100644 --- a/src/DataTable/DataTable.svelte +++ b/src/DataTable/DataTable.svelte @@ -263,12 +263,13 @@ expanded = expandedRowIds.length === expandableRowIds.length; } $: if (radio || batchSelection) selectable = true; - $: headerKeys = headers.map(({ key }) => key); $: tableCellsByRowId = rows.reduce((rows, row) => { - rows[row.id] = headerKeys.map((key, index) => ({ - key, - value: resolvePath(row, key), - display: headers[index].display, + rows[row.id] = headers.map((header, index) => ({ + key: header.key || `key-${index}`, + value: header.key ? resolvePath(row, header.key) : undefined, + display: header.display, + empty: header.empty, + columnMenu: header.columnMenu, })); return rows; }, {}); @@ -557,8 +558,8 @@ {/if} {#each tableCellsByRowId[row.id] as cell, j (cell.key)} - {#if headers[j].empty} - + {#if cell.empty} + {cell.display ? cell.display(cell.value, row) : cell.value} diff --git a/tests/DataTable/DataTable.test.ts b/tests/DataTable/DataTable.test.ts index 7ab91561..85a554c4 100644 --- a/tests/DataTable/DataTable.test.ts +++ b/tests/DataTable/DataTable.test.ts @@ -772,4 +772,90 @@ describe("DataTable", () => { expect.any(Object), ); }); + + it("should handle changing number of headers without crashing", async () => { + const rows = [ + { + id: "a", + name: "Load Balancer 3", + protocol: "HTTP", + port: 3000, + rule: "Round robin", + }, + { + id: "b", + name: "Load Balancer 1", + protocol: "HTTP", + port: 443, + rule: "Round robin", + }, + ]; + + const headers5 = [ + { key: "name", value: "Name" }, + { key: "protocol", value: "Protocol" }, + { key: "port", value: "Port" }, + { key: "rule", value: "Rule" }, + { key: "actions", value: "Actions" }, + ]; + + const headers3 = [ + { key: "name", value: "Name" }, + { key: "protocol", value: "Protocol" }, + { key: "port", value: "Port" }, + ]; + + const { component, rerender } = render(DataTable, { + props: { + rows, + headers: headers5, + }, + }); + + // First render with 5 headers + expect(component).toBeTruthy(); + + // Change to 3 headers - this should not crash + await rerender({ + rows, + headers: headers3, + }); + + expect(component).toBeTruthy(); + + // Change back to 5 headers - this should not crash + await rerender({ + rows, + headers: headers5, + }); + + expect(component).toBeTruthy(); + }); + + it("should handle empty headers correctly", async () => { + const rows = [ + { + id: "a", + name: "Load Balancer 3", + protocol: "HTTP", + port: 3000, + rule: "Round robin", + }, + ]; + + const headersWithEmpty = [ + { key: "name", value: "Name" }, + { key: "protocol", value: "Protocol" }, + { key: "actions", value: "", empty: true, columnMenu: true }, + ]; + + const { component } = render(DataTable, { + props: { + rows, + headers: headersWithEmpty, + }, + }); + + expect(component).toBeTruthy(); + }); });