From f9ad276b397a10b3417b45f7c6a38a9be30028da Mon Sep 17 00:00:00 2001 From: Cahit Guerguec Date: Thu, 9 Apr 2026 14:19:49 +0200 Subject: [PATCH 1/3] feat(table): implement table grouping with accessibility support --- packages/main/src/Table.ts | 11 +- packages/main/src/TableGroupRow.ts | 56 +++++++++ packages/main/src/TableGroupRowTemplate.tsx | 13 ++ packages/main/src/TableRow.ts | 5 + packages/main/src/TableTemplate.tsx | 3 +- packages/main/src/bundle.esm.ts | 3 + packages/main/src/themes/TableGroupRow.css | 15 +++ packages/main/test/pages/TableGrouping.html | 125 ++++++++++++++++++++ 8 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 packages/main/src/TableGroupRow.ts create mode 100644 packages/main/src/TableGroupRowTemplate.tsx create mode 100644 packages/main/src/themes/TableGroupRow.css create mode 100644 packages/main/test/pages/TableGrouping.html diff --git a/packages/main/src/Table.ts b/packages/main/src/Table.ts index 6e864a648f71..a2da41c496f7 100644 --- a/packages/main/src/Table.ts +++ b/packages/main/src/Table.ts @@ -1,5 +1,6 @@ import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js"; import type { Slot, DefaultSlot } from "@ui5/webcomponents-base/dist/UI5Element.js"; +import type { AriaRole } from "@ui5/webcomponents-base/dist/types.js"; import { customElement, slotStrict as slot, property, eventStrict, i18n, } from "@ui5/webcomponents-base/dist/decorators.js"; @@ -275,7 +276,7 @@ class Table extends UI5Element { "default": true, invalidateOnChildChange: { properties: ["navigated", "position"], - slots: false, + slots: true, }, }) rows!: DefaultSlot; @@ -711,6 +712,14 @@ class Table extends UI5Element { return ariaColCount; } + get _hasGroupRows(): boolean { + return this.rows.some(row => row.hasAttribute("ui5-table-group-row")); + } + + get _ariaRole(): AriaRole { + return this._hasGroupRows ? "treegrid" as AriaRole : "grid" as AriaRole; + } + get _ariaMultiSelectable() { const selection = this._getSelection(); return (selection?.isSelectable() && this.rows.length) ? selection.isMultiSelectable() : undefined; diff --git a/packages/main/src/TableGroupRow.ts b/packages/main/src/TableGroupRow.ts new file mode 100644 index 000000000000..31e36f11ae5f --- /dev/null +++ b/packages/main/src/TableGroupRow.ts @@ -0,0 +1,56 @@ +import { customElement } from "@ui5/webcomponents-base/dist/decorators.js"; +import TableRowBase from "./TableRowBase.js"; +import TableGroupRowTemplate from "./TableGroupRowTemplate.js"; +import TableGroupRowCss from "./generated/themes/TableGroupRow.css.js"; + +/** + * @class + * + * ### Overview + * + * The `ui5-table-group-row` component represents a group header row in the `ui5-table`. + * It spans all columns and displays a group label. + * + * When group rows are present in the table, the table automatically switches + * from `role="grid"` to `role="treegrid"` for proper accessibility. + * + * ### ES6 Module Import + * + * `import "@ui5/webcomponents/dist/TableGroupRow.js";` + * + * @constructor + * @extends TableRowBase + * @since 2.0.0 + * @public + */ +@customElement({ + tag: "ui5-table-group-row", + styles: [TableRowBase.styles, TableGroupRowCss], + template: TableGroupRowTemplate, +}) +class TableGroupRow extends TableRowBase { + onEnterDOM() { + super.onEnterDOM(); + this.toggleAttribute("ui5-table-group-row", true); + } + + onBeforeRendering() { + super.onBeforeRendering(); + this.setAttribute("aria-level", "1"); + this.setAttribute("aria-expanded", "true"); + } + + get _ariaColSpan(): number { + return this._table?._ariaColCount || 1; + } + + async focus(focusOptions?: FocusOptions): Promise { + this.setAttribute("tabindex", "-1"); + HTMLElement.prototype.focus.call(this, focusOptions); + return Promise.resolve(); + } +} + +TableGroupRow.define(); + +export default TableGroupRow; diff --git a/packages/main/src/TableGroupRowTemplate.tsx b/packages/main/src/TableGroupRowTemplate.tsx new file mode 100644 index 000000000000..549a81254879 --- /dev/null +++ b/packages/main/src/TableGroupRowTemplate.tsx @@ -0,0 +1,13 @@ +import type TableGroupRow from "./TableGroupRow.js"; + +export default function TableGroupRowTemplate(this: TableGroupRow) { + return ( +
+ +
+ ); +} diff --git a/packages/main/src/TableRow.ts b/packages/main/src/TableRow.ts index 56f13c9257b3..e380f7973674 100644 --- a/packages/main/src/TableRow.ts +++ b/packages/main/src/TableRow.ts @@ -127,6 +127,11 @@ class TableRow extends TableRowBase { onBeforeRendering() { super.onBeforeRendering(); this.ariaRowIndex = (this.role === "row") ? `${this._rowIndex + 2}` : null; + if (this._table?._hasGroupRows) { + this.setAttribute("aria-level", "2"); + } else { + this.removeAttribute("aria-level"); + } toggleAttribute(this, "draggable", this.movable, "true"); toggleAttribute(this, "_interactive", this._isInteractive); toggleAttribute(this, "_alternate", this._alternate); diff --git a/packages/main/src/TableTemplate.tsx b/packages/main/src/TableTemplate.tsx index 8a615584a42d..2c9ed846d05e 100644 --- a/packages/main/src/TableTemplate.tsx +++ b/packages/main/src/TableTemplate.tsx @@ -9,7 +9,8 @@ export default function TableTemplate(this: Table) { <>
-
+ + + + + Table Grouping POC + + + + + + +
+ Table Grouping POC (Treegrid) + Group by Country +
+ + + + City + Country + Population + + + + + + + From dfe6751f72fc86a104ed4d621bdc675d2cd5b5bb Mon Sep 17 00:00:00 2001 From: Cahit Guerguec Date: Thu, 9 Apr 2026 16:10:03 +0200 Subject: [PATCH 2/3] feat(table): implement table grouping with accessibility support --- .../main/cypress/specs/TableGroupRow.cy.tsx | 197 ++++++++++++++++++ packages/main/src/Table.ts | 6 +- 2 files changed, 200 insertions(+), 3 deletions(-) create mode 100644 packages/main/cypress/specs/TableGroupRow.cy.tsx diff --git a/packages/main/cypress/specs/TableGroupRow.cy.tsx b/packages/main/cypress/specs/TableGroupRow.cy.tsx new file mode 100644 index 000000000000..bf039696cb5d --- /dev/null +++ b/packages/main/cypress/specs/TableGroupRow.cy.tsx @@ -0,0 +1,197 @@ +import Table from "../../src/Table.js"; +import TableHeaderRow from "../../src/TableHeaderRow.js"; +import TableHeaderCell from "../../src/TableHeaderCell.js"; +import TableRow from "../../src/TableRow.js"; +import TableCell from "../../src/TableCell.js"; +import TableGroupRow from "../../src/TableGroupRow.js"; +import Text from "../../src/Text.js"; + +describe("Table - Group Rows", () => { + function mountGroupedTable() { + cy.mount( + + + City + Country + Population + + + Country: Germany + + + Berlin + Germany + 3,748,148 + + + Munich + Germany + 1,471,508 + + + Country: France + + + Paris + France + 2,161,000 + +
+ ); + } + + it("should render group rows and data rows", () => { + mountGroupedTable(); + + cy.get("[ui5-table-group-row]").should("have.length", 2); + cy.get("[ui5-table-row]").should("have.length", 3); + cy.get("#group1").should("contain.text", "Country: Germany"); + cy.get("#group2").should("contain.text", "Country: France"); + }); + + it("should use treegrid role when group rows are present", () => { + mountGroupedTable(); + + cy.get("#table") + .shadow() + .find("#table") + .should("have.attr", "role", "treegrid"); + }); + + it("should use grid role when no group rows are present", () => { + cy.mount( + + + City + + + Berlin + +
+ ); + + cy.get("#table") + .shadow() + .find("#table") + .should("have.attr", "role", "grid"); + }); + + it("should set aria-level=1 on group rows and aria-level=2 on data rows", () => { + mountGroupedTable(); + + cy.get("#group1").should("have.attr", "aria-level", "1"); + cy.get("#group2").should("have.attr", "aria-level", "1"); + cy.get("#row1").should("have.attr", "aria-level", "2"); + cy.get("#row2").should("have.attr", "aria-level", "2"); + cy.get("#row3").should("have.attr", "aria-level", "2"); + }); + + it("should set aria-expanded=true on group rows", () => { + mountGroupedTable(); + + cy.get("#group1").should("have.attr", "aria-expanded", "true"); + cy.get("#group2").should("have.attr", "aria-expanded", "true"); + }); + + it("should have group cell spanning all columns", () => { + mountGroupedTable(); + + cy.get("#group1") + .shadow() + .find("#group-cell") + .should("have.attr", "role", "gridcell") + .and("have.attr", "aria-colindex", "1") + .and("have.attr", "aria-colspan", "3"); + }); + + it("should not have aria-level on data rows when no group rows exist", () => { + cy.mount( + + + City + + + Berlin + +
+ ); + + cy.get("#row1").should("not.have.attr", "aria-level"); + }); + + it("should not throw when group rows are added dynamically", () => { + cy.mount( + + + City + Country + + + Berlin + Germany + +
+ ); + + cy.get("#table") + .shadow() + .find("#table") + .should("have.attr", "role", "grid"); + + // Dynamically add a group row + cy.get("#table").then($table => { + const table = $table[0] as Table; + const groupRow = document.createElement("ui5-table-group-row") as TableGroupRow; + groupRow.id = "dynamicGroup"; + groupRow.textContent = "Group: Germany"; + table.insertBefore(groupRow, table.querySelector("[ui5-table-row]")); + }); + + // Should switch to treegrid and not throw errors + cy.get("#table") + .shadow() + .find("#table") + .should("have.attr", "role", "treegrid"); + + cy.get("#dynamicGroup").should("have.attr", "aria-level", "1"); + }); + + it("should not throw with popin mode and group rows", () => { + cy.mount( + + + City + Country + Population + + + Country: Germany + + + Berlin + Germany + 3,748,148 + + + Munich + Germany + 1,471,508 + +
+ ); + + // Shrink table to trigger popin - should not throw + cy.get("#table").invoke("css", "width", "300px"); + + // Table and rows should still be intact + cy.get("#table").should("exist"); + cy.get("#group1").should("exist"); + cy.get("#row1").should("exist"); + cy.get("#row2").should("exist"); + + // Expand again + cy.get("#table").invoke("css", "width", "800px"); + + cy.get("#group1").should("contain.text", "Country: Germany"); + }); +}); diff --git a/packages/main/src/Table.ts b/packages/main/src/Table.ts index a2da41c496f7..def5ee03f84e 100644 --- a/packages/main/src/Table.ts +++ b/packages/main/src/Table.ts @@ -587,10 +587,10 @@ class Table extends UI5Element { headerCell._popin = inPopin && this.overflowMode === TableOverflowMode.Popin; headerCell._popinWidth = popinWidth; this.rows.forEach(row => { - const cell = row.cells[headerIndex]; + const cell = row.cells?.[headerIndex]; if (cell) { - row.cells[headerIndex]._popinHidden = headerCell.popinHidden; - row.cells[headerIndex]._popin = headerCell._popin; + cell._popinHidden = headerCell.popinHidden; + cell._popin = headerCell._popin; } }); } From 88370a61961aacdb1440f500cc388cf96434ef35 Mon Sep 17 00:00:00 2001 From: Cahit Guerguec Date: Sun, 12 Apr 2026 14:56:10 +0200 Subject: [PATCH 3/3] feat(table): implement table grouping with accessibility support row-index for grouprows selection --- .../main/cypress/specs/TableGroupRow.cy.tsx | 62 +++++++++++++++++++ packages/main/src/Table.ts | 19 ++++-- packages/main/src/TableGroupRowTemplate.tsx | 6 +- packages/main/src/TableRow.ts | 18 ++---- packages/main/src/TableRowBase.ts | 7 +++ packages/main/src/TableSelection.ts | 25 +++++--- packages/main/src/TableSelectionBase.ts | 2 +- packages/main/src/TableSelectionMulti.ts | 15 ++--- packages/main/src/TableSelectionSingle.ts | 2 +- packages/main/src/TableVirtualizer.ts | 4 +- packages/main/test/pages/TableGrouping.html | 17 +++++ 11 files changed, 136 insertions(+), 41 deletions(-) diff --git a/packages/main/cypress/specs/TableGroupRow.cy.tsx b/packages/main/cypress/specs/TableGroupRow.cy.tsx index bf039696cb5d..8fb5272c3403 100644 --- a/packages/main/cypress/specs/TableGroupRow.cy.tsx +++ b/packages/main/cypress/specs/TableGroupRow.cy.tsx @@ -156,6 +156,68 @@ describe("Table - Group Rows", () => { cy.get("#dynamicGroup").should("have.attr", "aria-level", "1"); }); + it("should set aria-rowindex on group rows and data rows", () => { + mountGroupedTable(); + + // Header row is aria-rowindex="1" (set by TableHeaderRow) + // group1 is rows[0] → index 0 + 2 = 2 + cy.get("#group1").should("have.attr", "aria-rowindex", "2"); + // row1 is rows[1] → index 1 + 2 = 3 + cy.get("#row1").should("have.attr", "aria-rowindex", "3"); + // row2 is rows[2] → index 2 + 2 = 4 + cy.get("#row2").should("have.attr", "aria-rowindex", "4"); + // group2 is rows[3] → index 3 + 2 = 5 + cy.get("#group2").should("have.attr", "aria-rowindex", "5"); + // row3 is rows[4] → index 4 + 2 = 6 + cy.get("#row3").should("have.attr", "aria-rowindex", "6"); + }); + + it("should reset row alternation after each group header row", () => { + cy.mount( + + + City + Country + + + Country: Germany + + + Berlin + Germany + + + Munich + Germany + + + Hamburg + Germany + + + Country: France + + + Paris + France + + + Lyon + France + +
+ ); + + // After group1, alternation resets: rowDE1 → alternate (0), rowDE2 → not (1), rowDE3 → alternate (2) + cy.get("#rowDE1").should("have.attr", "_alternate"); + cy.get("#rowDE2").should("not.have.attr", "_alternate"); + cy.get("#rowDE3").should("have.attr", "_alternate"); + + // After group2, alternation resets again: rowFR1 → alternate (0), rowFR2 → not (1) + cy.get("#rowFR1").should("have.attr", "_alternate"); + cy.get("#rowFR2").should("not.have.attr", "_alternate"); + }); + it("should not throw with popin mode and group rows", () => { cy.mount( diff --git a/packages/main/src/Table.ts b/packages/main/src/Table.ts index def5ee03f84e..09c3f81ea548 100644 --- a/packages/main/src/Table.ts +++ b/packages/main/src/Table.ts @@ -21,6 +21,7 @@ import { getEffectiveAriaLabelText } from "@ui5/webcomponents-base/dist/util/Acc import type DropIndicator from "./DropIndicator.js"; import type TableHeaderRow from "./TableHeaderRow.js"; import type TableRow from "./TableRow.js"; +import type TableGroupRow from "./TableGroupRow.js"; import type { ResizeObserverCallback } from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js"; import type { MoveEventDetail } from "@ui5/webcomponents-base/dist/util/dragAndDrop/DragRegistry.js"; import type TableHeaderCell from "./TableHeaderCell.js"; @@ -279,7 +280,7 @@ class Table extends UI5Element { slots: true, }, }) - rows!: DefaultSlot; + rows!: DefaultSlot; /** * Defines the header row of the component. @@ -456,11 +457,17 @@ class Table extends UI5Element { } onBeforeRendering(): void { - this._renderNavigated = this.rows.some(row => row.navigated); - [...this.headerRow, ...this.rows].forEach((row, index) => { + let alternateIndex = 0; + let ariaRowIndex = 2; + this._renderNavigated = this.rows.some(row => "navigated" in row && row.navigated); + [...this.headerRow, ...this.rows].forEach(row => { row._renderNavigated = this._renderNavigated; row._rowActionCount = this.rowActionCount; - row._alternate = this.alternateRowColors && index % 2 === 0; + row._alternate = this.alternateRowColors && alternateIndex % 2 === 0; + alternateIndex = row.hasAttribute("ui5-table-group-row") ? 1 : alternateIndex + 1; + if (!row.isHeaderRow()) { + row.setAttribute("aria-rowindex", `${ariaRowIndex++}`); + } }); this.style.setProperty("--ui5_grid_sticky_top", this.stickyTop); @@ -663,6 +670,10 @@ class Table extends UI5Element { return widths.join(" "); } + get _rows(): TableRow[] { + return this.rows.filter((row): row is TableRow => row.hasAttribute("ui5-table-row")); + } + get _isRowSelectorRequired() { return this.rows.length > 0 && this._getSelection()?.isRowSelectorRequired(); } diff --git a/packages/main/src/TableGroupRowTemplate.tsx b/packages/main/src/TableGroupRowTemplate.tsx index 549a81254879..6569ef37b2b5 100644 --- a/packages/main/src/TableGroupRowTemplate.tsx +++ b/packages/main/src/TableGroupRowTemplate.tsx @@ -1,13 +1,13 @@ +import TableCell from "./TableCell.js"; import type TableGroupRow from "./TableGroupRow.js"; export default function TableGroupRowTemplate(this: TableGroupRow) { return ( -
-
+ ); } diff --git a/packages/main/src/TableRow.ts b/packages/main/src/TableRow.ts index e380f7973674..1c64feb51111 100644 --- a/packages/main/src/TableRow.ts +++ b/packages/main/src/TableRow.ts @@ -126,12 +126,10 @@ class TableRow extends TableRowBase { onBeforeRendering() { super.onBeforeRendering(); - this.ariaRowIndex = (this.role === "row") ? `${this._rowIndex + 2}` : null; - if (this._table?._hasGroupRows) { - this.setAttribute("aria-level", "2"); - } else { - this.removeAttribute("aria-level"); + if (this.position !== undefined) { + this.setAttribute("aria-rowindex", `${this.position + 2}`); } + toggleAttribute(this, "aria-level", !!this._table?._hasGroupRows, "2"); toggleAttribute(this, "draggable", this.movable, "true"); toggleAttribute(this, "_interactive", this._isInteractive); toggleAttribute(this, "_alternate", this._alternate); @@ -207,14 +205,8 @@ class TableRow extends TableRowBase { return this.cells.some(c => c._popin && !c._popinHidden); } - get _rowIndex() { - if (this.position !== undefined) { - return this.position; - } - if (this._table) { - return this._table.rows.indexOf(this); - } - return -1; + override _getRowIndex(): number { + return this.position ?? super._getRowIndex(); } get _hasOverflowActions() { diff --git a/packages/main/src/TableRowBase.ts b/packages/main/src/TableRowBase.ts index c9fdf238d181..38d094787276 100644 --- a/packages/main/src/TableRowBase.ts +++ b/packages/main/src/TableRowBase.ts @@ -66,6 +66,13 @@ abstract class TableRowBase extends return false; } + _getRowIndex(): number { + if (this._table) { + return (this._table.rows as TableRowBase[]).indexOf(this); + } + return -1; + } + _onSelectionChange() { const tableSelection = this._tableSelection!; const selected = tableSelection.isMultiSelectable() ? !this._isSelected : true; diff --git a/packages/main/src/TableSelection.ts b/packages/main/src/TableSelection.ts index 08e60c9beca4..8f90027bf294 100644 --- a/packages/main/src/TableSelection.ts +++ b/packages/main/src/TableSelection.ts @@ -7,7 +7,11 @@ import customElement from "@ui5/webcomponents-base/dist/decorators/customElement import property from "@ui5/webcomponents-base/dist/decorators/property.js"; import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js"; import TableSelectionMode from "./types/TableSelectionMode.js"; -import { isSelectionCell, isHeaderSelectionCell, findRowInPath } from "./TableUtils.js"; +import { + isSelectionCell, + isHeaderSelectionCell, + findRowInPath, +} from "./TableUtils.js"; import type Table from "./Table.js"; import type { ITableFeature } from "./Table.js"; import type TableRow from "./TableRow.js"; @@ -143,8 +147,8 @@ class TableSelection extends UI5Element implements ITableFeature { return undefined; } - getRowKey(row: TableRow): string { - return row.rowKey || ""; + getRowKey(row: TableRowBase): string { + return "rowKey" in row ? (row.rowKey as string) || "" : ""; } isSelected(row: TableRowBase): boolean { @@ -166,7 +170,7 @@ class TableSelection extends UI5Element implements ITableFeature { } const selectedArray = this.selectedAsArray; - return this._table.rows.some(row => { + return this._table._rows.some(row => { const rowKey = this.getRowKey(row); return selectedArray.includes(rowKey); }); @@ -178,7 +182,7 @@ class TableSelection extends UI5Element implements ITableFeature { } const selectedArray = this.selectedAsArray; - return this._table.rows.every(row => { + return this._table._rows.every(row => { const rowKey = this.getRowKey(row); return selectedArray.includes(rowKey); }); @@ -229,7 +233,7 @@ class TableSelection extends UI5Element implements ITableFeature { _selectHeaderRow(selected: boolean) { const selectedSet = this.selectedAsSet; - this._table!.rows.forEach(row => { + this._table!._rows.forEach(row => { const rowKey = this.getRowKey(row); selectedSet[selected ? "add" : "delete"](rowKey); }); @@ -312,8 +316,8 @@ class TableSelection extends UI5Element implements ITableFeature { if (e.shiftKey && this._rangeSelection?.isMouse) { const startRow = this._rangeSelection.rows[0]; - const startIndex = this._table.rows.indexOf(startRow); - const endIndex = this._table.rows.indexOf(row); + const startIndex = this._table._rows.indexOf(startRow); + const endIndex = this._table._rows.indexOf(row); const selectionState = this.isSelected(startRow); @@ -369,10 +373,11 @@ class TableSelection extends UI5Element implements ITableFeature { if (shouldReverseSelection) { this._reverseRangeSelection(); } else { - const rowIndex = this._table!.rows.indexOf(targetRow); + const rows = this._table!._rows; + const rowIndex = rows.indexOf(targetRow); const [startIndex, endIndex] = [rowIndex, rowIndex - change].sort((a, b) => a - b); - selectionChanged = this._table?.rows.slice(startIndex, endIndex + 1).reduce((changed, row) => { + selectionChanged = rows.slice(startIndex, endIndex + 1).reduce((changed, row) => { const isRowNotInSelection = !this._rangeSelection?.rows.includes(row); const isRowSelectionDifferent = this.isSelected(row) !== this._rangeSelection!.selected; diff --git a/packages/main/src/TableSelectionBase.ts b/packages/main/src/TableSelectionBase.ts index 7578210a1c2d..513320c46497 100644 --- a/packages/main/src/TableSelectionBase.ts +++ b/packages/main/src/TableSelectionBase.ts @@ -142,7 +142,7 @@ abstract class TableSelectionBase extends UI5Element implements ITableFeature { */ getRowByKey(rowKey: string): TableRow | undefined { if (this._table && rowKey) { - return this._table.rows.find(row => this.getRowKey(row) === rowKey); + return this._table._rows.find(row => this.getRowKey(row) === rowKey); } } diff --git a/packages/main/src/TableSelectionMulti.ts b/packages/main/src/TableSelectionMulti.ts index 36ad46d319e8..aa1283251f75 100644 --- a/packages/main/src/TableSelectionMulti.ts +++ b/packages/main/src/TableSelectionMulti.ts @@ -108,7 +108,7 @@ class TableSelectionMulti extends TableSelectionBase { return; } - const tableRows = row.isHeaderRow() ? this._table!.rows : [row as TableRow]; + const tableRows = row.isHeaderRow() ? this._table!._rows : [row as TableRow]; const selectedSet = this.getSelectedAsSet(); const selectionChanged = tableRows.reduce((selectedSetChanged, tableRow) => { const rowKey = this.getRowKey(tableRow); @@ -133,7 +133,7 @@ class TableSelectionMulti extends TableSelectionBase { * @public */ getSelectedRows(): TableRow[] { - return this._table ? this._table.rows.filter(row => this.isSelected(row)) : []; + return this._table ? this._table._rows.filter(row => this.isSelected(row)) : []; } /** @@ -145,7 +145,7 @@ class TableSelectionMulti extends TableSelectionBase { } const selectedSet = this.getSelectedAsSet(); - return this._table.rows.every(row => { + return this._table._rows.every(row => { const rowKey = this.getRowKey(row); return selectedSet.has(rowKey); }); @@ -253,8 +253,8 @@ class TableSelectionMulti extends TableSelectionBase { if (e.shiftKey && this._rangeSelection?.isMouse) { const startRow = this._rangeSelection.rows[0]; - const startIndex = this._table.rows.indexOf(startRow); - const endIndex = this._table.rows.indexOf(row); + const startIndex = this._table._rows.indexOf(startRow); + const endIndex = this._table._rows.indexOf(row); // Set checkbox to the selection state of the start row (if it is selected) const selectionState = this.isSelected(startRow); @@ -311,11 +311,12 @@ class TableSelectionMulti extends TableSelectionBase { if (shouldReverseSelection) { this._reverseRangeSelection(); } else { - const rowIndex = this._table!.rows.indexOf(targetRow); + const rows = this._table!._rows; + const rowIndex = rows.indexOf(targetRow); const [startIndex, endIndex] = [rowIndex, rowIndex - change].sort((a, b) => a - b); const selectedSet = this.getSelectedAsSet(); - selectionChanged = this._table?.rows.slice(startIndex, endIndex + 1).reduce((changed, row) => { + selectionChanged = rows.slice(startIndex, endIndex + 1).reduce((changed, row) => { const isRowNotInSelection = !this._rangeSelection?.rows.includes(row); const isRowSelectionDifferent = this.isSelected(row) !== this._rangeSelection!.selected; diff --git a/packages/main/src/TableSelectionSingle.ts b/packages/main/src/TableSelectionSingle.ts index 85332ec173ec..ec03bd1bfde2 100644 --- a/packages/main/src/TableSelectionSingle.ts +++ b/packages/main/src/TableSelectionSingle.ts @@ -62,7 +62,7 @@ class TableSelectionSingle extends TableSelectionBase { * @public */ getSelectedRow(): TableRow | undefined { - return this._table?.rows.find(row => this.isSelected(row)); + return this._table?._rows.find(row => this.isSelected(row)); } } diff --git a/packages/main/src/TableVirtualizer.ts b/packages/main/src/TableVirtualizer.ts index 87f9f49f4acc..f5bf43681c05 100644 --- a/packages/main/src/TableVirtualizer.ts +++ b/packages/main/src/TableVirtualizer.ts @@ -218,7 +218,7 @@ class TableVirtualizer extends UI5Element implements ITableFeature { return; } - const firstRow = this._table.rows[0]; + const firstRow = this._table._rows[0]; if (firstRow && firstRow.position !== undefined && firstRow.position > 0) { const transform = firstRow.position * this.rowHeight; return `translateY(${transform}px)`; @@ -238,7 +238,7 @@ class TableVirtualizer extends UI5Element implements ITableFeature { } let scrollTopChange = 0; - const rows = this._table.rows; + const rows = this._table._rows; const firstRow = rows[0]; const lastRow = rows[rows.length - 1]; const hasDataBeforeFirstRow = firstRow.position !== 0; diff --git a/packages/main/test/pages/TableGrouping.html b/packages/main/test/pages/TableGrouping.html index e1778fab4d9e..3ab1d2f6ebc0 100644 --- a/packages/main/test/pages/TableGrouping.html +++ b/packages/main/test/pages/TableGrouping.html @@ -25,9 +25,11 @@
Table Grouping POC (Treegrid) Group by Country + Enable Alternating Colors
+ City Country @@ -35,9 +37,18 @@ +
+ aria-rowindex and Row Alternation Reset Demo +

+ Inspect rows with DevTools to verify: group rows receive sequential aria-rowindex, + and alternating colors restart after each group header row when enabled. +

+
+