From 4af49cd4f1b18e0e2688c3db47ec54c8887657a2 Mon Sep 17 00:00:00 2001 From: "Frank Pigeon Jr." <4629398+fpigeonjr@users.noreply.github.com> Date: Tue, 1 Sep 2026 12:06:41 -0500 Subject: [PATCH 1/2] Fix AbstractRow rowheaders/columnheaders no-op concat _setRowHeaders discarded the result of Array.prototype.concat instead of assigning it, so the public rowheaders/columnheaders collections on AbstractRow were always empty regardless of the row's actual DOM content. Assign the concat result back to the properties, and add a spec asserting the header collections are populated correctly. Fixes #658 --- .../aria/abstract-grid/abstract-row.spec.ts | 14 ++++++++++++++ .../aria/abstract-grid/abstract-row.ts | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/ui-kit/experimental/aria/abstract-grid/abstract-row.spec.ts b/src/ui-kit/experimental/aria/abstract-grid/abstract-row.spec.ts index a32dbdad4..0055d55d8 100644 --- a/src/ui-kit/experimental/aria/abstract-grid/abstract-row.spec.ts +++ b/src/ui-kit/experimental/aria/abstract-grid/abstract-row.spec.ts @@ -35,6 +35,20 @@ describe("AbstractRow", () => { ]); }); + it("populates rowheaders and columnheaders with their respective cells", () => { + const row = buildRow([ + { role: "columnheader", key: "col" }, + { role: "rowheader", key: "row" }, + { role: "gridcell", key: "a" }, + { role: "gridcell", key: "b" }, + ]); + + const abstractRow = new AbstractRow(row); + + expect(abstractRow.rowheaders.map((c) => c.key)).toEqual(["row"]); + expect(abstractRow.columnheaders.map((c) => c.key)).toEqual(["col"]); + }); + it("addCell appends a cell to the cells collection", () => { const row = buildRow([{ role: "gridcell", key: "a" }]); const abstractRow = new AbstractRow(row); diff --git a/src/ui-kit/experimental/aria/abstract-grid/abstract-row.ts b/src/ui-kit/experimental/aria/abstract-grid/abstract-row.ts index 538f9cef6..eec5119d6 100755 --- a/src/ui-kit/experimental/aria/abstract-grid/abstract-row.ts +++ b/src/ui-kit/experimental/aria/abstract-grid/abstract-row.ts @@ -43,9 +43,9 @@ export class AbstractRow { private _setRowHeaders(cells: AbstractCell[], role: CellRole): void { if (role === "rowheader") { - this.rowheaders.concat(cells); + this.rowheaders = this.rowheaders.concat(cells); } else if (role === "columnheader") { - this.columnheaders.concat(cells); + this.columnheaders = this.columnheaders.concat(cells); } else { return; } From a4b75b2e1c49dd85a99d7d6adcaf0037c2700780 Mon Sep 17 00:00:00 2001 From: "Frank Pigeon Jr." <4629398+fpigeonjr@users.noreply.github.com> Date: Tue, 1 Sep 2026 12:22:29 -0500 Subject: [PATCH 2/2] Reuse the same AbstractCell instances in cells and header collections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _findCells called _getCellsByRole twice per role — once to build the header collections via _setRowHeaders, and again to build the accumulator returned as cells. Each call constructs new AbstractCell instances, which also register duplicate keydown/click listeners on every header DOM node. Reuse the already-created cells value in the reducer so rowheaders/columnheaders are true subsets of cells and each node is initialized exactly once. Addresses Copilot review comment on PR #671. --- .../aria/abstract-grid/abstract-row.spec.ts | 16 ++++++++++++++++ .../aria/abstract-grid/abstract-row.ts | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/ui-kit/experimental/aria/abstract-grid/abstract-row.spec.ts b/src/ui-kit/experimental/aria/abstract-grid/abstract-row.spec.ts index 0055d55d8..232a96b8f 100644 --- a/src/ui-kit/experimental/aria/abstract-grid/abstract-row.spec.ts +++ b/src/ui-kit/experimental/aria/abstract-grid/abstract-row.spec.ts @@ -49,6 +49,22 @@ describe("AbstractRow", () => { expect(abstractRow.columnheaders.map((c) => c.key)).toEqual(["col"]); }); + it("reuses the same AbstractCell instances in rowheaders/columnheaders as in cells", () => { + const row = buildRow([ + { role: "columnheader", key: "col" }, + { role: "rowheader", key: "row" }, + { role: "gridcell", key: "a" }, + ]); + + const abstractRow = new AbstractRow(row); + + const rowheaderInCells = abstractRow.cells.find((c) => c.key === "row"); + const columnheaderInCells = abstractRow.cells.find((c) => c.key === "col"); + + expect(abstractRow.rowheaders[0]).toBe(rowheaderInCells); + expect(abstractRow.columnheaders[0]).toBe(columnheaderInCells); + }); + it("addCell appends a cell to the cells collection", () => { const row = buildRow([{ role: "gridcell", key: "a" }]); const abstractRow = new AbstractRow(row); diff --git a/src/ui-kit/experimental/aria/abstract-grid/abstract-row.ts b/src/ui-kit/experimental/aria/abstract-grid/abstract-row.ts index eec5119d6..c8636d739 100755 --- a/src/ui-kit/experimental/aria/abstract-grid/abstract-row.ts +++ b/src/ui-kit/experimental/aria/abstract-grid/abstract-row.ts @@ -24,7 +24,7 @@ export class AbstractRow { this._setRowHeaders(cells, role); - return accumulator.concat(this._getCellsByRole(node, role)); + return accumulator.concat(cells); }, []); }