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
79 changes: 78 additions & 1 deletion packages/main/cypress/specs/SegmentedButton.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -566,4 +566,81 @@ describe("SegmentedButtonItem Accessibility", () => {
.trigger("mouseover")
.should("have.attr", "title", TOOLTIP_TEXT);
});
});
});

describe("SegmentedButtonItem: selection-change event", () => {
it("should fire selection-change event when item is clicked", () => {
const selectionChangeSpy = cy.spy().as("selectionChangeSpy");

cy.mount(
<SegmentedButton>
<SegmentedButtonItem id="item1">First</SegmentedButtonItem>
<SegmentedButtonItem id="item2">Second</SegmentedButtonItem>
</SegmentedButton>
);

cy.get("#item2")
.then($el => {
$el[0].addEventListener("selection-change", selectionChangeSpy);
});

cy.get("#item2")
.ui5SegmentedButtonItemToggleSelect();

cy.get("@selectionChangeSpy").should("have.been.calledOnce");
});

it("should prevent selection when preventDefault is called", () => {
cy.mount(
<SegmentedButton>
<SegmentedButtonItem id="item1">First</SegmentedButtonItem>
<SegmentedButtonItem id="item2">Second</SegmentedButtonItem>
</SegmentedButton>
);

cy.get("#item2")
.then($el => {
$el[0].addEventListener("selection-change", (e: Event) => {
e.preventDefault();
});
});

// Item 1 should be selected initially
cy.get("#item1").should("have.attr", "selected");
cy.get("#item2").should("not.have.attr", "selected");

// Click item 2
cy.get("#item2").realClick();

// Item 2 should NOT be selected because we called preventDefault
cy.get("#item1").should("have.attr", "selected");
cy.get("#item2").should("not.have.attr", "selected");
});

it("should not fire selection-change event when disabled item is clicked", () => {
const selectionChangeSpy = cy.spy().as("selectionChangeSpy");

cy.mount(
<SegmentedButton>
<SegmentedButtonItem id="item1">First</SegmentedButtonItem>
<SegmentedButtonItem id="item2" disabled>Second</SegmentedButtonItem>
</SegmentedButton>
);

cy.get("#item2")
.then($el => {
$el[0].addEventListener("selection-change", selectionChangeSpy);
});

// Click the disabled item directly
cy.get("#item2")
.shadow()
.find("li")
.click({ force: true });

cy.get("@selectionChangeSpy").should("not.have.been.called");
cy.get("#item2").should("not.have.attr", "selected");
});

});

39 changes: 39 additions & 0 deletions packages/main/src/SegmentedButtonItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ import type { ISegmentedButtonItem } from "./SegmentedButton.js";
import SegmentedButtonItemTemplate from "./SegmentedButtonItemTemplate.js";

import type { IButton } from "./Button.js";
import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js";
import segmentedButtonItemCss from "./generated/themes/SegmentedButtonItem.css.js";

type SegmentedButtonItemClickEventDetail = {
originalEvent: Event;
item: SegmentedButtonItem;
};

/**
* @class
*
Expand Down Expand Up @@ -48,7 +55,26 @@ import segmentedButtonItemCss from "./generated/themes/SegmentedButtonItem.css.j
template: SegmentedButtonItemTemplate,
styles: segmentedButtonItemCss,
})

/**
* Fired when the component is activated either with a mouse/tap or by using the Enter or Space key.
*
* **Note:** The event will not be fired if the `disabled` property is set to `true`.
*
* @since 2.22.0
* @public
* @param {MouseEvent} originalEvent The original mouse event that triggered the selection
* @param {SegmentedButtonItem} item The segmented button item that was clicked
*/
@event("selection-change", {
bubbles: true,
cancelable: true,
})

class SegmentedButtonItem extends UI5Element implements IButton, ISegmentedButtonItem {
eventDetails!: {
"selection-change": SegmentedButtonItemClickEventDetail,
}
/**
* Defines whether the component is disabled.
* A disabled component can't be selected or
Expand Down Expand Up @@ -196,6 +222,18 @@ class SegmentedButtonItem extends UI5Element implements IButton, ISegmentedButto
return;
}

const prevented = !this.fireDecoratorEvent("selection-change", {
originalEvent: e,
item: this,
});

if (prevented) {
// Stop the native MouseEvent from reaching the parent
e.stopPropagation();
e.preventDefault();
return;
}

this.selected = !this.selected;
}

Expand Down Expand Up @@ -253,3 +291,4 @@ class SegmentedButtonItem extends UI5Element implements IButton, ISegmentedButto
SegmentedButtonItem.define();

export default SegmentedButtonItem;
export type { SegmentedButtonItemClickEventDetail };
49 changes: 49 additions & 0 deletions packages/main/test/pages/SegmentedButton.html
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,28 @@ <h3>Accessibility</h3>
<span id="accRefText">accessible ref text</span>
</section>

<section>
<h3>selection-change Event Demo</h3>
<p>Normal behavior - selection works:</p>
<ui5-segmented-button id="eventTestSB1">
<ui5-segmented-button-item id="normalItem1" selected>First</ui5-segmented-button-item>
<ui5-segmented-button-item id="normalItem2">Second</ui5-segmented-button-item>
<ui5-segmented-button-item id="normalItem3">Third</ui5-segmented-button-item>
</ui5-segmented-button>
<br><br>
<ui5-input id="normalEventLog" readonly value="No event yet"></ui5-input>
<br><br>

<p>With preventDefault - "Second" item blocks selection:</p>
<ui5-segmented-button id="eventTestSB2">
<ui5-segmented-button-item id="blockedItem1" selected>First</ui5-segmented-button-item>
<ui5-segmented-button-item id="blockedItem2">Second (Blocked)</ui5-segmented-button-item>
<ui5-segmented-button-item id="blockedItem3">Third</ui5-segmented-button-item>
</ui5-segmented-button>
<br><br>
<ui5-input id="blockedEventLog" readonly value="No event yet"></ui5-input>
</section>

<script>

progSetButton1.addEventListener("click", function() {
Expand All @@ -265,6 +287,33 @@ <h3>Accessibility</h3>
segButtonProg.items[0].selected = true;
});

// Normal behavior - events fire, selection works
normalItem1.addEventListener("selection-change", function(e) {
normalEventLog.value = "Item 1 clicked - selection allowed";
});

normalItem2.addEventListener("selection-change", function(e) {
normalEventLog.value = "Item 2 clicked - selection allowed";
});

normalItem3.addEventListener("selection-change", function(e) {
normalEventLog.value = "Item 3 clicked - selection allowed";
});

// Blocked behavior - "Second" item prevents selection
blockedItem1.addEventListener("selection-change", function(e) {
blockedEventLog.value = "Item 1 clicked - selection allowed";
});

blockedItem2.addEventListener("selection-change", function(e) {
e.preventDefault(); // Block selection for this item
blockedEventLog.value = "Item 2 clicked - SELECTION BLOCKED!";
});

blockedItem3.addEventListener("selection-change", function(e) {
blockedEventLog.value = "Item 3 clicked - selection allowed";
});

</script>

</body>
Expand Down
Loading