From b49802632e31ec5ef1082fc34541ba2e10948051 Mon Sep 17 00:00:00 2001 From: Georgi Damyanov Date: Thu, 16 Apr 2026 11:58:47 +0300 Subject: [PATCH 1/2] feat: add semantic selection change event on item --- .../main/cypress/specs/SegmentedButton.cy.tsx | 79 ++++++++++++++++++- packages/main/src/SegmentedButtonItem.ts | 38 +++++++++ packages/main/test/pages/SegmentedButton.html | 49 ++++++++++++ 3 files changed, 165 insertions(+), 1 deletion(-) diff --git a/packages/main/cypress/specs/SegmentedButton.cy.tsx b/packages/main/cypress/specs/SegmentedButton.cy.tsx index e3623bde5457..f34dc0fefacf 100644 --- a/packages/main/cypress/specs/SegmentedButton.cy.tsx +++ b/packages/main/cypress/specs/SegmentedButton.cy.tsx @@ -566,4 +566,81 @@ describe("SegmentedButtonItem Accessibility", () => { .trigger("mouseover") .should("have.attr", "title", TOOLTIP_TEXT); }); -}); \ No newline at end of file +}); + +describe("SegmentedButtonItem: selection-change event", () => { + it("should fire selection-change event when item is clicked", () => { + const selectionChangeSpy = cy.spy().as("selectionChangeSpy"); + + cy.mount( + + First + Second + + ); + + 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( + + First + Second + + ); + + 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( + + First + Second + + ); + + 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"); + }); + +}); + \ No newline at end of file diff --git a/packages/main/src/SegmentedButtonItem.ts b/packages/main/src/SegmentedButtonItem.ts index 1bcb0e005c1a..e6173bc02759 100644 --- a/packages/main/src/SegmentedButtonItem.ts +++ b/packages/main/src/SegmentedButtonItem.ts @@ -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 * @@ -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 @@ -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; } diff --git a/packages/main/test/pages/SegmentedButton.html b/packages/main/test/pages/SegmentedButton.html index c271de06f12a..60a228c80b34 100644 --- a/packages/main/test/pages/SegmentedButton.html +++ b/packages/main/test/pages/SegmentedButton.html @@ -245,6 +245,28 @@

Accessibility

accessible ref text +
+

selection-change Event Demo

+

Normal behavior - selection works:

+ + First + Second + Third + +

+ +

+ +

With preventDefault - "Second" item blocks selection:

+ + First + Second (Blocked) + Third + +

+ +
+ From 9a92e68fa89bcbca13db1f98b690a854e17f9757 Mon Sep 17 00:00:00 2001 From: Georgi Damyanov Date: Thu, 16 Apr 2026 14:21:25 +0300 Subject: [PATCH 2/2] fix: export type --- packages/main/src/SegmentedButtonItem.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/main/src/SegmentedButtonItem.ts b/packages/main/src/SegmentedButtonItem.ts index e6173bc02759..106e431f54ea 100644 --- a/packages/main/src/SegmentedButtonItem.ts +++ b/packages/main/src/SegmentedButtonItem.ts @@ -291,3 +291,4 @@ class SegmentedButtonItem extends UI5Element implements IButton, ISegmentedButto SegmentedButtonItem.define(); export default SegmentedButtonItem; +export type { SegmentedButtonItemClickEventDetail };