diff --git a/packages/main/cypress/specs/Table.cy.tsx b/packages/main/cypress/specs/Table.cy.tsx
index f41b55637967..a59c4c8b6c35 100644
--- a/packages/main/cypress/specs/Table.cy.tsx
+++ b/packages/main/cypress/specs/Table.cy.tsx
@@ -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(
+
+
+ ColumnA
+ ColumnB
+
+
+
+
+
+
+
+
+
+
+ );
+
+ 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).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).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).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", () => {
diff --git a/packages/main/src/TableRow.ts b/packages/main/src/TableRow.ts
index e60c521d5194..04eb8499bc4b 100644
--- a/packages/main/src/TableRow.ts
+++ b/packages/main/src/TableRow.ts
@@ -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";
@@ -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.
+ *
+ * **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 {
+ eventDetails!: TableRowBase["eventDetails"] & {
+ click: void
+ }
+
/**
* Defines the cells of the component.
*
@@ -160,15 +178,25 @@ class TableRow extends TableRowBase {
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);
}
}
diff --git a/packages/main/src/TableRowBase.ts b/packages/main/src/TableRowBase.ts
index 0798cdad6dfd..fedf563abc73 100644
--- a/packages/main/src/TableRowBase.ts
+++ b/packages/main/src/TableRowBase.ts
@@ -26,6 +26,10 @@ import {
styles: TableRowBaseCss,
})
abstract class TableRowBase extends UI5Element {
+ eventDetails!: {
+ click: void
+ }
+
cells!: Array;
@property({ type: Number, noAttribute: true })
diff --git a/packages/website/docs/_components_pages/main/Table/Table.mdx b/packages/website/docs/_components_pages/main/Table/Table.mdx
index dd2d159d020d..b2800099847d 100644
--- a/packages/website/docs/_components_pages/main/Table/Table.mdx
+++ b/packages/website/docs/_components_pages/main/Table/Table.mdx
@@ -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%>
@@ -57,6 +58,13 @@ will fire the `row-click` event.
+### 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.
+
+
+
### Drag and Drop
Enable Drag and Drop by using the `move-over` and `move` event in combination with the `movable` property on the
diff --git a/packages/website/docs/_components_pages/main/Table/TableRow.mdx b/packages/website/docs/_components_pages/main/Table/TableRow.mdx
index 9b7821ceb777..c741295a695d 100644
--- a/packages/website/docs/_components_pages/main/Table/TableRow.mdx
+++ b/packages/website/docs/_components_pages/main/Table/TableRow.mdx
@@ -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%>
@@ -16,6 +17,13 @@ will fire the `row-click` event.
+## 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.
+
+
+
## Movable Rows
Adding the `movable` property enables the `ui5-table-row` for drag and drop operations.
diff --git a/packages/website/docs/_samples/main/Table/RowClick/RowClick.md b/packages/website/docs/_samples/main/Table/RowClick/RowClick.md
new file mode 100644
index 000000000000..0c062a836e84
--- /dev/null
+++ b/packages/website/docs/_samples/main/Table/RowClick/RowClick.md
@@ -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';
+
+
diff --git a/packages/website/docs/_samples/main/Table/RowClick/main.js b/packages/website/docs/_samples/main/Table/RowClick/main.js
new file mode 100644
index 000000000000..d1e22690a2df
--- /dev/null
+++ b/packages/website/docs/_samples/main/Table/RowClick/main.js
@@ -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;
+});
diff --git a/packages/website/docs/_samples/main/Table/RowClick/sample.html b/packages/website/docs/_samples/main/Table/RowClick/sample.html
new file mode 100644
index 000000000000..f13f850b8a25
--- /dev/null
+++ b/packages/website/docs/_samples/main/Table/RowClick/sample.html
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+ Sample
+
+
+
+
+
+
+
+
+ Product
+ Supplier
+ Price
+
+
+
+ Notebook Basic 15
+ Very Best Screens
+ 956 EUR
+
+
+ Notebook Basic 17
+ Smartcards
+ 1249 EUR
+
+
+
+
+
+
+
+
diff --git a/packages/website/docs/_samples/main/Table/RowClick/sample.tsx b/packages/website/docs/_samples/main/Table/RowClick/sample.tsx
new file mode 100644
index 000000000000..fb44a8716ebc
--- /dev/null
+++ b/packages/website/docs/_samples/main/Table/RowClick/sample.tsx
@@ -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 (
+ <>
+
+
+ {/* playground-fold */}
+
+ Product
+ Supplier
+ Price
+
+ {/* playground-fold-end */}
+ showToast("Row A clicked!")}>
+
+
+
+
+
+
+
+
+
+
+ showToast("Row B clicked!")}>
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
+
+export default App;