Skip to content

Commit 819edcd

Browse files
authored
fix(ui5-table): update select all checkbox (#10833)
Select all checkbox should be updated when number of rows is changed. Fixes: #10658 Role description is added for the header row. Fixes: #10574 Pressing on iteractive element should not trigger row-click event for interactive rows. Fixes: #10804 Broken Table.html test page is updated TableSortOrder type is moved to base as SortOrder
1 parent 500582c commit 819edcd

File tree

8 files changed

+125
-53
lines changed

8 files changed

+125
-53
lines changed

packages/main/src/types/TableSortOrder.ts packages/base/src/types/SortOrder.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
/**
2-
* Defines the sort order for `ui5-table`-related components.
2+
* Defines the sort order.
33
*
44
* @public
5-
* @since 2.7.0
5+
* @since 2.8.0
66
*/
7-
enum TableSortOrder {
7+
enum SortOrder {
88
/**
99
* Sorting is not applied.
1010
* @public
@@ -24,4 +24,4 @@ enum TableSortOrder {
2424
Descending = "Descending",
2525
}
2626

27-
export default TableSortOrder;
27+
export default SortOrder;

packages/main/cypress/specs/Table.cy.tsx

+72
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Input from "../../src/Input.js";
1010
import Bar from "../../src/Bar.js";
1111
import Title from "../../src/Title.js";
1212
import Slider from "../../src/Slider.js";
13+
import Button from "../../src/Button.js";
1314

1415
// Porting Table.spec.js (wdio tests) to cypress tests
1516
const ROLE_COLUMN_HEADER = "columnheader";
@@ -33,6 +34,7 @@ describe("Table - Rendering", () => {
3334
cy.get("ui5-table-header-row").should("exist");
3435
cy.get("ui5-table-row").should("exist");
3536
cy.get("ui5-table-header-cell").should("have.length", 2);
37+
cy.get("ui5-table-header-row").should("have.attr", "aria-roledescription", "Column Header Row");
3638
});
3739

3840
it("tests if initial empty table renders without errors", () => {
@@ -665,6 +667,76 @@ describe("Table - Navigated Rows", () => {
665667
});
666668
});
667669

670+
describe("Table - Interactive Rows", () => {
671+
it("fires the row-click event", () => {
672+
cy.mount(
673+
<Table id="table1">
674+
<TableSelection id="selection" selected="1 2" slot="features"></TableSelection>
675+
<TableHeaderRow id="headerRow" slot="headerRow">
676+
<TableHeaderCell>ColumnA</TableHeaderCell>
677+
<TableHeaderCell>ColumnB</TableHeaderCell>
678+
</TableHeaderRow>
679+
<TableRow id="row1" rowKey="1">
680+
<TableCell><Label>Cell A</Label></TableCell>
681+
<TableCell><Button>Cell B</Button></TableCell>
682+
</TableRow>
683+
<TableRow id="row2" rowKey="2" interactive={true}>
684+
<TableCell><Label>Cell A</Label></TableCell>
685+
<TableCell><Button>Cell B</Button></TableCell>
686+
</TableRow>
687+
</Table>
688+
);
689+
690+
cy.get("#table1").invoke("on", "row-click", cy.stub().as("rowClickHandler"));
691+
cy.get("#row1").realClick();
692+
cy.get("@rowClickHandler").should("not.have.been.called");
693+
cy.get("#row1").realPress("Enter");
694+
cy.get("@rowClickHandler").should("not.have.been.called");
695+
696+
cy.get("#row2").realClick();
697+
cy.get("@rowClickHandler").invoke("getCall", 0).its("args.0.detail.row").as("clickedRow");
698+
cy.get("@clickedRow").should("have.attr", "id", "row2");
699+
cy.get("#row2").realPress("Enter");
700+
cy.get("@rowClickHandler").should("have.been.calledTwice");
701+
702+
cy.get("#row2").find("ui5-label").realClick();
703+
cy.get("@rowClickHandler").should("have.been.calledThrice");
704+
705+
cy.get("#row2").find("ui5-button").as("row2button");
706+
cy.get("@row2button").invoke("on", "click", cy.stub().as("buttonClickHandler"));
707+
cy.get("@row2button").realClick();
708+
cy.get("@buttonClickHandler").should("have.been.calledOnce");
709+
cy.get("@rowClickHandler").should("have.been.calledThrice");
710+
711+
cy.get("@row2button").realPress("Enter");
712+
cy.get("@buttonClickHandler").should("have.been.calledTwice");
713+
cy.get("@rowClickHandler").should("have.been.calledThrice");
714+
715+
cy.get("@row2button").realPress("Space");
716+
cy.get("@buttonClickHandler").should("have.been.calledThrice");
717+
cy.get("@rowClickHandler").should("have.been.calledThrice");
718+
719+
// move the following tests to the TableSelection.cy.tsx
720+
cy.get("#headerRow").shadow().find("#selection-cell").as("headerRowSelectionCell");
721+
cy.get("@headerRowSelectionCell").find("#selection-component").as("headerRowCheckBox");
722+
cy.get("@headerRowCheckBox").should("have.attr", "checked");
723+
cy.get("#table1").then($table => {
724+
$table.append(
725+
`<ui5-table-row id="row3" row-key="3">
726+
<ui5-table-cell>Cell A</ui5-table-cell>
727+
<ui5-table-cell>Cell B</ui5-table-cell>
728+
</ui5-table-row>`
729+
);
730+
});
731+
cy.get("@headerRowCheckBox").should("not.have.attr", "checked");
732+
cy.get("#row3").invoke("remove");
733+
cy.get("@headerRowCheckBox").should("have.attr", "checked");
734+
cy.get("#row2").invoke("remove");
735+
cy.get("#row1").invoke("remove");
736+
cy.get("@headerRowCheckBox").should("not.have.attr", "checked");
737+
});
738+
});
739+
668740
describe("Table - HeaderCell", () => {
669741
beforeEach(() => {
670742
cy.mount(

packages/main/src/Table.ts

+33-28
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,33 @@
11
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
2-
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
2+
import {
3+
customElement, slot, property, eventStrict, i18n,
4+
} from "@ui5/webcomponents-base/dist/decorators.js";
35
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
4-
import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";
5-
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
6-
import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js";
7-
import { getScopedVarName } from "@ui5/webcomponents-base/dist/CustomElementsScope.js";
8-
import { getEffectiveAriaLabelText } from "@ui5/webcomponents-base/dist/util/AccessibilityTextsHelper.js";
9-
import type { ResizeObserverCallback } from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js";
10-
import ResizeHandler from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js";
11-
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
12-
import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js";
13-
import type { MoveEventDetail } from "@ui5/webcomponents-base/dist/util/dragAndDrop/DragRegistry.js";
146
import TableTemplate from "./generated/templates/TableTemplate.lit.js";
157
import TableStyles from "./generated/themes/Table.css.js";
16-
import TableRow from "./TableRow.js";
178
import TableHeaderRow from "./TableHeaderRow.js";
18-
import type TableHeaderCell from "./TableHeaderCell.js";
9+
import TableRow from "./TableRow.js";
10+
import TableCell from "./TableCell.js";
1911
import TableExtension from "./TableExtension.js";
20-
import type TableSelection from "./TableSelection.js";
21-
import TableOverflowMode from "./types/TableOverflowMode.js";
2212
import TableNavigation from "./TableNavigation.js";
13+
import TableOverflowMode from "./types/TableOverflowMode.js";
14+
import TableDragAndDrop from "./TableDragAndDrop.js";
2315
import DropIndicator from "./DropIndicator.js";
24-
import {
25-
TABLE_NO_DATA,
26-
} from "./generated/i18n/i18n-defaults.js";
2716
import BusyIndicator from "./BusyIndicator.js";
28-
import TableCell from "./TableCell.js";
17+
import ResizeHandler from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js";
2918
import { findVerticalScrollContainer, scrollElementIntoView, isFeature } from "./TableUtils.js";
30-
import TableDragAndDrop from "./TableDragAndDrop.js";
19+
import { getScopedVarName } from "@ui5/webcomponents-base/dist/CustomElementsScope.js";
20+
import { getEffectiveAriaLabelText } from "@ui5/webcomponents-base/dist/util/AccessibilityTextsHelper.js";
21+
import type { ResizeObserverCallback } from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js";
22+
import type { MoveEventDetail } from "@ui5/webcomponents-base/dist/util/dragAndDrop/DragRegistry.js";
23+
import type TableHeaderCell from "./TableHeaderCell.js";
24+
import type TableSelection from "./TableSelection.js";
3125
import type TableRowActionBase from "./TableRowActionBase.js";
3226
import type TableVirtualizer from "./TableVirtualizer.js";
27+
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
28+
import {
29+
TABLE_NO_DATA,
30+
} from "./generated/i18n/i18n-defaults.js";
3331

3432
/**
3533
* Interface for components that can be slotted inside the `features` slot of the `ui5-table`.
@@ -70,6 +68,7 @@ interface ITableGrowing extends ITableFeature {
7068

7169
/**
7270
* Fired when an interactive row is clicked.
71+
*
7372
* @param {TableRow} row The clicked row instance
7473
* @public
7574
*/
@@ -81,6 +80,7 @@ type TableMoveEventDetail = MoveEventDetail;
8180

8281
/**
8382
* Fired when a row action is clicked.
83+
*
8484
* @param {TableRowActionBase} action The row action instance
8585
* @param {TableRow} row The row instance
8686
* @public
@@ -192,7 +192,7 @@ type TableRowActionClickEventDetail = {
192192
* @param {TableRow} row The row instance
193193
* @public
194194
*/
195-
@event("row-click", {
195+
@eventStrict("row-click", {
196196
bubbles: false,
197197
})
198198

@@ -209,7 +209,7 @@ type TableRowActionClickEventDetail = {
209209
* @param {object} destination The destination object
210210
* @public
211211
*/
212-
@event("move-over", {
212+
@eventStrict("move-over", {
213213
cancelable: true,
214214
bubbles: true,
215215
})
@@ -229,7 +229,7 @@ type TableRowActionClickEventDetail = {
229229
* @param {object} destination The destination object
230230
* @public
231231
*/
232-
@event("move", {
232+
@eventStrict("move", {
233233
bubbles: true,
234234
})
235235

@@ -241,7 +241,7 @@ type TableRowActionClickEventDetail = {
241241
* @since 2.6.0
242242
* @public
243243
*/
244-
@event("row-action-click", {
244+
@eventStrict("row-action-click", {
245245
bubbles: false,
246246
})
247247

@@ -289,6 +289,7 @@ class Table extends UI5Element {
289289

290290
/**
291291
* Defines the features of the component.
292+
*
292293
* @public
293294
*/
294295
@slot({ type: HTMLElement, individualSlots: true })
@@ -348,6 +349,7 @@ class Table extends UI5Element {
348349

349350
/**
350351
* Defines the delay in milliseconds, after which the loading indicator will show up for this component.
352+
*
351353
* @default 1000
352354
* @public
353355
*/
@@ -386,13 +388,12 @@ class Table extends UI5Element {
386388
_onResizeBound: ResizeObserverCallback;
387389
_tableNavigation?: TableNavigation;
388390
_tableDragAndDrop?: TableDragAndDrop;
389-
_poppedIn: Array<{col: TableHeaderCell, width: float}>;
390-
_containerWidth: number;
391+
_poppedIn: Array<{col: TableHeaderCell, width: float}> = [];
392+
_containerWidth = 0;
393+
_rowsLength = 0;
391394

392395
constructor() {
393396
super();
394-
this._poppedIn = [];
395-
this._containerWidth = 0;
396397
this._onResizeBound = this._onResize.bind(this);
397398
this._onEventBound = this._onEvent.bind(this);
398399
}
@@ -420,6 +421,10 @@ class Table extends UI5Element {
420421
this._renderNavigated = this.rows.some(row => row.navigated);
421422
if (this.headerRow[0]) {
422423
this.headerRow[0]._rowActionCount = this.rowActionCount;
424+
if (this._getSelection()?.isMultiSelect() && this._rowsLength !== this.rows.length) {
425+
this._rowsLength = this.rows.length;
426+
this.headerRow[0]._invalidate++;
427+
}
423428
}
424429
this.rows.forEach(row => {
425430
row._renderNavigated = this._renderNavigated;

packages/main/src/TableHeaderCell.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import TableCellBase from "./TableCellBase.js";
33
import TableHeaderCellTemplate from "./generated/templates/TableHeaderCellTemplate.lit.js";
44
import TableHeaderCellStyles from "./generated/themes/TableHeaderCell.css.js";
55
import Icon from "./Icon.js";
6-
import TableSortOrder from "./types/TableSortOrder.js";
6+
import SortOrder from "@ui5/webcomponents-base/dist/types/SortOrder.js";
77
import type TableHeaderCellActionBase from "./TableHeaderCellActionBase.js";
88
import "@ui5/webcomponents-icons/dist/sort-ascending.js";
99
import "@ui5/webcomponents-icons/dist/sort-descending.js";
@@ -98,7 +98,7 @@ class TableHeaderCell extends TableCellBase {
9898
* @public
9999
*/
100100
@property()
101-
sortIndicator: `${TableSortOrder}` = "None";
101+
sortIndicator: `${SortOrder}` = "None";
102102

103103
/**
104104
* Defines the action of the column.
@@ -130,15 +130,15 @@ class TableHeaderCell extends TableCellBase {
130130
// overwrite setting of TableCellBase so that the TableHeaderCell always uses the slot variable
131131
this.style.justifyContent = `var(--horizontal-align-${this._individualSlot})`;
132132
}
133-
if (this.sortIndicator !== TableSortOrder.None) {
133+
if (this.sortIndicator !== SortOrder.None) {
134134
this.setAttribute("aria-sort", this.sortIndicator.toLowerCase());
135135
} else if (this.hasAttribute("aria-sort")) {
136136
this.removeAttribute("aria-sort");
137137
}
138138
}
139139

140140
get _sortIcon() {
141-
if (this.sortIndicator !== TableSortOrder.None) {
141+
if (this.sortIndicator !== SortOrder.None) {
142142
return `sort-${this.sortIndicator.toLowerCase()}`;
143143
}
144144
}

packages/main/src/TableHeaderRow.ts

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
TABLE_SELECTION,
88
TABLE_ROW_POPIN,
99
TABLE_ROW_ACTIONS,
10+
TABLE_COLUMN_HEADER_ROW,
1011
} from "./generated/i18n/i18n-defaults.js";
1112

1213
/**
@@ -73,6 +74,11 @@ class TableHeaderRow extends TableRowBase {
7374
@property({ type: Boolean })
7475
sticky = false;
7576

77+
onEnterDOM(): void {
78+
super.onEnterDOM();
79+
this.setAttribute("aria-roledescription", TableRowBase.i18nBundle.getText(TABLE_COLUMN_HEADER_ROW));
80+
}
81+
7682
onBeforeRendering() {
7783
super.onBeforeRendering();
7884
if (this._table) {

packages/main/src/TableNavigation.ts

+2-14
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,6 @@
11
import {
2-
isUp,
3-
isUpShift,
4-
isDown,
5-
isDownShift,
6-
isLeft,
7-
isRight,
8-
isPageUp,
9-
isPageDown,
10-
isHome,
11-
isEnd,
12-
isTabNext,
13-
isTabPrevious,
2+
isUp, isUpShift, isDown, isDownShift, isLeft, isRight, isPageUp, isPageDown, isHome, isEnd, isTabNext, isTabPrevious,
143
} from "@ui5/webcomponents-base/dist/Keys.js";
15-
import isElementClickable from "@ui5/webcomponents-base/dist/util/isElementClickable.js";
164
import isElementHidden from "@ui5/webcomponents-base/dist/util/isElementHidden.js";
175
import getActiveElement from "@ui5/webcomponents-base/dist/util/getActiveElement.js";
186
import { getTabbableElements } from "@ui5/webcomponents-base/dist/util/TabbableElements.js";
@@ -259,7 +247,7 @@ class TableNavigation extends TableExtension {
259247
for (const target of e.composedPath() as any[]) {
260248
if (target.nodeType === Node.ELEMENT_NODE) {
261249
const element = target as HTMLElement;
262-
if (element.getAttribute("tabindex") === "-1" || isElementClickable(element)) {
250+
if (element.matches(":focus-within")) {
263251
focusableElement = element;
264252
break;
265253
}

packages/main/src/i18n/messagebundle.properties

+3-1
Original file line numberDiff line numberDiff line change
@@ -621,4 +621,6 @@ TABLE_ROW_ACTIONS=Row Actions
621621
#XACT: ARIA description for the row action navigation
622622
TABLE_NAVIGATION=Navigation
623623
#XTOL: Tooltip for the AI button in the column header to indicate that the column is generated by AI
624-
TABLE_GENERATED_BY_AI=Generated by AI
624+
TABLE_GENERATED_BY_AI=Generated by AI
625+
#XACT: ARIA description for the column header row of the table
626+
TABLE_COLUMN_HEADER_ROW=Column Header Row

packages/main/test/pages/Table.html

+1-2
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,8 @@
2727
<ui5-table-growing id="growing" type="Scroll" slot="features"></ui5-table-growing>
2828
<ui5-table-selection id="selection" selected="0 2" slot="features"></ui5-table-selection>
2929
<ui5-table-header-row slot="headerRow" sticky>
30-
<ui5-table-column id="produtCol" width="300px" sort-indicator="Descending" horizontal-align="Center">
30+
<ui5-table-header-cell id="produtCol" width="300px" sort-indicator="Descending">
3131
<ui5-label required>Text</ui5-label>
32-
<ui5-label slot="footler"></ui5-label>
3332
<ui5-table-header-cell-action-ai slot="action" id="ai1"></ui5-table-header-cell-action-ai>
3433
</ui5-table-header-cell>
3534
<ui5-table-header-cell id="supplierCol" width="400px">Supplier</ui5-table-header-cell>

0 commit comments

Comments
 (0)