Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions packages/main/cypress/specs/Table.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,50 @@ describe("Table - Interactive Rows", () => {
cy.get("@buttonClickHandler").should("have.been.calledThrice");
cy.get("@rowClickHandler").should("have.been.calledThrice");
});

it("fires click event on the row element", () => {
cy.mount(
<Table id="table1">
<TableHeaderRow slot="headerRow">
<TableHeaderCell>ColumnA</TableHeaderCell>
<TableHeaderCell>ColumnB</TableHeaderCell>
</TableHeaderRow>
<TableRow id="row1" rowKey="1">
<TableCell><Label>Cell A</Label></TableCell>
<TableCell><Label>Cell B</Label></TableCell>
</TableRow>
<TableRow id="row2" rowKey="2" interactive={true}>
<TableCell><Label>Cell A</Label></TableCell>
<TableCell><Label>Cell B</Label></TableCell>
</TableRow>
</Table>
);

cy.get("#row1").invoke("on", "click", cy.stub().as("row1ClickSpy"));
cy.get("#row2").invoke("on", "click", cy.stub().as("row2ClickSpy"));

// Non-interactive row should not fire custom click
cy.get("#row1").realClick();
cy.get("@row1ClickSpy").then(stub => {
const customClicks = (stub as unknown as Cypress.Agent<sinon.SinonStub>).getCalls().filter(call => call.args[0].originalEvent instanceof CustomEvent);
expect(customClicks).to.have.length(0);
});

// Interactive row does not fire custom click on mouse click (native click bubbles normally)
cy.get("#row2").realClick();
cy.get("@row2ClickSpy").then(stub => {
const customClicks = (stub as unknown as Cypress.Agent<sinon.SinonStub>).getCalls().filter(call => call.args[0].originalEvent instanceof CustomEvent);
expect(customClicks).to.have.length(0);
});

// Interactive row fires custom click on Enter key
cy.get("#row2").realPress("Enter");
cy.get("@row2ClickSpy").then(stub => {
const customClicks = (stub as unknown as Cypress.Agent<sinon.SinonStub>).getCalls().filter(call => call.args[0].originalEvent instanceof CustomEvent);
expect(customClicks).to.have.length(1);
expect(customClicks[0].args[0].originalEvent).to.have.property("detail");
});
});
});

