From e908df8297a63c43f0198896c47e22b53e4a9dff Mon Sep 17 00:00:00 2001 From: Cahit Guerguec Date: Wed, 25 Feb 2026 02:55:50 +0100 Subject: [PATCH] docs(ui5-table): navigation icon is deprecated - Active state is reimplemeted with JS - Selection component should not be focusable via keyboard Fixes: #13066 - Horizontal alignment must wrk for multi line texts Fixes: #13077 --- packages/main/cypress/specs/Table.cy.tsx | 15 +++++++-- .../main/cypress/specs/TableSelection.cy.tsx | 2 +- .../main/cypress/specs/TableSelections.cy.tsx | 8 ++--- packages/main/src/Table.ts | 4 +-- packages/main/src/TableCell.ts | 2 ++ packages/main/src/TableHeaderCell.ts | 1 + packages/main/src/TableRow.ts | 31 +++++++++++++------ packages/main/src/TableRowActionBase.ts | 2 +- packages/main/src/TableRowActionNavigation.ts | 4 +++ packages/main/src/TableRowTemplate.tsx | 1 - packages/main/src/themes/TableCellBase.css | 3 +- packages/main/src/themes/TableRow.css | 10 +++--- packages/main/src/types/TableOverflowMode.ts | 3 +- 13 files changed, 56 insertions(+), 30 deletions(-) diff --git a/packages/main/cypress/specs/Table.cy.tsx b/packages/main/cypress/specs/Table.cy.tsx index 62d27762b963..f41b55637967 100644 --- a/packages/main/cypress/specs/Table.cy.tsx +++ b/packages/main/cypress/specs/Table.cy.tsx @@ -493,11 +493,13 @@ describe("Table - Popin Mode", () => { describe("Table - Horizontal alignment of cells", () => { function check(id: string, index: number, alignment: string) { cy.get(id) - .should("have.css", "justify-content", alignment); + .should("have.css", "justify-content", alignment) + .should("have.css", "text-align", alignment === "normal" ? "start" : alignment); cy.get("ui5-table-row") .get(`ui5-table-cell:nth-of-type(${index})`) - .should("have.css", "justify-content", alignment); + .should("have.css", "justify-content", alignment) + .should("have.css", "text-align", alignment === "normal" ? "start" : alignment); } beforeEach(() => { @@ -948,6 +950,11 @@ describe("Table - Interactive Rows", () => { cy.get("#row1").realPress("Enter"); cy.get("@rowClickHandler").should("not.have.been.called"); + cy.get("#row2").realMouseDown(); + cy.get("#row2").should("have.attr", "_active"); + cy.get("#row1").realMouseUp(); + cy.get("#row2").should("not.have.attr", "_active"); + cy.get("#row2").realClick(); cy.get("@rowClickHandler").invoke("getCall", 0).its("args.0.detail.row").as("clickedRow"); cy.get("@clickedRow").should("have.attr", "id", "row2"); @@ -958,6 +965,10 @@ describe("Table - Interactive Rows", () => { cy.get("@rowClickHandler").should("have.been.calledThrice"); cy.get("#row2").find("ui5-button").as("row2button"); + cy.get("@row2button").realMouseDown(); + cy.get("#row2").should("not.have.attr", "_active"); + cy.get("@row2button").realMouseUp(); + cy.get("#row2").should("not.have.attr", "_active"); cy.get("@row2button").invoke("on", "click", cy.stub().as("buttonClickHandler")); cy.get("@row2button").realClick(); cy.get("@buttonClickHandler").should("have.been.calledOnce"); diff --git a/packages/main/cypress/specs/TableSelection.cy.tsx b/packages/main/cypress/specs/TableSelection.cy.tsx index c0e9c99a7ef2..81c4c470c912 100644 --- a/packages/main/cypress/specs/TableSelection.cy.tsx +++ b/packages/main/cypress/specs/TableSelection.cy.tsx @@ -93,7 +93,7 @@ const testConfig = { }, "ARROWS_BOX": { "arrow_initial": "0", - "arrow_down": "1", + "arrow_down": "0", "arrow_up": "0", }, "MOUSE": { diff --git a/packages/main/cypress/specs/TableSelections.cy.tsx b/packages/main/cypress/specs/TableSelections.cy.tsx index 8a0320fcf599..1078ff9d3c4b 100644 --- a/packages/main/cypress/specs/TableSelections.cy.tsx +++ b/packages/main/cypress/specs/TableSelections.cy.tsx @@ -111,7 +111,7 @@ const testConfig = { }, "ARROWS_BOX": { "arrow_initial": "0", - "arrow_down": "1", + "arrow_down": "0", "arrow_up": "0", }, "MOUSE": { @@ -257,13 +257,11 @@ Object.entries(testConfig).forEach(([mode, testConfigEntry]) => { cy.realPress("ArrowDown"); checkSelection(testConfigEntry.cases.ARROWS_BOX.arrow_down); - let callCount = mode === "Single" ? 2 : 1; - checkSelectionChangeSpy(callCount); + checkSelectionChangeSpy(1); cy.realPress("ArrowUp"); checkSelection(testConfigEntry.cases.ARROWS_BOX.arrow_up); - callCount = mode === "Single" ? 3 : 1; - checkSelectionChangeSpy(callCount); + checkSelectionChangeSpy(1); }); it("select row via mouse", () => { diff --git a/packages/main/src/Table.ts b/packages/main/src/Table.ts index 501bddd81677..6e864a648f71 100644 --- a/packages/main/src/Table.ts +++ b/packages/main/src/Table.ts @@ -425,7 +425,7 @@ class Table extends UI5Element { @i18n("@ui5/webcomponents") static i18nBundle: I18nBundle; - _events = ["keydown", "keyup", "click", "focusin", "focusout", "dragstart", "dragenter", "dragleave", "dragover", "drop", "dragend"]; + _events = ["keydown", "keyup", "click", "focusin", "focusout", "pointerdown", "dragstart", "dragenter", "dragleave", "dragover", "drop", "dragend"]; _onEventBound: (e: Event) => void; _onResizeBound: ResizeObserverCallback; _tableNavigation?: TableNavigation; @@ -611,7 +611,7 @@ class Table extends UI5Element { const virtualizer = this._getVirtualizer(); const headerStyleMap: Record = {}; this.headerRow[0]?.cells.forEach(headerCell => { - headerStyleMap[`--halign-${headerCell._id}`] = headerCell.horizontalAlign || "normal"; + headerStyleMap[`--halign-${headerCell._id}`] = headerCell.horizontalAlign || "initial"; }); return { table: { diff --git a/packages/main/src/TableCell.ts b/packages/main/src/TableCell.ts index 1d833f4cf0a7..48ff7dddb9be 100644 --- a/packages/main/src/TableCell.ts +++ b/packages/main/src/TableCell.ts @@ -39,8 +39,10 @@ class TableCell extends TableCellBase { onBeforeRendering() { super.onBeforeRendering(); if (this.horizontalAlign) { + this.style.textAlign = this.horizontalAlign; this.style.justifyContent = this.horizontalAlign; } else if (this._headerCell) { + this.style.textAlign = `var(--halign-${this._headerCell._id})`; this.style.justifyContent = `var(--halign-${this._headerCell._id})`; } } diff --git a/packages/main/src/TableHeaderCell.ts b/packages/main/src/TableHeaderCell.ts index b6065cd016d5..5107c46ffadf 100644 --- a/packages/main/src/TableHeaderCell.ts +++ b/packages/main/src/TableHeaderCell.ts @@ -134,6 +134,7 @@ class TableHeaderCell extends TableCellBase { onBeforeRendering() { super.onBeforeRendering(); + this.style.textAlign = this.horizontalAlign || ""; this.style.justifyContent = this.horizontalAlign || ""; toggleAttribute(this, "aria-sort", this.sortIndicator !== SortOrder.None, this.sortIndicator.toLowerCase()); } diff --git a/packages/main/src/TableRow.ts b/packages/main/src/TableRow.ts index 2ccab661520b..e60c521d5194 100644 --- a/packages/main/src/TableRow.ts +++ b/packages/main/src/TableRow.ts @@ -10,10 +10,10 @@ import type TableCell from "./TableCell.js"; import type TableRowActionBase from "./TableRowActionBase.js"; import type Button from "./Button.js"; import type { UI5CustomEvent } from "@ui5/webcomponents-base"; +import type { Slot, DefaultSlot } from "@ui5/webcomponents-base/dist/UI5Element.js"; import { TABLE_ROW_MULTIPLE_ACTIONS, TABLE_ROW_SINGLE_ACTION, } from "./generated/i18n/i18n-defaults.js"; -import type { Slot, DefaultSlot } from "@ui5/webcomponents-base/dist/UI5Element.js"; /** * @class @@ -138,6 +138,20 @@ class TableRow extends TableRowBase { return Promise.resolve(); } + async _onpointerdown(e: PointerEvent) { + if (e.button !== 0 || !this._isInteractive) { + return; + } + + const composedPath = e.composedPath(); + composedPath.splice(composedPath.indexOf(this)); + await new Promise(resolve => setTimeout(resolve)); // wait for the focus to be set + const activeElement = getActiveElement() as Element; + if (!composedPath.includes(activeElement)) { + this._setActive("pointerup"); + } + } + _onkeydown(e: KeyboardEvent, eventOrigin: HTMLElement) { super._onkeydown(e, eventOrigin); if (e.defaultPrevented) { @@ -145,7 +159,7 @@ class TableRow extends TableRowBase { } if (eventOrigin === this && this._isInteractive && isEnter(e)) { - this.toggleAttribute("_active", true); + this._setActive("keyup"); this._onclick(); } } @@ -160,12 +174,11 @@ class TableRow extends TableRowBase { } } - _onkeyup() { - this.removeAttribute("_active"); - } - - _onfocusout() { - this.removeAttribute("_active"); + _setActive(deactivationEvent: string) { + this.toggleAttribute("_active", true); + document.addEventListener(deactivationEvent, () => { + this.removeAttribute("_active"); + }, { once: true }); } _onOverflowButtonClick(e: UI5CustomEvent) { @@ -180,7 +193,7 @@ class TableRow extends TableRowBase { get _isNavigable() { return this._fixedActions.find(action => { - return action.hasAttribute("ui5-table-row-action-navigation") && !action._isInteractive; + return action.hasAttribute("ui5-table-row-action-navigation") && !action.invisible && !action._isInteractive; }) !== undefined; } diff --git a/packages/main/src/TableRowActionBase.ts b/packages/main/src/TableRowActionBase.ts index 1ea02986094a..4f7bd435d67f 100644 --- a/packages/main/src/TableRowActionBase.ts +++ b/packages/main/src/TableRowActionBase.ts @@ -93,7 +93,7 @@ abstract class TableRowActionBase extends UI5Element { abstract getRenderInfo(): { text: string; icon: string; - interactive: boolean; + interactive?: boolean; }; isFixedAction() { diff --git a/packages/main/src/TableRowActionNavigation.ts b/packages/main/src/TableRowActionNavigation.ts index 88a7950c5fb5..81817951677a 100644 --- a/packages/main/src/TableRowActionNavigation.ts +++ b/packages/main/src/TableRowActionNavigation.ts @@ -28,6 +28,10 @@ class TableRowActionNavigation extends TableRowActionBase { * * @default false * @public + * @deprecated As of version 2.20.0, the navigation icon is deprecated. + * For better accessibility, the interactive mode which renders a button, must be used instead. To handle the action, attach a listener to the `click` event. + * If the navigation should be triggered when a row is pressed, set the row's `interactive` property and use the `row-click` event of the `ui5-table`. + * This property will be removed in a future release. */ @property({ type: Boolean }) interactive = false; diff --git a/packages/main/src/TableRowTemplate.tsx b/packages/main/src/TableRowTemplate.tsx index 5d722f0970a0..75064cc7d3e0 100644 --- a/packages/main/src/TableRowTemplate.tsx +++ b/packages/main/src/TableRowTemplate.tsx @@ -27,7 +27,6 @@ export default function TableRowTemplate(this: TableRow, ariaColIndex: number = :