diff --git a/packages/main/cypress/specs/List.cy.tsx b/packages/main/cypress/specs/List.cy.tsx
index 42a3372fbc8d6..3b58c5dfd7e14 100644
--- a/packages/main/cypress/specs/List.cy.tsx
+++ b/packages/main/cypress/specs/List.cy.tsx
@@ -3874,4 +3874,355 @@ describe("List - ListItem accessible role inheritance", () => {
.find("li")
.should("have.attr", "role", "listitem");
});
+});
+
+describe("List - InactiveSelectable type", () => {
+ it("type='InactiveSelectable' + selectionMode='Multiple' — clicking item toggles checkbox selection", () => {
+ cy.mount(
+
+ Option A
+ Option B
+
+ );
+
+ // Initially not selected
+ cy.get("#item1").should("not.have.attr", "selected");
+
+ // Click item — should become selected (checkbox toggled)
+ cy.get("#item1").click();
+ cy.get("#item1").should("have.attr", "selected");
+
+ // Click again — should become deselected
+ cy.get("#item1").click();
+ cy.get("#item1").should("not.have.attr", "selected");
+
+ // Second item is independent
+ cy.get("#item2").click();
+ cy.get("#item2").should("have.attr", "selected");
+ cy.get("#item1").should("not.have.attr", "selected");
+ });
+
+ it("type='InactiveSelectable' + selectionMode='Multiple' — pressing Space toggles selection", () => {
+ cy.mount(
+
+ Option A
+
+ );
+
+ cy.get("#item1").should("not.have.attr", "selected");
+
+ // Focus item and press Space
+ cy.get("#item1").shadow().find("li").focus();
+ cy.realPress("Space");
+ cy.get("#item1").should("have.attr", "selected");
+
+ // Press Space again — should deselect
+ cy.realPress("Space");
+ cy.get("#item1").should("not.have.attr", "selected");
+ });
+
+ it("type='InactiveSelectable' — item-click event is NOT fired on click", () => {
+ cy.mount(
+
+ Option A
+
+ );
+
+ cy.get("[ui5-list]").then(($list) => {
+ $list[0].addEventListener("ui5-item-click", cy.stub().as("itemClickStub"));
+ });
+
+ cy.get("#item1").click();
+
+ cy.get("@itemClickStub").should("not.have.been.called");
+ });
+
+ it("type='InactiveSelectable' — item-click event is NOT fired on Space key", () => {
+ cy.mount(
+
+ Option A
+
+ );
+
+ cy.get("[ui5-list]").then(($list) => {
+ $list[0].addEventListener("ui5-item-click", cy.stub().as("itemClickStub"));
+ });
+
+ cy.get("#item1").shadow().find("li").focus();
+ cy.realPress("Space");
+
+ cy.get("@itemClickStub").should("not.have.been.called");
+ });
+
+ it("type='Inactive' — clicking item does NOT toggle selection (regression guard)", () => {
+ cy.mount(
+
+ Option A
+
+ );
+
+ cy.get("#item1").should("not.have.attr", "selected");
+
+ cy.get("#item1").click();
+
+ cy.get("#item1").should("not.have.attr", "selected");
+ });
+
+ it("type='Inactive' + selectionMode='Single' — clicking item does NOT select it (regression guard)", () => {
+ cy.mount(
+
+ Option A
+
+ );
+
+ cy.get("#item1").should("not.have.attr", "selected");
+
+ cy.get("#item1").click();
+
+ cy.get("#item1").should("not.have.attr", "selected");
+ });
+
+ it("type='Inactive' — Space and Enter do NOT toggle selection (regression guard)", () => {
+ cy.mount(
+
+ Option A
+
+ );
+
+ cy.get("[ui5-list]").then(($list) => {
+ $list[0].addEventListener("ui5-item-click", cy.stub().as("itemClickStub"));
+ $list[0].addEventListener("ui5-selection-change", cy.stub().as("selectionChangeStub"));
+ });
+
+ cy.get("#item1").shadow().find("li").focus();
+ cy.realPress("Space");
+ cy.get("#item1").should("not.have.attr", "selected");
+
+ cy.realPress("Enter");
+ cy.get("#item1").should("not.have.attr", "selected");
+
+ cy.get("@itemClickStub").should("not.have.been.called");
+ cy.get("@selectionChangeStub").should("not.have.been.called");
+ });
+
+ it("type='InactiveSelectable' + selectionMode='Single' — clicking item selects it", () => {
+ cy.mount(
+
+ Option A
+ Option B
+
+ );
+
+ cy.get("#item1").should("not.have.attr", "selected");
+
+ cy.get("#item1").click();
+ cy.get("#item1").should("have.attr", "selected");
+
+ // Clicking second item moves selection
+ cy.get("#item2").click();
+ cy.get("#item2").should("have.attr", "selected");
+ cy.get("#item1").should("not.have.attr", "selected");
+ });
+
+ it("type='InactiveSelectable' + selectionMode='Single' — pressing Space selects item", () => {
+ cy.mount(
+
+ Option A
+
+ );
+
+ cy.get("#item1").should("not.have.attr", "selected");
+
+ cy.get("#item1").shadow().find("li").focus();
+ cy.realPress("Space");
+
+ cy.get("#item1").should("have.attr", "selected");
+ });
+
+ it("type='InactiveSelectable' + selectionMode='None' — clicking item does nothing", () => {
+ cy.mount(
+
+ Option A
+
+ );
+
+ cy.get("[ui5-list]").then(($list) => {
+ $list[0].addEventListener("ui5-item-click", cy.stub().as("itemClickStub"));
+ $list[0].addEventListener("ui5-selection-change", cy.stub().as("selectionChangeStub"));
+ });
+
+ cy.get("#item1").click();
+
+ cy.get("#item1").should("not.have.attr", "selected");
+ cy.get("@itemClickStub").should("not.have.been.called");
+ cy.get("@selectionChangeStub").should("not.have.been.called");
+ });
+
+ it("type='InactiveSelectable' + selectionMode='Delete' — clicking item does nothing", () => {
+ cy.mount(
+
+ Option A
+
+ );
+
+ cy.get("[ui5-list]").then(($list) => {
+ $list[0].addEventListener("ui5-item-click", cy.stub().as("itemClickStub"));
+ $list[0].addEventListener("ui5-selection-change", cy.stub().as("selectionChangeStub"));
+ });
+
+ cy.get("#item1").click();
+
+ cy.get("#item1").should("not.have.attr", "selected");
+ cy.get("@itemClickStub").should("not.have.been.called");
+ cy.get("@selectionChangeStub").should("not.have.been.called");
+ });
+
+ it("type='InactiveSelectable' + selectionMode='Multiple' — selection-change event is fired", () => {
+ cy.mount(
+
+ Option A
+
+ );
+
+ cy.get("[ui5-list]").then(($list) => {
+ $list[0].addEventListener("ui5-selection-change", cy.stub().as("selectionChangeStub"));
+ });
+
+ cy.get("#item1").click();
+
+ cy.get("@selectionChangeStub").should("have.been.calledOnce");
+ });
+
+ it("type='InactiveSelectable' + selectionMode='SingleStart' — clicking item selects it", () => {
+ cy.mount(
+
+ Option A
+ Option B
+
+ );
+
+ cy.get("#item1").click();
+ cy.get("#item1").should("have.attr", "selected");
+
+ cy.get("#item2").click();
+ cy.get("#item2").should("have.attr", "selected");
+ cy.get("#item1").should("not.have.attr", "selected");
+ });
+
+ it("type='InactiveSelectable' + selectionMode='SingleEnd' — clicking item selects it", () => {
+ cy.mount(
+
+ Option A
+ Option B
+
+ );
+
+ cy.get("#item1").click();
+ cy.get("#item1").should("have.attr", "selected");
+
+ cy.get("#item2").click();
+ cy.get("#item2").should("have.attr", "selected");
+ cy.get("#item1").should("not.have.attr", "selected");
+ });
+
+ it("type='InactiveSelectable' + selectionMode='Multiple' — pressing Enter toggles selection", () => {
+ cy.mount(
+
+ Option A
+
+ );
+
+ cy.get("#item1").should("not.have.attr", "selected");
+
+ cy.get("#item1").shadow().find("li").focus();
+ cy.realPress("Enter");
+ cy.get("#item1").should("have.attr", "selected");
+
+ cy.realPress("Enter");
+ cy.get("#item1").should("not.have.attr", "selected");
+ });
+
+ it("type='InactiveSelectable' + selectionMode='Single' — pressing Enter selects item", () => {
+ cy.mount(
+
+ Option A
+
+ );
+
+ cy.get("#item1").should("not.have.attr", "selected");
+
+ cy.get("#item1").shadow().find("li").focus();
+ cy.realPress("Enter");
+
+ cy.get("#item1").should("have.attr", "selected");
+ });
+
+ it("type='InactiveSelectable' — item-click event is NOT fired on Enter key", () => {
+ cy.mount(
+
+ Option A
+
+ );
+
+ cy.get("[ui5-list]").then(($list) => {
+ $list[0].addEventListener("ui5-item-click", cy.stub().as("itemClickStub"));
+ });
+
+ cy.get("#item1").shadow().find("li").focus();
+ cy.realPress("Enter");
+
+ cy.get("@itemClickStub").should("not.have.been.called");
+ });
+
+ it("type='InactiveSelectable' + selectionMode='Multiple' — clicking checkbox directly toggles selection", () => {
+ cy.mount(
+
+ Option A
+
+ );
+
+ cy.get("[ui5-list]").then(($list) => {
+ $list[0].addEventListener("ui5-selection-change", cy.stub().as("selectionChangeStub"));
+ });
+
+ cy.get("#item1").shadow().find("ui5-checkbox").click();
+
+ cy.get("#item1").should("have.attr", "selected");
+ cy.get("@selectionChangeStub").should("have.been.calledOnce");
+ });
+
+ it("type='InactiveSelectable' + selectionMode='SingleStart' — clicking radio directly selects item", () => {
+ cy.mount(
+
+ Option A
+
+ );
+
+ cy.get("[ui5-list]").then(($list) => {
+ $list[0].addEventListener("ui5-selection-change", cy.stub().as("selectionChangeStub"));
+ });
+
+ cy.get("#item1").shadow().find("ui5-radio-button").click();
+
+ cy.get("#item1").should("have.attr", "selected");
+ cy.get("@selectionChangeStub").should("have.been.calledOnce");
+ });
+
+ it("type='InactiveSelectable' + selectionMode='SingleAuto' — clicking item does nothing", () => {
+ cy.mount(
+
+ Option A
+
+ );
+
+ cy.get("[ui5-list]").then(($list) => {
+ $list[0].addEventListener("ui5-item-click", cy.stub().as("itemClickStub"));
+ $list[0].addEventListener("ui5-selection-change", cy.stub().as("selectionChangeStub"));
+ });
+
+ cy.get("#item1").click();
+
+ cy.get("@itemClickStub").should("not.have.been.called");
+ cy.get("@selectionChangeStub").should("have.been.calledOnce");
+ });
});
\ No newline at end of file
diff --git a/packages/main/src/ListItem.ts b/packages/main/src/ListItem.ts
index 668619999151e..7004bba656e0d 100644
--- a/packages/main/src/ListItem.ts
+++ b/packages/main/src/ListItem.ts
@@ -107,10 +107,13 @@ abstract class ListItem extends ListItemBase {
}
/**
* Defines the visual indication and behavior of the list items.
- * Available options are `Active` (by default), `Inactive`, `Detail` and `Navigation`.
+ * Available options are `Active` (by default), `Inactive`, `InactiveSelectable`, `Detail` and `Navigation`.
*
* **Note:** When set to `Active` or `Navigation`, the item will provide visual response upon press and hover,
- * while with type `Inactive` and `Detail` - will not.
+ * while with type `Inactive`, `InactiveSelectable` and `Detail` - will not.
+ *
+ * **Note:** `InactiveSelectable` behaves like `Inactive` but allows selection (checkbox/radio) to be toggled
+ * when the list has a selection mode. The `item-click` event is not fired for this type.
* @default "Active"
* @public
*/
@@ -401,6 +404,12 @@ abstract class ListItem extends ListItemBase {
if (this.isInactive) {
return;
}
+ if (this.isInactiveSelectable) {
+ if (this.modeSingleSelect || this.modeMultiple) {
+ this.fireDecoratorEvent("selection-requested", { item: this, selected: !this.selected, selectionComponentPressed: false });
+ }
+ return;
+ }
super.fireItemPress(e);
if (document.activeElement !== this) {
this.focus();
@@ -411,6 +420,10 @@ abstract class ListItem extends ListItemBase {
return this.type === ListItemType.Inactive || this.type === ListItemType.Detail;
}
+ get isInactiveSelectable() {
+ return this.type === ListItemType.InactiveSelectable;
+ }
+
get placeSelectionElementBefore() {
return this._selectionMode === ListSelectionMode.Multiple
|| this._selectionMode === ListSelectionMode.SingleStart;
diff --git a/packages/main/src/types/ListItemType.ts b/packages/main/src/types/ListItemType.ts
index 1baae23ff25b3..a63fa608bf345 100644
--- a/packages/main/src/types/ListItemType.ts
+++ b/packages/main/src/types/ListItemType.ts
@@ -9,6 +9,15 @@ enum ListItemType {
*/
Inactive = "Inactive",
+ /**
+ * Indicates the list item does not have any active feedback when item is pressed,
+ * but selection (checkbox/radio) is still possible when a selection mode is active.
+ * The `item-click` event is not fired for items of this type.
+ * @public
+ * @since 2.26.0
+ */
+ InactiveSelectable = "InactiveSelectable",
+
/**
* Indicates that the item is clickable via active feedback when item is pressed.
* @public