Skip to content

Commit ad4afa3

Browse files
committed
feat(ui5-table): add label component for the header cell
1 parent af907a3 commit ad4afa3

File tree

11 files changed

+245
-22
lines changed

11 files changed

+245
-22
lines changed

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

+100
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import TableCell from "../../src/TableCell.js";
44
import TableRow from "../../src/TableRow.js";
55
import TableSelection from "../../src/TableSelection.js";
66
import TableHeaderCell from "../../src/TableHeaderCell.js";
7+
import TableHeaderCellActionAI from "../../src/TableHeaderCellActionAI.js";
78
import Label from "../../src/Label.js";
89
import Input from "../../src/Input.js";
910
import Bar from "../../src/Bar.js";
@@ -663,3 +664,102 @@ describe("Table - Navigated Rows", () => {
663664
});
664665
});
665666
});
667+
668+
describe("Table - HeaderCell", () => {
669+
beforeEach(() => {
670+
cy.mount(
671+
<Table overflow-mode="Popin">
672+
<TableHeaderRow slot="headerRow">
673+
<TableHeaderCell min-width="300px">Column A</TableHeaderCell>
674+
<TableHeaderCell min-width="200px" sort-indicator="Ascending">
675+
<Label required wrappingType="None">Column B</Label>
676+
<TableHeaderCellActionAI slot="action"></TableHeaderCellActionAI>
677+
</TableHeaderCell>
678+
<TableHeaderCell min-width="150px" popin-text="Popin Text">
679+
<Label required>Column C</Label>
680+
</TableHeaderCell>
681+
</TableHeaderRow>
682+
<TableRow>
683+
<TableCell>Cell A</TableCell>
684+
<TableCell>Cell B</TableCell>
685+
<TableCell>Cell C</TableCell>
686+
</TableRow>
687+
<TableRow>
688+
<TableCell>Cell A</TableCell>
689+
<TableCell>Cell B</TableCell>
690+
<TableCell>Cell C</TableCell>
691+
</TableRow>
692+
</Table>
693+
);
694+
cy.get("[ui5-table]").as("table").children("ui5-table-row").as("rows");
695+
cy.get("@table").children("ui5-table-header-row").first().as("headerRow");
696+
cy.get("@headerRow").get("ui5-table-header-cell").each(($headerCell, index) => {
697+
cy.wrap($headerCell).as(`headerCell${index + 1}`);
698+
});
699+
cy.get("@rows").each(($row, index) => {
700+
cy.wrap($row).as(`row${index + 1}`);
701+
});
702+
});
703+
704+
it("should render header-cell correctly", () => {
705+
cy.get("@headerCell1").contains("Column A");
706+
cy.get("@headerCell2").should("have.attr", "aria-sort", "ascending");
707+
cy.get("@headerCell2").find("ui5-table-header-cell-action-ai").as("actionB");
708+
cy.get("@actionB").shadow().find("ui5-button").as("actionBbutton");
709+
cy.get("@actionBbutton").should("have.attr", "icon", "ai");
710+
cy.get("@actionBbutton").should("have.attr", "tooltip", "Generated by AI");
711+
cy.get("@actionB").invoke("on", "click", cy.stub().as("actionBclick"));
712+
cy.get("@actionBbutton").realClick();
713+
cy.get("@actionBclick").should("have.been.calledOnce");
714+
cy.get("@headerCell2").shadow().find("ui5-icon").as("actionBicon");
715+
cy.get("@actionBicon").should("have.attr", "name", "sort-ascending");
716+
717+
cy.get("@headerCell2").invoke("attr", "sort-indicator", "Descending");
718+
cy.get("@headerCell2").shadow().find("ui5-icon").should("have.attr", "name", "sort-descending");
719+
cy.get("@actionBicon").should("have.attr", "name", "sort-descending");
720+
cy.get("@headerCell2").should("have.attr", "aria-sort", "descending");
721+
722+
cy.get("@headerCell2").invoke("attr", "sort-indicator", "None");
723+
cy.get("@headerCell2").shadow().find("ui5-icon").should("not.exist");
724+
cy.get("@headerCell2").should("not.have.attr", "aria-sort");
725+
726+
cy.get("@table").invoke("css", "width", "250px");
727+
// eslint-disable-next-line cypress/no-unnecessary-waiting
728+
cy.wait(50);
729+
730+
cy.get("@row1").find("ui5-table-cell[_popin]").as("row1popins");
731+
cy.get("@row1popins").first().as("row1popinB");
732+
cy.get("@row1popinB").shadow().find("ui5-table-header-cell-action-ai").as("row1popinBaction");
733+
cy.get("@row1popinBaction").shadow().find("ui5-button").as("row1popinBbutton");
734+
cy.get("@row1popinBbutton").should("have.attr", "icon", "ai");
735+
cy.get("@row1popinBbutton").should("have.attr", "design", "Transparent");
736+
cy.get("@row1popinBbutton").should("have.attr", "tooltip", "Generated by AI");
737+
cy.get("@row1popinBbutton").realClick();
738+
cy.get("@actionBclick").invoke("getCall", 1).its("args.0.detail.targetRef").as("actionBclickTarget");
739+
cy.get("@actionBclickTarget").should("have.attr", "icon", "ai");
740+
cy.get("@actionBclickTarget").should("have.attr", "design", "Transparent");
741+
cy.get("@actionBclickTarget").should("have.attr", "tooltip", "Generated by AI");
742+
743+
cy.get("@row1popinB").shadow().find("ui5-label").as("row1popinBlabel");
744+
cy.get("@row1popinBlabel").contains("Column B");
745+
cy.get("@row1popinBlabel").should("have.attr", "wrapping-type", "None");
746+
cy.get("@row1popinBlabel").should("have.attr", "required");
747+
748+
cy.get("@row1popins").last().as("row1popinC");
749+
cy.get("@row1popinC").shadow().find("ui5-label").should("not.exist");
750+
cy.get("@row1popinC").shadow().should("have.text", "Popin Text:");
751+
cy.get("@row1popinC").should("have.text", "Cell C");
752+
753+
cy.get("@row2").find("ui5-table-cell[_popin]").as("row2popins");
754+
cy.get("@row2popins").first().as("row2popinB");
755+
cy.get("@row2popinB").shadow().find("ui5-table-header-cell-action-ai").as("row2popinBaction");
756+
cy.get("@row2popinBaction").shadow().find("ui5-button").as("row2popinBbutton");
757+
cy.get("@row2popinBbutton").should("have.attr", "icon", "ai");
758+
cy.get("@row2popinBbutton").should("have.attr", "tooltip", "Generated by AI");
759+
cy.get("@row2popinBbutton").realClick();
760+
cy.get("@actionBclick").invoke("getCall", 2).its("args.0.detail.targetRef").as("actionBclickTarget");
761+
cy.get("@actionBclickTarget").should("have.attr", "icon", "ai");
762+
cy.get("@actionBclickTarget").should("have.attr", "design", "Transparent");
763+
cy.get("@actionBclickTarget").should("have.attr", "tooltip", "Generated by AI");
764+
});
765+
});

