Skip to content
Merged
351 changes: 351 additions & 0 deletions packages/main/cypress/specs/List.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<List selectionMode="Multiple">
<ListItemStandard id="item1" type="InactiveSelectable">Option A</ListItemStandard>
<ListItemStandard id="item2" type="InactiveSelectable">Option B</ListItemStandard>
</List>
);

// 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(
<List selectionMode="Multiple">
<ListItemStandard id="item1" type="InactiveSelectable">Option A</ListItemStandard>
</List>
);

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(
<List selectionMode="Multiple">
<ListItemStandard id="item1" type="InactiveSelectable">Option A</ListItemStandard>
</List>
);

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(
<List selectionMode="Multiple">
<ListItemStandard id="item1" type="InactiveSelectable">Option A</ListItemStandard>
</List>
);

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(
<List selectionMode="Multiple">
<ListItemStandard id="item1" type="Inactive">Option A</ListItemStandard>
</List>
);

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(
<List selectionMode="Single">
<ListItemStandard id="item1" type="Inactive">Option A</ListItemStandard>
</List>
);

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(
<List selectionMode="Multiple">
<ListItemStandard id="item1" type="Inactive">Option A</ListItemStandard>
</List>
);

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(
<List selectionMode="Single">
<ListItemStandard id="item1" type="InactiveSelectable">Option A</ListItemStandard>
<ListItemStandard id="item2" type="InactiveSelectable">Option B</ListItemStandard>
</List>
);

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(
<List selectionMode="Single">
<ListItemStandard id="item1" type="InactiveSelectable">Option A</ListItemStandard>
</List>
);

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(
<List selectionMode="None">
<ListItemStandard id="item1" type="InactiveSelectable">Option A</ListItemStandard>
</List>
);

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(
<List selectionMode="Delete">
<ListItemStandard id="item1" type="InactiveSelectable">Option A</ListItemStandard>
</List>
);

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(
<List selectionMode="Multiple">
<ListItemStandard id="item1" type="InactiveSelectable">Option A</ListItemStandard>
</List>
);

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(
<List selectionMode="SingleStart">
<ListItemStandard id="item1" type="InactiveSelectable">Option A</ListItemStandard>
<ListItemStandard id="item2" type="InactiveSelectable">Option B</ListItemStandard>
</List>
);

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(
<List selectionMode="SingleEnd">
<ListItemStandard id="item1" type="InactiveSelectable">Option A</ListItemStandard>
<ListItemStandard id="item2" type="InactiveSelectable">Option B</ListItemStandard>
</List>
);

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(
<List selectionMode="Multiple">
<ListItemStandard id="item1" type="InactiveSelectable">Option A</ListItemStandard>
</List>
);

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(
<List selectionMode="Single">
<ListItemStandard id="item1" type="InactiveSelectable">Option A</ListItemStandard>
</List>
);

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(
<List selectionMode="Multiple">
<ListItemStandard id="item1" type="InactiveSelectable">Option A</ListItemStandard>
</List>
);

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(
<List selectionMode="Multiple">
<ListItemStandard id="item1" type="InactiveSelectable">Option A</ListItemStandard>
</List>
);

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(
<List selectionMode="SingleStart">
<ListItemStandard id="item1" type="InactiveSelectable">Option A</ListItemStandard>
</List>
);

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(
<List selectionMode="SingleAuto">
<ListItemStandard id="item1" type="InactiveSelectable">Option A</ListItemStandard>
</List>
);

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");
});
});
17 changes: 15 additions & 2 deletions packages/main/src/ListItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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();
Expand All @@ -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;
Expand Down
Loading
Loading