describe("Table - HeaderCell", () => {
Expand Down
34 changes: 31 additions & 3 deletions packages/main/src/TableRow.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { customElement, slotStrict as slot, property } from "@ui5/webcomponents-base/dist/decorators.js";
import {
customElement, slotStrict as slot, property, eventStrict,
} from "@ui5/webcomponents-base/dist/decorators.js";
import { isEnter } from "@ui5/webcomponents-base/dist/Keys.js";
import getActiveElement from "@ui5/webcomponents-base/dist/util/getActiveElement.js";
import query from "@ui5/webcomponents-base/dist/decorators/query.js";
Expand Down Expand Up @@ -36,7 +38,23 @@ import {
styles: [TableRowBase.styles, TableRowCss],
template: TableRowTemplate,
})
/**
* Fired when the row is activated by the user via click or Enter key.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is not even true.

*
* **Note:** This event is not fired when the row has `behavior="RowOnly"` selection.
* In that case, use the selection component's `change` event instead.
*
* @public
* @since 2.9.0
*/
@eventStrict("click", {
bubbles: true,
})
class TableRow extends TableRowBase<TableCell> {
eventDetails!: TableRowBase["eventDetails"] & {
click: void
}

/**
* Defines the cells of the component.
*
Expand Down Expand Up @@ -160,15 +178,25 @@ class TableRow extends TableRowBase<TableCell> {

if (eventOrigin === this && this._isInteractive && isEnter(e)) {
this._setActive("keyup");
this._onclick();
this._handleClick(true);
}
}

_onclick(e: Event) {
if (e instanceof CustomEvent) {
return;
}
this._handleClick(false);
}

_onclick() {
_handleClick(fireClick = false) {
if (this === getActiveElement()) {
if (this._isSelectable && !this._hasSelector) {
this._onSelectionChange();
} else if (this.interactive || this._isNavigable) {
if (fireClick) {
this.fireDecoratorEvent("click");
}
this._table?._onRowClick(this);
}
}
Expand Down
4 changes: 4 additions & 0 deletions packages/main/src/TableRowBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ import {
styles: TableRowBaseCss,
})
abstract class TableRowBase<TCell extends TableCellBase = TableCellBase> extends UI5Element {
eventDetails!: {
click: void
}

cells!: Array<TCell>;

@property({ type: Number, noAttribute: true })
Expand Down
8 changes: 8 additions & 0 deletions packages/website/docs/_components_pages/main/Table/Table.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import StickyHeader from "../../../_samples/main/Table/StickyHeader/StickyHeader
import StickyHeaderContainer from "../../../_samples/main/Table/StickyHeaderContainer/StickyHeaderContainer.md";
import NoDataSlot from "../../../_samples/main/Table/NoDataSlot/NoDataSlot.md";
import Interactive from "../../../_samples/main/Table/Interactive/Interactive.md";
import RowClick from "../../../_samples/main/Table/RowClick/RowClick.md";
import DragAndDrop from "../../../_samples/main/Table/DragAndDrop/DragAndDrop.md";

<%COMPONENT_OVERVIEW%>
Expand Down Expand Up @@ -57,6 +58,13 @@ will fire the `row-click` event.

<Interactive />

### Row Click Event

The `click` event is fired directly on `ui5-table-row` when an interactive row is activated via mouse click or keyboard Enter.
This allows attaching click handlers directly on row elements, which is particularly useful for framework wrappers like React.

<RowClick />

### Drag and Drop

Enable Drag and Drop by using the `move-over` and `move` event in combination with the `movable` property on the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ slug: ../../TableRow
---

import Interactive from "../../../_samples/main/Table/Interactive/Interactive.md";
import RowClick from "../../../_samples/main/Table/RowClick/RowClick.md";
import DragAndDrop from "../../../_samples/main/Table/DragAndDrop/DragAndDrop.md";

<%COMPONENT_OVERVIEW%>
Expand All @@ -16,6 +17,13 @@ will fire the `row-click` event.

<Interactive />

## Row Click Event

The `click` event is fired directly on `ui5-table-row` when an interactive row is activated via mouse click or keyboard Enter.
This allows attaching click handlers directly on row elements, which is particularly useful for framework wrappers like React.

<RowClick />

## Movable Rows

Adding the `movable` property enables the `ui5-table-row` for drag and drop operations.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import html from '!!raw-loader!./sample.html';
import js from '!!raw-loader!./main.js';
import react from '!!raw-loader!./sample.tsx';

<Editor html={html} js={js} react={react} />
17 changes: 17 additions & 0 deletions packages/website/docs/_samples/main/Table/RowClick/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import "@ui5/webcomponents/dist/Table.js";
import "@ui5/webcomponents/dist/TableHeaderRow.js";
import "@ui5/webcomponents/dist/TableHeaderCell.js";
import "@ui5/webcomponents/dist/Label.js";
import "@ui5/webcomponents/dist/Toast.js";

const toast = document.getElementById("message");

document.getElementById("row-a").addEventListener("click", () => {
toast.textContent = "Row A clicked!";
toast.open = true;
});

document.getElementById("row-b").addEventListener("click", () => {
toast.textContent = "Row B clicked!";
toast.open = true;
});
38 changes: 38 additions & 0 deletions packages/website/docs/_samples/main/Table/RowClick/sample.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!-- 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)">
<!-- playground-fold-end -->
<ui5-toast id="message"></ui5-toast>
<ui5-table id="table">
<!-- playground-fold -->
<ui5-table-header-row slot="headerRow">
<ui5-table-header-cell width="300px">Product</ui5-table-header-cell>
<ui5-table-header-cell width="200px">Supplier</ui5-table-header-cell>
<ui5-table-header-cell width="100px">Price</ui5-table-header-cell>
</ui5-table-header-row>
<!-- playground-fold-end -->
<ui5-table-row id="row-a" row-key="a" interactive>
<ui5-table-cell><ui5-label>Notebook Basic 15</ui5-label></ui5-table-cell>
<ui5-table-cell><ui5-label>Very Best Screens</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 id="row-b" row-key="b" interactive>
<ui5-table-cell><ui5-label>Notebook Basic 17</ui5-label></ui5-table-cell>
<ui5-table-cell><ui5-label>Smartcards</ui5-label></ui5-table-cell>
<ui5-table-cell><ui5-label><b>1249</b> EUR</ui5-label></ui5-table-cell>
</ui5-table-row>
</ui5-table>
<!-- playground-fold -->
<script type="module" src="main.js"></script>
</body>

</html>
<!-- playground-fold-end -->
71 changes: 71 additions & 0 deletions packages/website/docs/_samples/main/Table/RowClick/sample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { useRef } from "react";
import createReactComponent from "@ui5/webcomponents-base/dist/createReactComponent.js";
import LabelClass from "@ui5/webcomponents/dist/Label.js";
import TableClass from "@ui5/webcomponents/dist/Table.js";
import TableCellClass from "@ui5/webcomponents/dist/TableCell.js";
import TableHeaderCellClass from "@ui5/webcomponents/dist/TableHeaderCell.js";
import TableHeaderRowClass from "@ui5/webcomponents/dist/TableHeaderRow.js";
import TableRowClass from "@ui5/webcomponents/dist/TableRow.js";
import ToastClass from "@ui5/webcomponents/dist/Toast.js";

const Label = createReactComponent(LabelClass);
const Table = createReactComponent(TableClass);
const TableCell = createReactComponent(TableCellClass);
const TableHeaderCell = createReactComponent(TableHeaderCellClass);
const TableHeaderRow = createReactComponent(TableHeaderRowClass);
const TableRow = createReactComponent(TableRowClass);
const Toast = createReactComponent(ToastClass);

function App() {
const toastRef = useRef(null);

const showToast = (message: string) => {
if (toastRef.current) {
toastRef.current!.textContent = message;
toastRef.current!.open = true;
}
};

return (
<>
<Toast ref={toastRef} id="message" />
<Table id="table">
{/* playground-fold */}
<TableHeaderRow slot="headerRow">
<TableHeaderCell width="300px">Product</TableHeaderCell>
<TableHeaderCell width="200px">Supplier</TableHeaderCell>
<TableHeaderCell width="100px">Price</TableHeaderCell>
</TableHeaderRow>
{/* playground-fold-end */}
<TableRow rowKey="a" interactive={true} onClick={() => showToast("Row A clicked!")}>
<TableCell>
<Label>Notebook Basic 15</Label>
</TableCell>
<TableCell>
<Label>Very Best Screens</Label>
</TableCell>
<TableCell>
<Label>
<b>956</b> EUR
</Label>
</TableCell>
</TableRow>
<TableRow rowKey="b" interactive={true} onClick={() => showToast("Row B clicked!")}>
<TableCell>
<Label>Notebook Basic 17</Label>
</TableCell>
<TableCell>
<Label>Smartcards</Label>
</TableCell>
<TableCell>
<Label>
<b>1249</b> EUR
</Label>
</TableCell>
</TableRow>
</Table>
</>
);
}

export default App;
Loading