packages/main/src/TableHeaderCell.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class TableHeaderCell extends TableCellBase {
9494
* Defines the sort indicator of the column.
9595
*
9696
* @default "None"
97-
* @since 2.7.0
97+
* @since 2.8.0
9898
* @public
9999
*/
100100
@property()
@@ -103,10 +103,10 @@ class TableHeaderCell extends TableCellBase {
103103
/**
104104
* Defines the action of the column.
105105
*
106-
* **Note:** Only one `action` is allowed.
106+
* **Note:** While multiple actions are technically possible, this is not supported.
107107
*
108108
* @public
109-
* @since 2.7.0
109+
* @since 2.8.0
110110
*/
111111
@slot()
112112
action!: Array<TableHeaderCellActionBase>;

packages/main/src/TableHeaderCellActionAI.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import "@ui5/webcomponents-icons/dist/ai.js";
1717
*
1818
* @constructor
1919
* @extends TableHeaderCellActionBase
20-
* @since 2.7.0
20+
* @since 2.8.0
2121
* @public
2222
*/
2323
@customElement({ tag: "ui5-table-header-cell-action-ai" })

packages/main/src/TableHeaderCellActionBase.ts

+29-14
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,35 @@ import TableHeaderCellActionBaseStyles from "./generated/themes/TableHeaderCellA
66
import Button from "./Button.js";
77
import type TableCell from "./TableCell.js";
88

9+
/**
10+
* Fired when a header cell action is clicked.
11+
*
12+
* @param {HTMLElement} targetRef The reference to the element that triggered the event
13+
* @public
14+
* @since 2.8.0
15+
*/
16+
type TableHeaderCellActionClickEventDetail = {
17+
targetRef: HTMLElement;
18+
};
19+
20+
/**
21+
* Fired when a header cell action is clicked.
22+
*
23+
* @param {HTMLElement} targetRef The reference to the element, that triggered the event
24+
* @public
25+
* @since 2.8.0
26+
*/
27+
@eventStrict("click", {
28+
bubbles: false,
29+
})
30+
931
/**
1032
* @class
1133
* The `TableHeaderCellActionBase` class serves as a foundation for table header cell actions.
1234
* @constructor
1335
* @abstract
1436
* @extends UI5Element
15-
* @since 2.7.0
37+
* @since 2.8.0
1638
* @public
1739
*/
1840
@customElement({
@@ -21,20 +43,9 @@ import type TableCell from "./TableCell.js";
2143
template: TableHeaderCellActionBaseTemplate,
2244
dependencies: [Button],
2345
})
24-
25-
/**
26-
* Fired when an action is clicked.
27-
*
28-
* @public
29-
* @since 2.7.0
30-
*/
31-
@eventStrict("click", {
32-
bubbles: false,
33-
})
34-
3546
abstract class TableHeaderCellActionBase extends UI5Element {
3647
eventDetails!: {
37-
"click": void
48+
"click": TableHeaderCellActionClickEventDetail,
3849
}
3950

4051
abstract getRenderInfo(): {
@@ -48,7 +59,7 @@ abstract class TableHeaderCellActionBase extends UI5Element {
4859

4960
_onClick(e: MouseEvent) {
5061
const action = this.parentElement ? this : ((this.getRootNode() as ShadowRoot).host as TableCell)._headerCell.action[0] as this;
51-
action.fireDecoratorEvent("click");
62+
action.fireDecoratorEvent("click", { targetRef: e.target as HTMLElement });
5263
e.stopPropagation();
5364
}
5465

@@ -62,3 +73,7 @@ abstract class TableHeaderCellActionBase extends UI5Element {
6273
}
6374

6475
export default TableHeaderCellActionBase;
76+
77+
export type {
78+
TableHeaderCellActionClickEventDetail,
79+
};

packages/main/src/i18n/messagebundle.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -620,5 +620,5 @@ TABLE_MORE_DESCRIPTION=To load more rows, press Enter or Space
620620
TABLE_ROW_ACTIONS=Row Actions
621621
#XACT: ARIA description for the row action navigation
622622
TABLE_NAVIGATION=Navigation
623-
#XTOL: Tooltip for the AI icon in the column header to indicate that the column is generated by AI
623+
#XTOL: Tooltip for the AI button in the column header to indicate that the column is generated by AI
624624
TABLE_GENERATED_BY_AI=Generated by AI

packages/main/src/types/TableSortOrder.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Defines the sort order for `ui5-table` related components.
2+
* Defines the sort order for `ui5-table`-related components.
33
*
44
* @public
55
* @since 2.7.0
@@ -21,7 +21,7 @@ enum TableSortOrder {
2121
* Sorting is applied in descending order.
2222
* @public
2323
*/
24-
Descending = "Descending"
24+
Descending = "Descending",
2525
}
2626

2727
export default TableSortOrder;

packages/main/test/pages/Table.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@
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-header-cell id="produtCol" width="300px" sort-indicator="Descending">
30+
<ui5-table-column id="produtCol" width="300px" sort-indicator="Descending" horizontal-align="Center">
3131
<ui5-label required>Text</ui5-label>
32+
<ui5-label slot="footler"></ui5-label>
3233
<ui5-table-header-cell-action-ai slot="action" id="ai1"></ui5-table-header-cell-action-ai>
3334
</ui5-table-header-cell>
3435
<ui5-table-header-cell id="supplierCol" width="400px">Supplier</ui5-table-header-cell>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
slug: ../../TableHeaderCellActionAI
3+
sidebar_class_name: expComponentBadge
4+
---
5+
6+
import HeaderCellActionAI from "../../../_samples/main/Table/HeaderCellActionAI/HeaderCellActionAI.md";
7+
8+
<%COMPONENT_OVERVIEW%>
9+
10+
<%COMPONENT_METADATA%>
11+
12+
## Basic Sample
13+
14+
<HeaderCellActionAI/>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import html from '!!raw-loader!./sample.html';
2+
import js from '!!raw-loader!./main.js';
3+
4+
To indicate that the column is generated by an AI model, you can add an action to the header cell. This is done by adding a `ui5-table-header-cell-action-ai` component to the `action` slot of the `ui5-table-header-cell`.
5+
6+
<Editor html={html} js={js} />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import "@ui5/webcomponents/dist/Table.js";
2+
import "@ui5/webcomponents/dist/TableRow.js";
3+
import "@ui5/webcomponents/dist/TableCell.js";
4+
import "@ui5/webcomponents/dist/TableHeaderRow.js";
5+
import "@ui5/webcomponents/dist/TableHeaderCell.js";
6+
import "@ui5/webcomponents/dist/TableHeaderCellActionAI.js";
7+
import "@ui5/webcomponents/dist/Text.js";
8+
import "@ui5/webcomponents/dist/Label.js";
9+
import "@ui5/webcomponents/dist/Popover.js";
10+
11+
function showGeneratedByAIPopover(e) {
12+
const popover = document.getElementById("generatedByAIPopover");
13+
popover.opener = e.detail.targetRef;
14+
popover.open = true;
15+
}
16+
17+
const aiActionProduct = document.getElementById("aiActionProduct");
18+
const aiActionPrice = document.getElementById("aiActionPrice");
19+
20+
aiActionProduct.addEventListener("click", showGeneratedByAIPopover);
21+
aiActionPrice.addEventListener("click", showGeneratedByAIPopover);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<!-- playground-fold -->
2+
<!DOCTYPE html>
3+
<html lang="en">
4+
5+
<head>
6+
<meta charset="UTF-8">
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
8+
<title>Sample</title>
9+
</head>
10+
11+
<body style="background-color: var(--sapBackgroundColor)">
12+
<div class="section">
13+
<ui5-table id="table" overflow-mode="Popin">
14+
<ui5-table-header-row slot="headerRow">
15+
<!-- playground-fold-end -->
16+
<ui5-table-header-cell id="produtCol" width="200px">
17+
<ui5-label required>Product</ui5-label>
18+
<ui5-table-header-cell-action-ai slot="action" id="aiActionProduct"></ui5-table-header-cell-action-ai>
19+
</ui5-table-header-cell>
20+
<!-- playground-fold -->
21+
<ui5-table-header-cell id="supplierCol">Supplier</ui5-table-header-cell>
22+
<ui5-table-header-cell id="dimensionsCol" importance="-1">Dimensions</ui5-table-header-cell>
23+
<ui5-table-header-cell id="weightCol" popin-text="Weight">Weight</ui5-table-header-cell>
24+
<!-- playground-fold-end -->
25+
<ui5-table-header-cell id="priceCol" min-width="100px" horizontal-align="End" popin-text="Price in Popin" sort-indicator="Descending">
26+
<ui5-label>Price</ui5-label>
27+
<ui5-table-header-cell-action-ai slot="action" id="aiActionPrice"></ui5-table-header-cell-action-ai>
28+
</ui5-table-header-cell>
29+
<!-- playground-fold -->
30+
</ui5-table-header-row>
31+
<ui5-table-row row-key="0">
32+
<ui5-table-cell><ui5-label><b>Notebook Basic 17</b><br>HT-1001</ui5-label></ui5-table-cell>
33+
<ui5-table-cell><ui5-label>Smartcards</ui5-label></ui5-table-cell>
34+
<ui5-table-cell><ui5-label>29 x 17 x 3.1 cm</ui5-label></ui5-table-cell>
35+
<ui5-table-cell><ui5-label style="color: #2b7c2b"><b>4.5</b> KG</ui5-label></ui5-table-cell>
36+
<ui5-table-cell><ui5-label><b>1249</b> EUR</ui5-label></ui5-table-cell>
37+
</ui5-table-row>
38+
<ui5-table-row row-key="1">
39+
<ui5-table-cell><ui5-label><b>Notebook Basic 15</b><br>HT-1000</ui5-label></ui5-table-cell>
40+
<ui5-table-cell><ui5-label>Very Best Screens</ui5-label></ui5-table-cell>
41+
<ui5-table-cell><ui5-label>30 x 18 x 3 cm</ui5-label></ui5-table-cell>
42+
<ui5-table-cell><ui5-label style="color: #2b7c2b"><b>4.2</b> KG</ui5-label></ui5-table-cell>
43+
<ui5-table-cell><ui5-label><b>956</b> EUR</ui5-label></ui5-table-cell>
44+
</ui5-table-row>
45+
<ui5-table-row row-key="2">
46+
<ui5-table-cell><ui5-label><b>Notebook Basic 18</b><br>HT-1002</ui5-label></ui5-table-cell>
47+
<ui5-table-cell><ui5-label>Technocom</ui5-label></ui5-table-cell>
48+
<ui5-table-cell><ui5-label>32 x 21 x 4 cm</ui5-label></ui5-table-cell>
49+
<ui5-table-cell><ui5-label style="color: #2b7c2b"><b>3.7</b> KG</ui5-label></ui5-table-cell>
50+
<ui5-table-cell><ui5-label><b>290</b> EUR</ui5-label></ui5-table-cell>
51+
</ui5-table-row>
52+
</ui5-table>
53+
<!-- playground-fold-end -->
54+
<ui5-popover id="generatedByAIPopover" style="width: 25rem;">
55+
<ui5-text>
56+
This content was partially or fully generated by artificial intelligence (AI) technologies.<br><br>
57+
The AI-generated content may contain inaccuracies due to using multiple information sources. Verify results before use.
58+
</ui5-text>
59+
</ui5-popover>
60+
<!-- playground-fold -->
61+
</div>
62+
<script type="module" src="main.js"></script>
63+
</body>
64+
65+
</html>
66+
<!-- playground-fold-end -->

0 commit comments

Comments
 (0)