Skip to content

Commit

Permalink
feat(ui5-table): add label component for the header cell
Browse files Browse the repository at this point in the history
  • Loading branch information
aborjinik committed Feb 5, 2025
1 parent af907a3 commit ad4afa3
Show file tree
Hide file tree
Showing 11 changed files with 245 additions and 22 deletions.
100 changes: 100 additions & 0 deletions packages/main/cypress/specs/Table.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import TableCell from "../../src/TableCell.js";
import TableRow from "../../src/TableRow.js";
import TableSelection from "../../src/TableSelection.js";
import TableHeaderCell from "../../src/TableHeaderCell.js";
import TableHeaderCellActionAI from "../../src/TableHeaderCellActionAI.js";
import Label from "../../src/Label.js";
import Input from "../../src/Input.js";
import Bar from "../../src/Bar.js";
Expand Down Expand Up @@ -663,3 +664,102 @@ describe("Table - Navigated Rows", () => {
});
});
});

describe("Table - HeaderCell", () => {
beforeEach(() => {
cy.mount(
<Table overflow-mode="Popin">
<TableHeaderRow slot="headerRow">
<TableHeaderCell min-width="300px">Column A</TableHeaderCell>
<TableHeaderCell min-width="200px" sort-indicator="Ascending">
<Label required wrappingType="None">Column B</Label>
<TableHeaderCellActionAI slot="action"></TableHeaderCellActionAI>
</TableHeaderCell>
<TableHeaderCell min-width="150px" popin-text="Popin Text">
<Label required>Column C</Label>
</TableHeaderCell>
</TableHeaderRow>
<TableRow>
<TableCell>Cell A</TableCell>
<TableCell>Cell B</TableCell>
<TableCell>Cell C</TableCell>
</TableRow>
<TableRow>
<TableCell>Cell A</TableCell>
<TableCell>Cell B</TableCell>
<TableCell>Cell C</TableCell>
</TableRow>
</Table>
);
cy.get("[ui5-table]").as("table").children("ui5-table-row").as("rows");
cy.get("@table").children("ui5-table-header-row").first().as("headerRow");
cy.get("@headerRow").get("ui5-table-header-cell").each(($headerCell, index) => {
cy.wrap($headerCell).as(`headerCell${index + 1}`);
});
cy.get("@rows").each(($row, index) => {
cy.wrap($row).as(`row${index + 1}`);
});
});

it("should render header-cell correctly", () => {
cy.get("@headerCell1").contains("Column A");
cy.get("@headerCell2").should("have.attr", "aria-sort", "ascending");
cy.get("@headerCell2").find("ui5-table-header-cell-action-ai").as("actionB");
cy.get("@actionB").shadow().find("ui5-button").as("actionBbutton");
cy.get("@actionBbutton").should("have.attr", "icon", "ai");
cy.get("@actionBbutton").should("have.attr", "tooltip", "Generated by AI");
cy.get("@actionB").invoke("on", "click", cy.stub().as("actionBclick"));
cy.get("@actionBbutton").realClick();
cy.get("@actionBclick").should("have.been.calledOnce");
cy.get("@headerCell2").shadow().find("ui5-icon").as("actionBicon");
cy.get("@actionBicon").should("have.attr", "name", "sort-ascending");

cy.get("@headerCell2").invoke("attr", "sort-indicator", "Descending");
cy.get("@headerCell2").shadow().find("ui5-icon").should("have.attr", "name", "sort-descending");
cy.get("@actionBicon").should("have.attr", "name", "sort-descending");
cy.get("@headerCell2").should("have.attr", "aria-sort", "descending");

cy.get("@headerCell2").invoke("attr", "sort-indicator", "None");
cy.get("@headerCell2").shadow().find("ui5-icon").should("not.exist");
cy.get("@headerCell2").should("not.have.attr", "aria-sort");

cy.get("@table").invoke("css", "width", "250px");
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(50);

cy.get("@row1").find("ui5-table-cell[_popin]").as("row1popins");
cy.get("@row1popins").first().as("row1popinB");
cy.get("@row1popinB").shadow().find("ui5-table-header-cell-action-ai").as("row1popinBaction");
cy.get("@row1popinBaction").shadow().find("ui5-button").as("row1popinBbutton");
cy.get("@row1popinBbutton").should("have.attr", "icon", "ai");
cy.get("@row1popinBbutton").should("have.attr", "design", "Transparent");
cy.get("@row1popinBbutton").should("have.attr", "tooltip", "Generated by AI");
cy.get("@row1popinBbutton").realClick();
cy.get("@actionBclick").invoke("getCall", 1).its("args.0.detail.targetRef").as("actionBclickTarget");
cy.get("@actionBclickTarget").should("have.attr", "icon", "ai");
cy.get("@actionBclickTarget").should("have.attr", "design", "Transparent");
cy.get("@actionBclickTarget").should("have.attr", "tooltip", "Generated by AI");

cy.get("@row1popinB").shadow().find("ui5-label").as("row1popinBlabel");
cy.get("@row1popinBlabel").contains("Column B");
cy.get("@row1popinBlabel").should("have.attr", "wrapping-type", "None");
cy.get("@row1popinBlabel").should("have.attr", "required");

cy.get("@row1popins").last().as("row1popinC");
cy.get("@row1popinC").shadow().find("ui5-label").should("not.exist");
cy.get("@row1popinC").shadow().should("have.text", "Popin Text:");
cy.get("@row1popinC").should("have.text", "Cell C");

cy.get("@row2").find("ui5-table-cell[_popin]").as("row2popins");
cy.get("@row2popins").first().as("row2popinB");
cy.get("@row2popinB").shadow().find("ui5-table-header-cell-action-ai").as("row2popinBaction");
cy.get("@row2popinBaction").shadow().find("ui5-button").as("row2popinBbutton");
cy.get("@row2popinBbutton").should("have.attr", "icon", "ai");
cy.get("@row2popinBbutton").should("have.attr", "tooltip", "Generated by AI");
cy.get("@row2popinBbutton").realClick();
cy.get("@actionBclick").invoke("getCall", 2).its("args.0.detail.targetRef").as("actionBclickTarget");
cy.get("@actionBclickTarget").should("have.attr", "icon", "ai");
cy.get("@actionBclickTarget").should("have.attr", "design", "Transparent");
cy.get("@actionBclickTarget").should("have.attr", "tooltip", "Generated by AI");
});
});
6 changes: 3 additions & 3 deletions packages/main/src/TableHeaderCell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class TableHeaderCell extends TableCellBase {
* Defines the sort indicator of the column.
*
* @default "None"
* @since 2.7.0
* @since 2.8.0
* @public
*/
@property()
Expand All @@ -103,10 +103,10 @@ class TableHeaderCell extends TableCellBase {
/**
* Defines the action of the column.
*
* **Note:** Only one `action` is allowed.
* **Note:** While multiple actions are technically possible, this is not supported.
*
* @public
* @since 2.7.0
* @since 2.8.0
*/
@slot()
action!: Array<TableHeaderCellActionBase>;
Expand Down
2 changes: 1 addition & 1 deletion packages/main/src/TableHeaderCellActionAI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import "@ui5/webcomponents-icons/dist/ai.js";
*
* @constructor
* @extends TableHeaderCellActionBase
* @since 2.7.0
* @since 2.8.0
* @public
*/
@customElement({ tag: "ui5-table-header-cell-action-ai" })
Expand Down
43 changes: 29 additions & 14 deletions packages/main/src/TableHeaderCellActionBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,35 @@ import TableHeaderCellActionBaseStyles from "./generated/themes/TableHeaderCellA
import Button from "./Button.js";
import type TableCell from "./TableCell.js";

/**
* Fired when a header cell action is clicked.
*
* @param {HTMLElement} targetRef The reference to the element that triggered the event
* @public
* @since 2.8.0
*/
type TableHeaderCellActionClickEventDetail = {
targetRef: HTMLElement;
};

/**
* Fired when a header cell action is clicked.
*
* @param {HTMLElement} targetRef The reference to the element, that triggered the event
* @public
* @since 2.8.0
*/
@eventStrict("click", {
bubbles: false,
})

/**
* @class
* The `TableHeaderCellActionBase` class serves as a foundation for table header cell actions.
* @constructor
* @abstract
* @extends UI5Element
* @since 2.7.0
* @since 2.8.0
* @public
*/
@customElement({
Expand All @@ -21,20 +43,9 @@ import type TableCell from "./TableCell.js";
template: TableHeaderCellActionBaseTemplate,
dependencies: [Button],
})

/**
* Fired when an action is clicked.
*
* @public
* @since 2.7.0
*/
@eventStrict("click", {
bubbles: false,
})

abstract class TableHeaderCellActionBase extends UI5Element {
eventDetails!: {
"click": void
"click": TableHeaderCellActionClickEventDetail,
}

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

_onClick(e: MouseEvent) {
const action = this.parentElement ? this : ((this.getRootNode() as ShadowRoot).host as TableCell)._headerCell.action[0] as this;
action.fireDecoratorEvent("click");
action.fireDecoratorEvent("click", { targetRef: e.target as HTMLElement });
e.stopPropagation();
}

Expand All @@ -62,3 +73,7 @@ abstract class TableHeaderCellActionBase extends UI5Element {
}

export default TableHeaderCellActionBase;

export type {
TableHeaderCellActionClickEventDetail,
};
2 changes: 1 addition & 1 deletion packages/main/src/i18n/messagebundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -620,5 +620,5 @@ TABLE_MORE_DESCRIPTION=To load more rows, press Enter or Space
TABLE_ROW_ACTIONS=Row Actions
#XACT: ARIA description for the row action navigation
TABLE_NAVIGATION=Navigation
#XTOL: Tooltip for the AI icon in the column header to indicate that the column is generated by AI
#XTOL: Tooltip for the AI button in the column header to indicate that the column is generated by AI
TABLE_GENERATED_BY_AI=Generated by AI
4 changes: 2 additions & 2 deletions packages/main/src/types/TableSortOrder.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Defines the sort order for `ui5-table` related components.
* Defines the sort order for `ui5-table`-related components.
*
* @public
* @since 2.7.0
Expand All @@ -21,7 +21,7 @@ enum TableSortOrder {
* Sorting is applied in descending order.
* @public
*/
Descending = "Descending"
Descending = "Descending",
}

export default TableSortOrder;
3 changes: 2 additions & 1 deletion packages/main/test/pages/Table.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@
<ui5-table-growing id="growing" type="Scroll" slot="features"></ui5-table-growing>
<ui5-table-selection id="selection" selected="0 2" slot="features"></ui5-table-selection>
<ui5-table-header-row slot="headerRow" sticky>
<ui5-table-header-cell id="produtCol" width="300px" sort-indicator="Descending">
<ui5-table-column id="produtCol" width="300px" sort-indicator="Descending" horizontal-align="Center">
<ui5-label required>Text</ui5-label>
<ui5-label slot="footler"></ui5-label>
<ui5-table-header-cell-action-ai slot="action" id="ai1"></ui5-table-header-cell-action-ai>
</ui5-table-header-cell>
<ui5-table-header-cell id="supplierCol" width="400px">Supplier</ui5-table-header-cell>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
slug: ../../TableHeaderCellActionAI
sidebar_class_name: expComponentBadge
---

import HeaderCellActionAI from "../../../_samples/main/Table/HeaderCellActionAI/HeaderCellActionAI.md";

<%COMPONENT_OVERVIEW%>

<%COMPONENT_METADATA%>

## Basic Sample

<HeaderCellActionAI/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import html from '!!raw-loader!./sample.html';
import js from '!!raw-loader!./main.js';

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`.

<Editor html={html} js={js} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import "@ui5/webcomponents/dist/Table.js";
import "@ui5/webcomponents/dist/TableRow.js";
import "@ui5/webcomponents/dist/TableCell.js";
import "@ui5/webcomponents/dist/TableHeaderRow.js";
import "@ui5/webcomponents/dist/TableHeaderCell.js";
import "@ui5/webcomponents/dist/TableHeaderCellActionAI.js";
import "@ui5/webcomponents/dist/Text.js";
import "@ui5/webcomponents/dist/Label.js";
import "@ui5/webcomponents/dist/Popover.js";

function showGeneratedByAIPopover(e) {
const popover = document.getElementById("generatedByAIPopover");
popover.opener = e.detail.targetRef;
popover.open = true;
}

const aiActionProduct = document.getElementById("aiActionProduct");
const aiActionPrice = document.getElementById("aiActionPrice");

aiActionProduct.addEventListener("click", showGeneratedByAIPopover);
aiActionPrice.addEventListener("click", showGeneratedByAIPopover);
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<!-- playground-fold -->
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sample</title>
</head>

<body style="background-color: var(--sapBackgroundColor)">
<div class="section">
<ui5-table id="table" overflow-mode="Popin">
<ui5-table-header-row slot="headerRow">
<!-- playground-fold-end -->
<ui5-table-header-cell id="produtCol" width="200px">
<ui5-label required>Product</ui5-label>
<ui5-table-header-cell-action-ai slot="action" id="aiActionProduct"></ui5-table-header-cell-action-ai>
</ui5-table-header-cell>
<!-- playground-fold -->
<ui5-table-header-cell id="supplierCol">Supplier</ui5-table-header-cell>
<ui5-table-header-cell id="dimensionsCol" importance="-1">Dimensions</ui5-table-header-cell>
<ui5-table-header-cell id="weightCol" popin-text="Weight">Weight</ui5-table-header-cell>
<!-- playground-fold-end -->
<ui5-table-header-cell id="priceCol" min-width="100px" horizontal-align="End" popin-text="Price in Popin" sort-indicator="Descending">
<ui5-label>Price</ui5-label>
<ui5-table-header-cell-action-ai slot="action" id="aiActionPrice"></ui5-table-header-cell-action-ai>
</ui5-table-header-cell>
<!-- playground-fold -->
</ui5-table-header-row>
<ui5-table-row row-key="0">
<ui5-table-cell><ui5-label><b>Notebook Basic 17</b><br>HT-1001</ui5-label></ui5-table-cell>
<ui5-table-cell><ui5-label>Smartcards</ui5-label></ui5-table-cell>
<ui5-table-cell><ui5-label>29 x 17 x 3.1 cm</ui5-label></ui5-table-cell>
<ui5-table-cell><ui5-label style="color: #2b7c2b"><b>4.5</b> KG</ui5-label></ui5-table-cell>
<ui5-table-cell><ui5-label><b>1249</b> EUR</ui5-label></ui5-table-cell>
</ui5-table-row>
<ui5-table-row row-key="1">
<ui5-table-cell><ui5-label><b>Notebook Basic 15</b><br>HT-1000</ui5-label></ui5-table-cell>
<ui5-table-cell><ui5-label>Very Best Screens</ui5-label></ui5-table-cell>
<ui5-table-cell><ui5-label>30 x 18 x 3 cm</ui5-label></ui5-table-cell>
<ui5-table-cell><ui5-label style="color: #2b7c2b"><b>4.2</b> KG</ui5-label></ui5-table-cell>
<ui5-table-cell><ui5-label><b>956</b> EUR</ui5-label></ui5-table-cell>
</ui5-table-row>
<ui5-table-row row-key="2">
<ui5-table-cell><ui5-label><b>Notebook Basic 18</b><br>HT-1002</ui5-label></ui5-table-cell>
<ui5-table-cell><ui5-label>Technocom</ui5-label></ui5-table-cell>
<ui5-table-cell><ui5-label>32 x 21 x 4 cm</ui5-label></ui5-table-cell>
<ui5-table-cell><ui5-label style="color: #2b7c2b"><b>3.7</b> KG</ui5-label></ui5-table-cell>
<ui5-table-cell><ui5-label><b>290</b> EUR</ui5-label></ui5-table-cell>
</ui5-table-row>
</ui5-table>
<!-- playground-fold-end -->
<ui5-popover id="generatedByAIPopover" style="width: 25rem;">
<ui5-text>
This content was partially or fully generated by artificial intelligence (AI) technologies.<br><br>
The AI-generated content may contain inaccuracies due to using multiple information sources. Verify results before use.
</ui5-text>
</ui5-popover>
<!-- playground-fold -->
</div>
<script type="module" src="main.js"></script>
</body>

</html>
<!-- playground-fold-end -->

0 comments on commit ad4afa3

Please sign in to comment.