From 4b9e525cc77b3ee76961d1ccd6cd67a3342e89c2 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Tue, 12 Aug 2025 16:12:50 +0300 Subject: [PATCH 01/43] chore(ui5-toolbar): toolbar item wrapper introduced --- packages/main/src/ToolbarItem.ts | 25 ++++++++++++++++++++++- packages/main/src/ToolbarItemTemplate.tsx | 5 +++++ packages/main/test/pages/Toolbar.html | 7 ++++++- 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 packages/main/src/ToolbarItemTemplate.tsx diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 44f55f1f6c37..5da94afeab22 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -1,6 +1,10 @@ import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js"; import property from "@ui5/webcomponents-base/dist/decorators/property.js"; import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js"; +import slot from "@ui5/webcomponents-base/dist/decorators/slot.js"; +import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js"; +import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; +import ToolbarItemTemplate from "./ToolbarItemTemplate.js"; import type ToolbarItemOverflowBehavior from "./types/ToolbarItemOverflowBehavior.js"; @@ -16,13 +20,19 @@ type ToolbarItemEventDetail = { bubbles: true, }) +@customElement({ + tag: "ui5-toolbar-item", + languageAware: true, + renderer: jsxRenderer, + template: ToolbarItemTemplate, +}) + /** * @class * * Represents an abstract class for items, used in the `ui5-toolbar`. * @constructor * @extends UI5Element - * @abstract * @public * @since 1.17.0 */ @@ -60,6 +70,17 @@ class ToolbarItem extends UI5Element { @property({ type: Boolean }) isOverflowed: boolean = false; + /** + * Defines if the toolbar item is overflowed. + * @default false + * @since 2.11.0 + */ + + @slot({ + "default": true, type: HTMLElement, invalidateOnChildChange: true, + }) + item!: HTMLElement | undefined; + /** * Defines if the width of the item should be ignored in calculating the whole width of the toolbar * @protected @@ -113,4 +134,6 @@ export type { IEventOptions, ToolbarItemEventDetail, }; +ToolbarItem.define(); + export default ToolbarItem; diff --git a/packages/main/src/ToolbarItemTemplate.tsx b/packages/main/src/ToolbarItemTemplate.tsx new file mode 100644 index 000000000000..d45096879aca --- /dev/null +++ b/packages/main/src/ToolbarItemTemplate.tsx @@ -0,0 +1,5 @@ +export default function ToolbarItemTemplate() { + return ( + + ); +} diff --git a/packages/main/test/pages/Toolbar.html b/packages/main/test/pages/Toolbar.html index de6b72c48bcf..26665d25a51b 100644 --- a/packages/main/test/pages/Toolbar.html +++ b/packages/main/test/pages/Toolbar.html @@ -77,7 +77,9 @@ - + + Call me later +

@@ -115,6 +117,9 @@ + + User Menu +
From 7c49aa42e9ccfce74741c916654d8d874d76d94e Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Tue, 9 Sep 2025 17:31:01 +0300 Subject: [PATCH 02/43] chore(ui5-toolbar-item): wrappers introduced --- packages/main/cypress/specs/Toolbar.cy.tsx | 148 +++++++++++++++++---- packages/main/src/ToolbarItem.ts | 23 ++++ packages/main/src/ToolbarItemTemplate.tsx | 8 +- packages/main/test/pages/Toolbar.html | 8 +- 4 files changed, 157 insertions(+), 30 deletions(-) diff --git a/packages/main/cypress/specs/Toolbar.cy.tsx b/packages/main/cypress/specs/Toolbar.cy.tsx index 45da94b0a372..81cf00445137 100644 --- a/packages/main/cypress/specs/Toolbar.cy.tsx +++ b/packages/main/cypress/specs/Toolbar.cy.tsx @@ -4,7 +4,7 @@ import ToolbarSelect from "../../src/ToolbarSelect.js"; import ToolbarSelectOption from "../../src/ToolbarSelectOption.js"; import ToolbarSeparator from "../../src/ToolbarSeparator.js"; import ToolbarSpacer from "../../src/ToolbarSpacer.js"; -import type ToolbarItem from "../../src/ToolbarItem.js"; +import ToolbarItem from "../../src/ToolbarItem.js"; import add from "@ui5/webcomponents-icons/dist/add.js"; import decline from "@ui5/webcomponents-icons/dist/decline.js"; import employee from "@ui5/webcomponents-icons/dist/employee.js"; @@ -325,20 +325,20 @@ describe("Toolbar general interaction", () => { it("Should render ui5-button by toolbar template, when slotting ui5-toolbar-button elements", () => { cy.mount( - - ); - + cy.get("[ui5-toolbar]") .find("[ui5-toolbar-button]") .first() @@ -353,51 +353,51 @@ describe("Toolbar general interaction", () => { .should("be.visible") .should("have.length", 2); }); - + it("Should call child events only once", () => { cy.mount( <> - - ); - + // Create stubs for event tracking cy.get("[data-testid='clickCountToolbar']") .as("toolbar") .then($toolbar => { $toolbar.get(0).addEventListener("click", cy.stub().as("toolbarClickStub")); }); - + cy.get("[data-testid='clickCounter']") .as("clickCounter") .then($button => { $button.get(0).addEventListener("click", cy.stub().as("counterClickStub")); }); - + cy.get("[data-testid='clearCounter']") .as("clearCounter") .then($button => { $button.get(0).addEventListener("click", cy.stub().as("clearClickStub")); }); - + // Set up input manipulation logic cy.get("@toolbar").then($toolbar => { $toolbar.get(0).addEventListener("click", (e) => { const input = document.querySelector("[data-testid='input']") as HTMLInputElement; const target = e.target as HTMLElement; - + if (target.dataset.testid === "clearCounter") { input.value = "0"; } else if (target.dataset.testid === "clickCounter") { @@ -406,16 +406,16 @@ describe("Toolbar general interaction", () => { } }); }); - + cy.get("[data-testid='input']").invoke("val", "0"); - + cy.get("@clickCounter").realClick(); - + cy.get("[data-testid='input']").should("have.prop", "value", "1"); - + cy.get("@toolbarClickStub").should("have.been.calledOnce"); cy.get("@counterClickStub").should("have.been.calledOnce"); - + cy.get("[data-testid='input']").invoke("val", "0"); }); }); @@ -620,3 +620,99 @@ describe("Toolbar Button", () => { cy.get("#value-input").should("have.value", "1"); }); }); + +describe("Toolbar Item", () => { + it("Should render ui5-toolbar-item with correct properties and not suppress events", () => { + // Mount the Toolbar with a ui5-toolbar-item wrapping a web component + cy.mount( + + + + + + ); + + // Verify the ui5-toolbar-item has the correct properties + cy.get("ui5-toolbar-item").should((item) => { + expect(item).to.have.attr("prevent-overflow-closing"); + expect(item).to.have.attr("overflow-priority", "AlwaysOverflow"); + }); + + // Verify the inner component (ui5-button) is rendered + cy.get("ui5-toolbar-item") + .find("ui5-button").should((button) => { + expect(button).to.exist; + expect(button).to.contain.text("User Menu"); + }); + + // Attach a click event to the inner button + cy.get("ui5-button#innerButton") + .then(button => { + button.get(0).addEventListener("click", cy.stub().as("buttonClicked")); + }); + + // Trigger a click event on the inner button + cy.get("ui5-button#innerButton").click(); + + // Verify the click event was triggered + cy.get("@buttonClicked").should("have.been.calledOnce"); + }); + + it("Should respect prevent-overflow-closing property", () => { + // Mount the Toolbar with a ui5-toolbar-item + cy.mount( + + + + + + ); + + // Simulate overflow behavior + cy.get("ui5-toolbar-item") + .invoke("attr", "prevent-overflow-closing") + .should("exist"); + + // Verify the inner button is still visible and not hidden + cy.get("ui5-toolbar-item") + .find("ui5-button") + .should("be.visible"); + + cy.get("ui5-toolbar-item") + .find("ui5-button") + .realClick(); + expect(cy.get("ui5-popover").should("be.visible")); + }); + + it("Should respect overflow-priority property", () => { + // Mount the Toolbar with multiple ui5-toolbar-items + cy.mount( + + + + + + + + + ); + + // Verify the overflow-priority property is respected + cy.get("ui5-toolbar-item[overflow-priority='AlwaysOverflow']") + .should("exist") + .should("have.attr", "overflow-priority", "AlwaysOverflow"); + + cy.get("ui5-toolbar-item[overflow-priority='NeverOverflow']") + .should("exist") + .should("have.attr", "overflow-priority", "NeverOverflow"); + + // Simulate overflow behavior and ensure high-priority item remains visible + cy.viewport(300, 1080); // Simulate a smaller viewport + cy.get("ui5-toolbar-item[overflow-priority='NeverOverflow']") + .should("be.visible"); + + // Ensure low-priority item is hidden or moved to overflow + cy.get("ui5-toolbar-item[overflow-priority='AlwaysOverflow']") + .should("not.be.visible"); + }); +}) diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 5da94afeab22..6d8a0fa6f051 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -20,6 +20,10 @@ type ToolbarItemEventDetail = { bubbles: true, }) +@event("click", { + bubbles: true, +}) + @customElement({ tag: "ui5-toolbar-item", languageAware: true, @@ -128,6 +132,25 @@ class ToolbarItem extends UI5Element { }, }; } + + _onClick = this.onClick.bind(this); + + constructor() { + super(); + } + + /** + * Handles the click event on the toolbar item. + * It stops the event propagation and fires a "click" event with the target reference. + * If `preventOverflowClosing` is false, it will also fire a "close-overflow" event. + * @param {Event} e - The click event + */ + onClick(e: Event): void { + e.stopImmediatePropagation(); + if (!this.preventOverflowClosing) { + this.fireDecoratorEvent("close-overflow"); + } + } } export type { diff --git a/packages/main/src/ToolbarItemTemplate.tsx b/packages/main/src/ToolbarItemTemplate.tsx index d45096879aca..32898b7ea647 100644 --- a/packages/main/src/ToolbarItemTemplate.tsx +++ b/packages/main/src/ToolbarItemTemplate.tsx @@ -1,5 +1,9 @@ -export default function ToolbarItemTemplate() { +import type ToolbarItem from "./ToolbarItem.js"; + +export default function ToolbarItemTemplate(this: ToolbarItem) { return ( - +
+ +
); } diff --git a/packages/main/test/pages/Toolbar.html b/packages/main/test/pages/Toolbar.html index 26665d25a51b..bcd053bda1e2 100644 --- a/packages/main/test/pages/Toolbar.html +++ b/packages/main/test/pages/Toolbar.html @@ -117,8 +117,8 @@ - - User Menu + + User Menu
@@ -403,6 +403,10 @@ menu2.open = !menu2.open; menu2.opener = btnOpenMenu2; }); + + tralala.addEventListener("ui5-click", function(event) { + alert("clicked"); + }); From 6169477425dc18994c1c0257ee721dbf7def634e Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Wed, 10 Sep 2025 14:40:50 +0300 Subject: [PATCH 03/43] chore(ui5-toolbar-item): toolbar wrappers --- packages/main/cypress/specs/Toolbar.cy.tsx | 69 +++++++++++++++------- 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/packages/main/cypress/specs/Toolbar.cy.tsx b/packages/main/cypress/specs/Toolbar.cy.tsx index 7d9ce5382a43..aef436a9a83a 100644 --- a/packages/main/cypress/specs/Toolbar.cy.tsx +++ b/packages/main/cypress/specs/Toolbar.cy.tsx @@ -611,30 +611,57 @@ describe("Toolbar Item", () => { }); it("Should respect prevent-overflow-closing property", () => { - // Mount the Toolbar with a ui5-toolbar-item - cy.mount( - - - - - - ); + // Mount the Toolbar with constrained width to force overflow + cy.mount( +
+ + + + + + + + +
+ ); - // Simulate overflow behavior - cy.get("ui5-toolbar-item") - .invoke("attr", "prevent-overflow-closing") - .should("exist"); + // Wait for overflow processing + cy.wait(500); - // Verify the inner button is still visible and not hidden - cy.get("ui5-toolbar-item") - .find("ui5-button") - .should("be.visible"); + // Click the overflow button to open the popover + cy.get("ui5-toolbar") + .shadow() + .find(".ui5-tb-overflow-btn") + .click(); - cy.get("ui5-toolbar-item") - .find("ui5-button") - .realClick(); - expect(cy.get("ui5-popover").should("be.visible")); - }); + // Verify the popover is open + cy.get("ui5-toolbar") + .shadow() + .find(".ui5-overflow-popover") + .should("have.prop", "open", true); + + // Click on the item with prevent-overflow-closing + cy.get("ui5-toolbar-item[prevent-overflow-closing]") + .find("ui5-button") + .click(); + + // Verify the popover remains open + cy.get("ui5-toolbar") + .shadow() + .find(".ui5-overflow-popover") + .should("have.prop", "open", true); + + // Optional: Test that normal items still close the popover + cy.get("ui5-toolbar-item:not([prevent-overflow-closing])") + .find("ui5-button") + .click(); + + // Verify the popover closes + cy.get("ui5-toolbar") + .shadow() + .find(".ui5-overflow-popover") + .should("have.prop", "open", false); + }); it("Should respect overflow-priority property", () => { // Mount the Toolbar with multiple ui5-toolbar-items From 6d61c76a38082785207104aa8df9a22d79072ef5 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Wed, 10 Sep 2025 16:18:41 +0300 Subject: [PATCH 04/43] chore(ui5-toolbar): toolbar item wrapper introduced --- packages/main/src/ToolbarItem.ts | 5 +- packages/main/src/ToolbarItemTemplate.tsx | 2 +- packages/main/test/pages/Toolbar.html | 7 -- packages/main/test/pages/ToolbarWrappers.html | 68 +++++++++++++++++++ 4 files changed, 70 insertions(+), 12 deletions(-) create mode 100644 packages/main/test/pages/ToolbarWrappers.html diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 6d8a0fa6f051..5db9aae6aead 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -133,16 +133,13 @@ class ToolbarItem extends UI5Element { }; } - _onClick = this.onClick.bind(this); - constructor() { super(); } /** * Handles the click event on the toolbar item. - * It stops the event propagation and fires a "click" event with the target reference. - * If `preventOverflowClosing` is false, it will also fire a "close-overflow" event. + * If `preventOverflowClosing` is false, it will fire a "close-overflow" event. * @param {Event} e - The click event */ onClick(e: Event): void { diff --git a/packages/main/src/ToolbarItemTemplate.tsx b/packages/main/src/ToolbarItemTemplate.tsx index 32898b7ea647..12126b0b4f48 100644 --- a/packages/main/src/ToolbarItemTemplate.tsx +++ b/packages/main/src/ToolbarItemTemplate.tsx @@ -2,7 +2,7 @@ import type ToolbarItem from "./ToolbarItem.js"; export default function ToolbarItemTemplate(this: ToolbarItem) { return ( -
+
); diff --git a/packages/main/test/pages/Toolbar.html b/packages/main/test/pages/Toolbar.html index bcd053bda1e2..8dcbe57ea41e 100644 --- a/packages/main/test/pages/Toolbar.html +++ b/packages/main/test/pages/Toolbar.html @@ -117,9 +117,6 @@ - - User Menu -
@@ -403,10 +400,6 @@ menu2.open = !menu2.open; menu2.opener = btnOpenMenu2; }); - - tralala.addEventListener("ui5-click", function(event) { - alert("clicked"); - }); diff --git a/packages/main/test/pages/ToolbarWrappers.html b/packages/main/test/pages/ToolbarWrappers.html new file mode 100644 index 000000000000..8a0d4f2b9024 --- /dev/null +++ b/packages/main/test/pages/ToolbarWrappers.html @@ -0,0 +1,68 @@ + + + + + + + Toolbar + + + + + + + + +
+ Standard Toolbar with ToolbarSelect and ToolbarButton +
+
+ + + + + + + + + + + 1 + 2 + 3 + + + + + +
+ Toolbar with ui5-select and ui5-button wrapped in ui5-toolbar-item +
+
+ + Left 1 (long) + Left 2 + Left 3 + Left 4 + Mid 1 + Mid 2 + Right 1 + Right 4 + + + 1 + 2 + 3 + + + + + + Call me later + + +
+
+ + + \ No newline at end of file From 84607fbc0bf8ff2c1371d944af6704913fce3234 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Wed, 10 Sep 2025 17:04:00 +0300 Subject: [PATCH 05/43] chore(ui5-toolbar): toolbar item wrapper introduced --- packages/main/src/ToolbarItem.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 5db9aae6aead..02f193b63382 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -140,7 +140,7 @@ class ToolbarItem extends UI5Element { /** * Handles the click event on the toolbar item. * If `preventOverflowClosing` is false, it will fire a "close-overflow" event. - * @param {Event} e - The click event + * @param {Event} e */ onClick(e: Event): void { e.stopImmediatePropagation(); From 8b60ec7d94814292970fffcdb3b05fcc206c0d79 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Wed, 10 Sep 2025 17:22:11 +0300 Subject: [PATCH 06/43] chore(ui5-toolbar): toolbar item wrapper introduced --- packages/main/src/ToolbarItem.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 02f193b63382..69cdd74410a6 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -140,7 +140,6 @@ class ToolbarItem extends UI5Element { /** * Handles the click event on the toolbar item. * If `preventOverflowClosing` is false, it will fire a "close-overflow" event. - * @param {Event} e */ onClick(e: Event): void { e.stopImmediatePropagation(); From fed79a56873bc137f032f51aa9542ec1b1df1c7e Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Tue, 16 Sep 2025 11:25:29 +0300 Subject: [PATCH 07/43] chore(ui5-toolbar-item): item wrapers introduced --- packages/main/src/ToolbarItem.ts | 11 ++---- packages/main/test/pages/ToolbarWrappers.html | 37 +++++++++++++++++++ 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 69cdd74410a6..a0cfca51b705 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -20,10 +20,6 @@ type ToolbarItemEventDetail = { bubbles: true, }) -@event("click", { - bubbles: true, -}) - @customElement({ tag: "ui5-toolbar-item", languageAware: true, @@ -75,9 +71,9 @@ class ToolbarItem extends UI5Element { isOverflowed: boolean = false; /** - * Defines if the toolbar item is overflowed. - * @default false - * @since 2.11.0 + * Wrapped component slot. + * @public + * @since 2.15.0 */ @slot({ @@ -142,7 +138,6 @@ class ToolbarItem extends UI5Element { * If `preventOverflowClosing` is false, it will fire a "close-overflow" event. */ onClick(e: Event): void { - e.stopImmediatePropagation(); if (!this.preventOverflowClosing) { this.fireDecoratorEvent("close-overflow"); } diff --git a/packages/main/test/pages/ToolbarWrappers.html b/packages/main/test/pages/ToolbarWrappers.html index 8a0d4f2b9024..1b000f9d3a58 100644 --- a/packages/main/test/pages/ToolbarWrappers.html +++ b/packages/main/test/pages/ToolbarWrappers.html @@ -62,6 +62,43 @@ + Toolbar with various components +
+
+ + + + + + Simple text + + + + + + + + + + + + + + + + + + Simple title + + + + + +
div with Link and text
+
+ +
+
From 669a44a31d749a1d2a58845266b78285de2cc86f Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Tue, 16 Sep 2025 14:06:38 +0300 Subject: [PATCH 08/43] chore(ui5-toolbar-item): items wrappers introduced --- packages/main/src/ToolbarItem.ts | 4 ++-- packages/main/test/pages/ToolbarWrappers.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 35064129c6f2..6bac1967fab1 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -142,8 +142,8 @@ class ToolbarItem extends UI5Element { * Handles the click event on the toolbar item. * If `preventOverflowClosing` is false, it will fire a "close-overflow" event. */ - onClick(e: Event): void { - if (!this.preventOverflowClosing) { + onClick(e?: Event): void { + if (e && !this.preventOverflowClosing) { this.fireDecoratorEvent("close-overflow"); } } diff --git a/packages/main/test/pages/ToolbarWrappers.html b/packages/main/test/pages/ToolbarWrappers.html index 1b000f9d3a58..99749b1b75aa 100644 --- a/packages/main/test/pages/ToolbarWrappers.html +++ b/packages/main/test/pages/ToolbarWrappers.html @@ -73,7 +73,7 @@ Simple text - + From 9a1252234a32c16e718b92f93750c166029f2466 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Tue, 16 Sep 2025 14:20:04 +0300 Subject: [PATCH 09/43] chore(ui5-toolbar): toolbar item wrapper introduced --- packages/main/test/pages/ToolbarWrappers.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/main/test/pages/ToolbarWrappers.html b/packages/main/test/pages/ToolbarWrappers.html index 99749b1b75aa..f105eaf9ef98 100644 --- a/packages/main/test/pages/ToolbarWrappers.html +++ b/packages/main/test/pages/ToolbarWrappers.html @@ -69,7 +69,7 @@ - + Simple text From 3456ef3cfcc0f168b367c032cea0f922d760a0b7 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Thu, 9 Oct 2025 11:42:21 +0300 Subject: [PATCH 10/43] chore(ui5-toolbar): item wrappers introduced --- packages/main/src/Breadcrumbs.ts | 5 +++ packages/main/src/Toolbar.ts | 4 +- packages/main/src/ToolbarItem.ts | 44 +++++++++++++++++++ packages/main/src/ToolbarTemplate.tsx | 23 +++------- packages/main/src/themes/Toolbar.css | 7 +-- packages/main/src/themes/ToolbarItem.css | 3 ++ packages/main/src/themes/ToolbarPopover.css | 4 ++ packages/main/test/pages/ToolbarWrappers.html | 16 ++++++- 8 files changed, 83 insertions(+), 23 deletions(-) create mode 100644 packages/main/src/themes/ToolbarItem.css diff --git a/packages/main/src/Breadcrumbs.ts b/packages/main/src/Breadcrumbs.ts index bd23b8976380..c26e8bb2766a 100644 --- a/packages/main/src/Breadcrumbs.ts +++ b/packages/main/src/Breadcrumbs.ts @@ -642,6 +642,11 @@ class Breadcrumbs extends UI5Element { get _cancelButtonText() { return Breadcrumbs.i18nBundle.getText(BREADCRUMBS_CANCEL_BUTTON); } + /* Toolbar api */ + + get totalContentWidth() { + return this._getTotalContentWidth(); + } } Breadcrumbs.define(); diff --git a/packages/main/src/Toolbar.ts b/packages/main/src/Toolbar.ts index 0a6165bb24e2..74a734536110 100644 --- a/packages/main/src/Toolbar.ts +++ b/packages/main/src/Toolbar.ts @@ -302,7 +302,7 @@ class Toolbar extends UI5Element { await renderFinished(); this.storeItemsWidth(); - this.processOverflowLayout(); + setTimeout(() => this.processOverflowLayout(), 0); this.items.forEach(item => { item.isOverflowed = this.overflowItems.map(overflowItem => overflowItem).indexOf(item) !== -1; }); @@ -470,7 +470,7 @@ class Toolbar extends UI5Element { onResize() { this.closeOverflow(); - this.processOverflowLayout(); + setTimeout(() => this.processOverflowLayout(), 0); } /** diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 6bac1967fab1..9613f2a75ae5 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -5,6 +5,7 @@ import slot from "@ui5/webcomponents-base/dist/decorators/slot.js"; import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js"; import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; import ToolbarItemTemplate from "./ToolbarItemTemplate.js"; +import ToolbarItemCss from "./generated/themes/ToolbarItem.css.js"; import type ToolbarItemOverflowBehavior from "./types/ToolbarItemOverflowBehavior.js"; @@ -12,6 +13,11 @@ type IEventOptions = { preventClosing: boolean; } +type ISelfOverflowedItem = { + totalContentWidth: () => number; + selfOverflowed: boolean; +} + type ToolbarItemEventDetail = { targetRef: HTMLElement; } @@ -25,6 +31,7 @@ type ToolbarItemEventDetail = { languageAware: true, renderer: jsxRenderer, template: ToolbarItemTemplate, + styles: ToolbarItemCss, }) /** @@ -70,6 +77,24 @@ class ToolbarItem extends UI5Element { @property({ type: Boolean }) isOverflowed: boolean = false; + /** + * @default false + * @public + * @since 2.16.0 + */ + + @property({ type: Boolean }) + selfOverflowed: boolean = false; + + /** + * @default false + * @public + * @since 2.16.0 + */ + + @property({ type: Boolean }) + expandInOverflow: boolean = false; + _isRendering = true; onAfterRendering(): void { @@ -121,10 +146,28 @@ class ToolbarItem extends UI5Element { return false; } + /** + * @protected + */ + get isDefaultWrapper() { + return false; + } + get stableDomRef() { return this.getAttribute("stable-dom-ref") || `${this._id}-stable-dom-ref`; } + get flexBasis(): string { + const item = this.querySelector(":first-child") as ISelfOverflowedItem | null; + + if (this.selfOverflowed && item && typeof item.totalContentWidth !== "undefined") { + const width = item.totalContentWidth; + return `flex-basis: ${width ? `${width + 1}px` : "auto"}`; + } + + return "flex-basis: auto"; + } + get classes() { return { root: { @@ -152,6 +195,7 @@ class ToolbarItem extends UI5Element { export type { IEventOptions, ToolbarItemEventDetail, + ISelfOverflowedItem, }; ToolbarItem.define(); diff --git a/packages/main/src/ToolbarTemplate.tsx b/packages/main/src/ToolbarTemplate.tsx index 9f086627b48f..425ec69731d3 100644 --- a/packages/main/src/ToolbarTemplate.tsx +++ b/packages/main/src/ToolbarTemplate.tsx @@ -14,15 +14,10 @@ export default function ToolbarTemplate(this: Toolbar) { aria-label={this.accInfo.root.accessibleName} > {this.standardItems.map(item => { - if ("styles" in item) { - return ( -
- -
- ); - } + const selfOverflowClass = item.selfOverflowed ? "ui5-tb-self-overflow" : ""; + const classes = `ui5-tb-item ${selfOverflowClass}`; return ( -
+
); @@ -56,15 +51,11 @@ export default function ToolbarTemplate(this: Toolbar) { "ui5-overflow-list": true }}> {this.overflowItems.map(item => { - if (item.isSeparator) { - return ( -
- -
- ); - } + const separatorClass = item.isSeparator ? "ui5-tb-separator ui5-tb-separator-in-overflow" : ""; + const selfOverflowClass = item.selfOverflowed ? "ui5-tb-popover-self-overflow" : ""; + const classes = `ui5-tb-popover-item ${separatorClass} ${selfOverflowClass}`; return ( -
+
); diff --git a/packages/main/src/themes/Toolbar.css b/packages/main/src/themes/Toolbar.css index 7b1813881459..4d3930cdfcdd 100644 --- a/packages/main/src/themes/Toolbar.css +++ b/packages/main/src/themes/Toolbar.css @@ -28,13 +28,14 @@ .ui5-tb-item { flex-shrink: 0; -} - -.ui5-tb-item { margin-inline-end: var(--_ui5-toolbar-item-margin-right); margin-inline-start: var(--_ui5-toolbar-item-margin-left); } +.ui5-tb-self-overflow { + flex-shrink: 1; +} + /* Last visible element should not have margins. Last element is: overflow button or tb-item when overflow button is hidden */ .ui5-tb-overflow-btn, diff --git a/packages/main/src/themes/ToolbarItem.css b/packages/main/src/themes/ToolbarItem.css new file mode 100644 index 000000000000..4babbf26d8d1 --- /dev/null +++ b/packages/main/src/themes/ToolbarItem.css @@ -0,0 +1,3 @@ +:host([expand-in-overflow]) ::slotted(*) { + width: 100%; +} \ No newline at end of file diff --git a/packages/main/src/themes/ToolbarPopover.css b/packages/main/src/themes/ToolbarPopover.css index aba44a2046d9..43cc036fcab6 100644 --- a/packages/main/src/themes/ToolbarPopover.css +++ b/packages/main/src/themes/ToolbarPopover.css @@ -32,3 +32,7 @@ .ui5-tb-separator-in-overflow[visible]{ display: block; } + +.ui5-tb-popover-self-overflow{ + max-width: 200px; +} diff --git a/packages/main/test/pages/ToolbarWrappers.html b/packages/main/test/pages/ToolbarWrappers.html index f105eaf9ef98..9cc7f090183f 100644 --- a/packages/main/test/pages/ToolbarWrappers.html +++ b/packages/main/test/pages/ToolbarWrappers.html @@ -40,8 +40,8 @@
- Left 1 (long) - Left 2 + Left 1 (long) + Left 2 Left 3 Left 4 Mid 1 @@ -66,6 +66,18 @@
+ + + Link1 + Link2 + Link3 + Link4 + Link5 + Link6 + Link7 + Location + + From 67f676cfb5ed3fade218433566493858574377e3 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Thu, 9 Oct 2025 19:30:05 +0300 Subject: [PATCH 11/43] fix(ui5-shellbar): adjust sizes-parameters for shellbar --- packages/main/src/themes/base/sizes-parameters.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/main/src/themes/base/sizes-parameters.css b/packages/main/src/themes/base/sizes-parameters.css index 2b6ffcc3d46c..20eab2e85cca 100644 --- a/packages/main/src/themes/base/sizes-parameters.css +++ b/packages/main/src/themes/base/sizes-parameters.css @@ -167,7 +167,7 @@ --_ui5_dynamic_page_title_actions_separator_height: var(--_ui5-toolbar-separator-height); /* ShellBar */ - --_ui5-shellbar-separator-height: 1.5rem; + --_ui5-shellbar-separator-height: 2rem; } [data-ui5-compact-size], @@ -406,5 +406,5 @@ --_ui5_dynamic_page_title_actions_separator_height: var(--_ui5-toolbar-separator-height); /* ShellBar */ - --_ui5-shellbar-separator-height: 1.5rem; + --_ui5-shellbar-separator-height: 2rem; } From a2f410b1bd2f52398853f16c5de635ca8e8c9344 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Mon, 10 Nov 2025 10:58:42 +0200 Subject: [PATCH 12/43] chore(ui5-toolbar): wrappers introduced --- packages/main/src/Toolbar.ts | 28 +++++- packages/main/src/ToolbarItem.ts | 12 +-- packages/main/src/ToolbarTemplate.tsx | 8 +- packages/main/src/themes/Toolbar.css | 7 ++ packages/main/src/themes/ToolbarPopover.css | 6 +- packages/main/test/pages/ToolbarWrappers.html | 88 ++++++++++--------- 6 files changed, 86 insertions(+), 63 deletions(-) diff --git a/packages/main/src/Toolbar.ts b/packages/main/src/Toolbar.ts index 74a734536110..81d42064bd61 100644 --- a/packages/main/src/Toolbar.ts +++ b/packages/main/src/Toolbar.ts @@ -298,13 +298,30 @@ class Toolbar extends UI5Element { } } - async onAfterRendering() { + onAfterRendering() { + if (!this.items.some((item: ToolbarItem) => item.selfOverflowed)) { + this.onAfterRenderingAsync(); + } + this.afterRenderingOps(); + } + + async onAfterRenderingAsync() { await renderFinished(); + } + afterRenderingOps() { this.storeItemsWidth(); - setTimeout(() => this.processOverflowLayout(), 0); + this.processOverflowLayout(); this.items.forEach(item => { item.isOverflowed = this.overflowItems.map(overflowItem => overflowItem).indexOf(item) !== -1; + const itemWrapper = this.shadowRoot!.querySelector(`#${item._individualSlot}`) as HTMLElement; + if (item.selfOverflowed && !item.isOverflowed && itemWrapper) { + itemWrapper.style.maxWidth = `none`; + itemWrapper?.classList.add("ui5-tb-self-overflow-grow"); + item._maxWidth = Math.max(this.getItemWidth(item), item._maxWidth); + itemWrapper.style.maxWidth = `${item._maxWidth}px`; + itemWrapper?.classList.remove("ui5-tb-self-overflow-grow"); + } }); } @@ -470,7 +487,7 @@ class Toolbar extends UI5Element { onResize() { this.closeOverflow(); - setTimeout(() => this.processOverflowLayout(), 0); + setTimeout(() => this.onAfterRendering(), 0); } /** @@ -513,6 +530,11 @@ class Toolbar extends UI5Element { return Math.ceil(itemWidth); } + getItemWidthCss(item: ToolbarItem) { + const itemMinWidth = item.selfOverflowed && Number(this.getItemWidth(item)) ? `${this.getItemWidth(item)}px` : "auto"; + return `${itemMinWidth}`; + } + getCachedItemWidth(id: string) { return this.ITEMS_WIDTH_MAP.get(id); } diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 9613f2a75ae5..b47ba043c4a4 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -96,6 +96,7 @@ class ToolbarItem extends UI5Element { expandInOverflow: boolean = false; _isRendering = true; + _maxWidth = 0; onAfterRendering(): void { this._isRendering = false; @@ -157,17 +158,6 @@ class ToolbarItem extends UI5Element { return this.getAttribute("stable-dom-ref") || `${this._id}-stable-dom-ref`; } - get flexBasis(): string { - const item = this.querySelector(":first-child") as ISelfOverflowedItem | null; - - if (this.selfOverflowed && item && typeof item.totalContentWidth !== "undefined") { - const width = item.totalContentWidth; - return `flex-basis: ${width ? `${width + 1}px` : "auto"}`; - } - - return "flex-basis: auto"; - } - get classes() { return { root: { diff --git a/packages/main/src/ToolbarTemplate.tsx b/packages/main/src/ToolbarTemplate.tsx index 425ec69731d3..0b5daa7c8d44 100644 --- a/packages/main/src/ToolbarTemplate.tsx +++ b/packages/main/src/ToolbarTemplate.tsx @@ -17,7 +17,7 @@ export default function ToolbarTemplate(this: Toolbar) { const selfOverflowClass = item.selfOverflowed ? "ui5-tb-self-overflow" : ""; const classes = `ui5-tb-item ${selfOverflowClass}`; return ( -
+
); @@ -51,9 +51,9 @@ export default function ToolbarTemplate(this: Toolbar) { "ui5-overflow-list": true }}> {this.overflowItems.map(item => { - const separatorClass = item.isSeparator ? "ui5-tb-separator ui5-tb-separator-in-overflow" : ""; - const selfOverflowClass = item.selfOverflowed ? "ui5-tb-popover-self-overflow" : ""; - const classes = `ui5-tb-popover-item ${separatorClass} ${selfOverflowClass}`; + const separatorClass = item.isSeparator ? " ui5-tb-separator ui5-tb-separator-in-overflow" : ""; + const selfOverflowClass = item.selfOverflowed ? " ui5-tb-popover-self-overflow" : ""; + const classes = `ui5-tb-popover-item${separatorClass}${selfOverflowClass}`; return (
diff --git a/packages/main/src/themes/Toolbar.css b/packages/main/src/themes/Toolbar.css index 4d3930cdfcdd..e6a94439e8a3 100644 --- a/packages/main/src/themes/Toolbar.css +++ b/packages/main/src/themes/Toolbar.css @@ -33,7 +33,14 @@ } .ui5-tb-self-overflow { + min-width: 2.5rem; flex-shrink: 1; + flex-grow: 1; + +} + +.ui5-tb-self-overflow-grow { + position: absolute } /* Last visible element should not have margins. diff --git a/packages/main/src/themes/ToolbarPopover.css b/packages/main/src/themes/ToolbarPopover.css index 43cc036fcab6..6319aecb5e08 100644 --- a/packages/main/src/themes/ToolbarPopover.css +++ b/packages/main/src/themes/ToolbarPopover.css @@ -6,7 +6,7 @@ display: flex; flex-direction: column; justify-content: center; - align-items: center; + align-items: flex-start; } .ui5-tb-popover-item { @@ -34,5 +34,7 @@ } .ui5-tb-popover-self-overflow{ - max-width: 200px; + /* max-width: min-content; + flex-grow: 0; + flex-basis: 0; */ } diff --git a/packages/main/test/pages/ToolbarWrappers.html b/packages/main/test/pages/ToolbarWrappers.html index 9cc7f090183f..d349dc7ae160 100644 --- a/packages/main/test/pages/ToolbarWrappers.html +++ b/packages/main/test/pages/ToolbarWrappers.html @@ -14,59 +14,61 @@
- Standard Toolbar with ToolbarSelect and ToolbarButton -
-
- - - - - - - - - - - 1 - 2 - 3 - - - - - -
- Toolbar with ui5-select and ui5-button wrapped in ui5-toolbar-item + Toolbar with various components
- - Left 1 (long) - Left 2 - Left 3 - Left 4 - Mid 1 - Mid 2 - Right 1 - Right 4 + + - - 1 - 2 - 3 - + - - - - Call me later + + Simple text + + + + + + + + + + + + + + + + + + Link1 + Link2 + Link3 + Link4 + Link5 + Link6 + Link7 + Location + + + + Simple title + + + + + +
div with Link and text
+
+
Toolbar with various components
- - + + Link1 Link2 From ee89f1c57693fe16971059fb5ea65be0f5568b02 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Mon, 10 Nov 2025 14:36:57 +0200 Subject: [PATCH 13/43] fix(ui5-toolbar): toolbar items introduced --- packages/main/cypress/specs/Toolbar.cy.tsx | 22 +++++++++++++++++---- packages/main/src/Breadcrumbs.ts | 5 ----- packages/main/src/themes/ToolbarPopover.css | 6 ------ 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/packages/main/cypress/specs/Toolbar.cy.tsx b/packages/main/cypress/specs/Toolbar.cy.tsx index ef51d3385bea..932d5f0cc073 100644 --- a/packages/main/cypress/specs/Toolbar.cy.tsx +++ b/packages/main/cypress/specs/Toolbar.cy.tsx @@ -601,12 +601,12 @@ describe("Toolbar Button", () => { cy.get("@toolbar").then($toolbar => { const toolbar = $toolbar[0] as Toolbar; const addButton = document.getElementById("add-btn") as ToolbarButton; - + expect(toolbar.itemsToOverflow.includes(addButton)).to.be.true; - + const initialOverflowCount = toolbar.itemsToOverflow.length; const initialItemsWidth = toolbar.itemsWidth; - + addButton.disabled = !addButton.disabled; cy.get("@toolbar").then($toolbarAfter => { @@ -642,14 +642,28 @@ describe("Toolbar Item", () => { expect(button).to.contain.text("User Menu"); }); + // Attach a click event to the inner button cy.get("ui5-button#innerButton") .then(button => { button.get(0).addEventListener("click", cy.stub().as("buttonClicked")); }); + cy.get('ui5-toolbar') // Select the toolbar + .shadow() // Access the shadow DOM of the toolbar + .find('.ui5-tb-overflow-btn') // Find the overflow button inside the shadow DOM + .realClick(); + + cy.get("ui5-toolbar") + .shadow() + .find("[ui5-popover]") + .as("popover") + + cy.get("@popover") + .should("have.prop", "open", true); + // Trigger a click event on the inner button - cy.get("ui5-button#innerButton").click(); + cy.get("ui5-button#innerButton").realClick(); // Verify the click event was triggered cy.get("@buttonClicked").should("have.been.calledOnce"); diff --git a/packages/main/src/Breadcrumbs.ts b/packages/main/src/Breadcrumbs.ts index c26e8bb2766a..bd23b8976380 100644 --- a/packages/main/src/Breadcrumbs.ts +++ b/packages/main/src/Breadcrumbs.ts @@ -642,11 +642,6 @@ class Breadcrumbs extends UI5Element { get _cancelButtonText() { return Breadcrumbs.i18nBundle.getText(BREADCRUMBS_CANCEL_BUTTON); } - /* Toolbar api */ - - get totalContentWidth() { - return this._getTotalContentWidth(); - } } Breadcrumbs.define(); diff --git a/packages/main/src/themes/ToolbarPopover.css b/packages/main/src/themes/ToolbarPopover.css index 6319aecb5e08..b11ad5e99025 100644 --- a/packages/main/src/themes/ToolbarPopover.css +++ b/packages/main/src/themes/ToolbarPopover.css @@ -32,9 +32,3 @@ .ui5-tb-separator-in-overflow[visible]{ display: block; } - -.ui5-tb-popover-self-overflow{ - /* max-width: min-content; - flex-grow: 0; - flex-basis: 0; */ -} From 83e8288955223073ddc266003c47b56ad96e6e7b Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Mon, 10 Nov 2025 15:26:51 +0200 Subject: [PATCH 14/43] chore(ui5-toolbar): toolbar wrappers introduced --- packages/main/src/Toolbar.ts | 8 +- packages/main/src/ToolbarItem.ts | 16 --- packages/main/test/pages/Toolbar.html | 7 +- packages/main/test/pages/ToolbarWrappers.html | 117 +++++++++++------- 4 files changed, 75 insertions(+), 73 deletions(-) diff --git a/packages/main/src/Toolbar.ts b/packages/main/src/Toolbar.ts index 81d42064bd61..21b287c54e9b 100644 --- a/packages/main/src/Toolbar.ts +++ b/packages/main/src/Toolbar.ts @@ -487,7 +487,8 @@ class Toolbar extends UI5Element { onResize() { this.closeOverflow(); - setTimeout(() => this.onAfterRendering(), 0); + this.storeItemsWidth(); + this.processOverflowLayout(); } /** @@ -530,11 +531,6 @@ class Toolbar extends UI5Element { return Math.ceil(itemWidth); } - getItemWidthCss(item: ToolbarItem) { - const itemMinWidth = item.selfOverflowed && Number(this.getItemWidth(item)) ? `${this.getItemWidth(item)}px` : "auto"; - return `${itemMinWidth}`; - } - getCachedItemWidth(id: string) { return this.ITEMS_WIDTH_MAP.get(id); } diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index b47ba043c4a4..0e513525fa04 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -13,11 +13,6 @@ type IEventOptions = { preventClosing: boolean; } -type ISelfOverflowedItem = { - totalContentWidth: () => number; - selfOverflowed: boolean; -} - type ToolbarItemEventDetail = { targetRef: HTMLElement; } @@ -77,12 +72,6 @@ class ToolbarItem extends UI5Element { @property({ type: Boolean }) isOverflowed: boolean = false; - /** - * @default false - * @public - * @since 2.16.0 - */ - @property({ type: Boolean }) selfOverflowed: boolean = false; @@ -167,10 +156,6 @@ class ToolbarItem extends UI5Element { }; } - constructor() { - super(); - } - /** * Handles the click event on the toolbar item. * If `preventOverflowClosing` is false, it will fire a "close-overflow" event. @@ -185,7 +170,6 @@ class ToolbarItem extends UI5Element { export type { IEventOptions, ToolbarItemEventDetail, - ISelfOverflowedItem, }; ToolbarItem.define(); diff --git a/packages/main/test/pages/Toolbar.html b/packages/main/test/pages/Toolbar.html index 8dcbe57ea41e..31f017e60a6d 100644 --- a/packages/main/test/pages/Toolbar.html +++ b/packages/main/test/pages/Toolbar.html @@ -1,4 +1,5 @@ - + + Call me later @@ -77,9 +78,7 @@ - - Call me later - +


diff --git a/packages/main/test/pages/ToolbarWrappers.html b/packages/main/test/pages/ToolbarWrappers.html index d349dc7ae160..6357e5eac88d 100644 --- a/packages/main/test/pages/ToolbarWrappers.html +++ b/packages/main/test/pages/ToolbarWrappers.html @@ -6,7 +6,6 @@ Toolbar - @@ -14,11 +13,46 @@
+ Standard Toolbar with ToolbarSelect and ToolbarButton +
+
+ + + + + + + + + + + 1 + 2 + 3 + + + + + +
+ Toolbar with ui5-select and ui5-button wrapped in ui5-toolbar-item Toolbar with various components
+
+ + Left 1 (long) + Left 2 + Left 3 + Left 4 + Mid 1 + Mid 2 + Right 1 + Right 4 + + Toolbar with various components +
- @@ -40,18 +74,6 @@ - - - Link1 - Link2 - Link3 - Link4 - Link5 - Link6 - Link7 - Location - - Simple title @@ -64,12 +86,12 @@
- Toolbar with various components + Toolbar with breadcrumbs (self-overflowed) component at the beginning
- + - + Link1 Link2 Link3 @@ -80,37 +102,38 @@ Location - - - - - Simple text - - - - - - - - - - - - - - - - - - Simple title - - - - - -
div with Link and text
+ + + + + + + +
+
+ Toolbar with breadcrumbs (self-overflowed) component at the middle +
+
+ + + + + + + + + + Link1 + Link2 + Link3 + Link4 + Link5 + Link6 + Link7 + Location + - +
From d53d29453030f6236ce55f706cd89966318a21df Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Tue, 11 Nov 2025 11:10:34 +0200 Subject: [PATCH 15/43] chore(ui5-toolbar): wrappers introduced --- packages/main/src/ToolbarItem.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 0e513525fa04..e5df6d299930 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -72,13 +72,20 @@ class ToolbarItem extends UI5Element { @property({ type: Boolean }) isOverflowed: boolean = false; + /** + * Defines if the component, wrapped in the toolbar item, has his own overflow mechanism. + * @default false + * @public + * @since 2.16.2 + */ @property({ type: Boolean }) selfOverflowed: boolean = false; /** + * Defines if the component, wrapped in the toolbar item, should be expanded in the overflow popover. * @default false * @public - * @since 2.16.0 + * @since 2.16.2 */ @property({ type: Boolean }) @@ -93,7 +100,7 @@ class ToolbarItem extends UI5Element { /** * Wrapped component slot. * @public - * @since 2.15.0 + * @since 2.16.2 */ @slot({ @@ -137,6 +144,7 @@ class ToolbarItem extends UI5Element { } /** + * Returns if the item is default wrapper for certain component. * @protected */ get isDefaultWrapper() { From 9615fee9e0d6ef775d7d96f02df649d05001ca88 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Mon, 17 Nov 2025 14:19:04 +0200 Subject: [PATCH 16/43] chore(ui5-toolbar): toolbarItem wrappers --- packages/main/src/Button.ts | 5 +++ packages/main/src/Select.ts | 5 +++ packages/main/src/Toolbar.ts | 39 +++++++++---------- packages/main/src/ToolbarButton.ts | 8 ---- packages/main/src/ToolbarItem.ts | 30 +++++++++++--- packages/main/src/ToolbarSelect.ts | 8 ---- packages/main/src/bundle.esm.ts | 1 + packages/main/src/themes/Toolbar.css | 2 +- packages/main/test/pages/ToolbarWrappers.html | 6 ++- 9 files changed, 60 insertions(+), 44 deletions(-) diff --git a/packages/main/src/Button.ts b/packages/main/src/Button.ts index 065fd36eb9e4..2ca95ca49eeb 100644 --- a/packages/main/src/Button.ts +++ b/packages/main/src/Button.ts @@ -689,6 +689,11 @@ class Button extends UI5Element implements IButton { get shouldRenderBadge() { return !!this.badge.length && (!!this.badge[0].text.length || this.badge[0].design === ButtonBadgeDesign.AttentionDot); } + + // Method called by ui5-toolbar to inform about the existing toolbar wrapper + get hasToolbarWrapper() { + return "ToolbarSelect"; + } } Button.define(); diff --git a/packages/main/src/Select.ts b/packages/main/src/Select.ts index fe7d4616cf22..b51d3dcb3c5b 100644 --- a/packages/main/src/Select.ts +++ b/packages/main/src/Select.ts @@ -1181,6 +1181,11 @@ class Select extends UI5Element implements IFormInputElement { _getPopover() { return this.shadowRoot!.querySelector("[ui5-popover]"); } + + // Method called by ui5-toolbar to inform about the existing toolbar wrapper + get hasToolbarWrapper() { + return "ToolbarSelect"; + } } Select.define(); diff --git a/packages/main/src/Toolbar.ts b/packages/main/src/Toolbar.ts index 21b287c54e9b..411ab66e4ed8 100644 --- a/packages/main/src/Toolbar.ts +++ b/packages/main/src/Toolbar.ts @@ -298,33 +298,32 @@ class Toolbar extends UI5Element { } } - onAfterRendering() { - if (!this.items.some((item: ToolbarItem) => item.selfOverflowed)) { - this.onAfterRenderingAsync(); - } - this.afterRenderingOps(); - } - - async onAfterRenderingAsync() { + async onAfterRendering() { await renderFinished(); - } - - afterRenderingOps() { this.storeItemsWidth(); this.processOverflowLayout(); this.items.forEach(item => { - item.isOverflowed = this.overflowItems.map(overflowItem => overflowItem).indexOf(item) !== -1; - const itemWrapper = this.shadowRoot!.querySelector(`#${item._individualSlot}`) as HTMLElement; - if (item.selfOverflowed && !item.isOverflowed && itemWrapper) { - itemWrapper.style.maxWidth = `none`; - itemWrapper?.classList.add("ui5-tb-self-overflow-grow"); - item._maxWidth = Math.max(this.getItemWidth(item), item._maxWidth); - itemWrapper.style.maxWidth = `${item._maxWidth}px`; - itemWrapper?.classList.remove("ui5-tb-self-overflow-grow"); - } + this.addItemsAdditionalProperties(item); }); } + addItemsAdditionalProperties(item: ToolbarItem) { + item.isOverflowed = this.overflowItems.map(overflowItem => overflowItem).indexOf(item) !== -1; + const itemWrapper = this.shadowRoot!.querySelector(`#${item._individualSlot}`) as HTMLElement; + if (item.selfOverflowed && !item.isOverflowed && itemWrapper) { + // We need to set the max-width to the self-overflow element in order ot prevent it from taking all the available space, + // since, unlike the other items, it is allowed to grow and shrink + // We need to set the max-width to none and its position to absolute to allow the item to grow and measure its width, + // then when set, the max-width will be cached and we will set its highest value to not cut it when the Toolbar shrinks it + // on rendering and then we resize it manually. + itemWrapper.style.maxWidth = `none`; + itemWrapper?.classList.add("ui5-tb-self-overflow-grow"); + item._maxWidth = Math.max(this.getItemWidth(item), item._maxWidth); + itemWrapper.style.maxWidth = `${item._maxWidth}px`; + itemWrapper?.classList.remove("ui5-tb-self-overflow-grow"); + } + } + /** * Returns if the overflow popup is open. * @public diff --git a/packages/main/src/ToolbarButton.ts b/packages/main/src/ToolbarButton.ts index 9396c144c74f..b2599472a8e8 100644 --- a/packages/main/src/ToolbarButton.ts +++ b/packages/main/src/ToolbarButton.ts @@ -162,14 +162,6 @@ class ToolbarButton extends ToolbarItem { }; } - onClick(e: Event) { - e.stopImmediatePropagation(); - const prevented = !this.fireDecoratorEvent("click", { targetRef: e.target as HTMLElement }); - if (!prevented && !this.preventOverflowClosing) { - this.fireDecoratorEvent("close-overflow"); - } - } - /** * @override */ diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index e5df6d299930..39ee346ae02a 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -6,7 +6,6 @@ import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js"; import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; import ToolbarItemTemplate from "./ToolbarItemTemplate.js"; import ToolbarItemCss from "./generated/themes/ToolbarItem.css.js"; - import type ToolbarItemOverflowBehavior from "./types/ToolbarItemOverflowBehavior.js"; type IEventOptions = { @@ -17,6 +16,10 @@ type ToolbarItemEventDetail = { targetRef: HTMLElement; } +type IOverflowToolbarItem = { + hasToolbarWrapper?: string | undefined; +} + @event("close-overflow", { bubbles: true, }) @@ -76,7 +79,7 @@ class ToolbarItem extends UI5Element { * Defines if the component, wrapped in the toolbar item, has his own overflow mechanism. * @default false * @public - * @since 2.16.2 + * @since 2.17.0 */ @property({ type: Boolean }) selfOverflowed: boolean = false; @@ -85,7 +88,7 @@ class ToolbarItem extends UI5Element { * Defines if the component, wrapped in the toolbar item, should be expanded in the overflow popover. * @default false * @public - * @since 2.16.2 + * @since 2.17.0 */ @property({ type: Boolean }) @@ -94,13 +97,18 @@ class ToolbarItem extends UI5Element { _isRendering = true; _maxWidth = 0; + onBeforeRendering(): void { + this.checkForWrapper(); + } + onAfterRendering(): void { this._isRendering = false; } + /** * Wrapped component slot. * @public - * @since 2.16.2 + * @since 2.17.0 */ @slot({ @@ -108,6 +116,14 @@ class ToolbarItem extends UI5Element { }) item!: HTMLElement | undefined; + // Method called by ui5-toolbar to inform about the existing toolbar wrapper + checkForWrapper() { + if (this.tagName === "UI5-TOOLBAR-ITEM" + && this.getSlottedNodes("item").length + && this.getSlottedNodes("item")[0]!.hasToolbarWrapper) { + console.warn(`This UI5 web component has its predefined toolbar wrapper called ${this.getSlottedNodes("item")[0]!.hasToolbarWrapper}.`); + } + } /** * Defines if the width of the item should be ignored in calculating the whole width of the toolbar * @protected @@ -168,8 +184,10 @@ class ToolbarItem extends UI5Element { * Handles the click event on the toolbar item. * If `preventOverflowClosing` is false, it will fire a "close-overflow" event. */ - onClick(e?: Event): void { - if (e && !this.preventOverflowClosing) { + onClick(e: Event): void { + e.stopImmediatePropagation(); + const prevented = !this.fireDecoratorEvent("click", { targetRef: e.target as HTMLElement }); + if (prevented && !this.preventOverflowClosing) { this.fireDecoratorEvent("close-overflow"); } } diff --git a/packages/main/src/ToolbarSelect.ts b/packages/main/src/ToolbarSelect.ts index 2e8451f9973d..5073804d8d13 100644 --- a/packages/main/src/ToolbarSelect.ts +++ b/packages/main/src/ToolbarSelect.ts @@ -163,14 +163,6 @@ class ToolbarSelect extends ToolbarItem { // Internal value storage, in case the composite select is not rendered on the the assignment happens _value: string = ""; - onClick(e: Event): void { - e.stopImmediatePropagation(); - const prevented = !this.fireDecoratorEvent("click", { targetRef: e.target as HTMLElement }); - if (prevented && !this.preventOverflowClosing) { - this.fireDecoratorEvent("close-overflow"); - } - } - onOpen(e: Event): void { e.stopImmediatePropagation(); const prevented = !this.fireDecoratorEvent("open", { targetRef: e.target as HTMLElement }); diff --git a/packages/main/src/bundle.esm.ts b/packages/main/src/bundle.esm.ts index 77c873528352..39061900ac38 100644 --- a/packages/main/src/bundle.esm.ts +++ b/packages/main/src/bundle.esm.ts @@ -118,6 +118,7 @@ import Title from "./Title.js"; import Toast from "./Toast.js"; import ToggleButton from "./ToggleButton.js"; import Toolbar from "./Toolbar.js"; +import ToolbarItem from "./ToolbarItem.js"; import ToolbarButton from "./ToolbarButton.js"; import ToolbarSeparator from "./ToolbarSeparator.js"; import ToolbarSpacer from "./ToolbarSpacer.js"; diff --git a/packages/main/src/themes/Toolbar.css b/packages/main/src/themes/Toolbar.css index e6a94439e8a3..d5931dcc2beb 100644 --- a/packages/main/src/themes/Toolbar.css +++ b/packages/main/src/themes/Toolbar.css @@ -40,7 +40,7 @@ } .ui5-tb-self-overflow-grow { - position: absolute + position: absolute; } /* Last visible element should not have margins. diff --git a/packages/main/test/pages/ToolbarWrappers.html b/packages/main/test/pages/ToolbarWrappers.html index 6357e5eac88d..dc0584a86e4b 100644 --- a/packages/main/test/pages/ToolbarWrappers.html +++ b/packages/main/test/pages/ToolbarWrappers.html @@ -33,6 +33,7 @@ + 123
Toolbar with ui5-select and ui5-button wrapped in ui5-toolbar-item @@ -59,7 +60,7 @@ Simple text - + @@ -137,6 +138,9 @@
+ \ No newline at end of file From 079ae126777a9c3b9c6f31c55a47cc47cdd14005 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Mon, 17 Nov 2025 14:38:00 +0200 Subject: [PATCH 17/43] chore(ui5-toolbar): toolbar wrappers introduced --- packages/main/src/ToolbarItem.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 39ee346ae02a..3784621f4212 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -121,6 +121,7 @@ class ToolbarItem extends UI5Element { if (this.tagName === "UI5-TOOLBAR-ITEM" && this.getSlottedNodes("item").length && this.getSlottedNodes("item")[0]!.hasToolbarWrapper) { + // eslint-disable-next-line no-console console.warn(`This UI5 web component has its predefined toolbar wrapper called ${this.getSlottedNodes("item")[0]!.hasToolbarWrapper}.`); } } From 4ad904bf2b4e12322704300f1bae0b85ebdb2495 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Tue, 18 Nov 2025 17:33:35 +0200 Subject: [PATCH 18/43] chore(ui5-toolbar): toolbar item wrapper introduced --- packages/main/src/ToolbarItem.ts | 10 ++++++++-- packages/main/src/ToolbarSelect.ts | 8 ++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 3784621f4212..fed9a7281d9f 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -22,6 +22,12 @@ type IOverflowToolbarItem = { @event("close-overflow", { bubbles: true, + cancelable: true, +}) + +@event("click", { + bubbles: true, + cancelable: true, }) @customElement({ @@ -185,10 +191,10 @@ class ToolbarItem extends UI5Element { * Handles the click event on the toolbar item. * If `preventOverflowClosing` is false, it will fire a "close-overflow" event. */ - onClick(e: Event): void { + onClick(e: Event) { e.stopImmediatePropagation(); const prevented = !this.fireDecoratorEvent("click", { targetRef: e.target as HTMLElement }); - if (prevented && !this.preventOverflowClosing) { + if (!prevented && !this.preventOverflowClosing) { this.fireDecoratorEvent("close-overflow"); } } diff --git a/packages/main/src/ToolbarSelect.ts b/packages/main/src/ToolbarSelect.ts index 5073804d8d13..2e8451f9973d 100644 --- a/packages/main/src/ToolbarSelect.ts +++ b/packages/main/src/ToolbarSelect.ts @@ -163,6 +163,14 @@ class ToolbarSelect extends ToolbarItem { // Internal value storage, in case the composite select is not rendered on the the assignment happens _value: string = ""; + onClick(e: Event): void { + e.stopImmediatePropagation(); + const prevented = !this.fireDecoratorEvent("click", { targetRef: e.target as HTMLElement }); + if (prevented && !this.preventOverflowClosing) { + this.fireDecoratorEvent("close-overflow"); + } + } + onOpen(e: Event): void { e.stopImmediatePropagation(); const prevented = !this.fireDecoratorEvent("open", { targetRef: e.target as HTMLElement }); From 180104d10d01b9e7c82e33d42f61b1fe4349b11d Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Wed, 19 Nov 2025 09:19:51 +0200 Subject: [PATCH 19/43] chore(ui5-toolbar-item): wrappers introduced --- packages/main/src/ToolbarItem.ts | 6 +- packages/main/src/themes/ToolbarItem.css | 5 + packages/main/test/pages/ToolbarItems.html | 143 +++++++++++++++++++++ 3 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 packages/main/test/pages/ToolbarItems.html diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index fed9a7281d9f..75e29739af51 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -193,11 +193,15 @@ class ToolbarItem extends UI5Element { */ onClick(e: Event) { e.stopImmediatePropagation(); - const prevented = !this.fireDecoratorEvent("click", { targetRef: e.target as HTMLElement }); + const prevented = e.defaultPrevented || this.supportsChangeEvent(e.target) || !(this.fireDecoratorEvent("click", { targetRef: e.target as HTMLElement })); if (!prevented && !this.preventOverflowClosing) { this.fireDecoratorEvent("close-overflow"); } } + + supportsChangeEvent(elem: EventTarget | null) { + return elem && ("onchange" in elem); + } } export type { diff --git a/packages/main/src/themes/ToolbarItem.css b/packages/main/src/themes/ToolbarItem.css index 4babbf26d8d1..dc98ab3dc9e2 100644 --- a/packages/main/src/themes/ToolbarItem.css +++ b/packages/main/src/themes/ToolbarItem.css @@ -1,3 +1,8 @@ +/* This style we need for the element inside Popover to not fire the click event on empty space */ +:host { + display: inline-block; +} + :host([expand-in-overflow]) ::slotted(*) { width: 100%; } \ No newline at end of file diff --git a/packages/main/test/pages/ToolbarItems.html b/packages/main/test/pages/ToolbarItems.html new file mode 100644 index 000000000000..7e51a1e3cfb5 --- /dev/null +++ b/packages/main/test/pages/ToolbarItems.html @@ -0,0 +1,143 @@ + + + + + + + Toolbar + + + + + + + +
+ Standard Toolbar with ToolbarSelect and ToolbarButton +
+
+ + + + + + + + + + + 1 + 2 + 3 + + + + + 123 + +
+ Toolbar with ui5-select and ui5-button wrapped in ui5-toolbar-item + Toolbar with various components +
+
+ + Left 1 (long) + Left 2 + Left 3 + Left 4 + Mid 1 + Mid 2 + Right 1 + Right 4 + + Toolbar with various components +
+
+ + + + + + Simple text + + + + + + + + + + + + + + + + + + Simple title + + + + + +
div with Link and text
+
+ +
+
+ Toolbar with breadcrumbs (self-overflowed) component at the beginning +
+
+ + + + Link1 + Link2 + Link3 + Link4 + Link5 + Link6 + Link7 + Location + + + + + + + + + + +
+ Toolbar with breadcrumbs (self-overflowed) component at the middle +
+
+ + + + + + + + + + Link1 + Link2 + Link3 + Link4 + Link5 + Link6 + Link7 + Location + + + + +
+
+ + + \ No newline at end of file From 258aa4a0fd0c8c391558ad66f6d3aa2424b73cc1 Mon Sep 17 00:00:00 2001 From: Petya Markova Date: Wed, 19 Nov 2025 12:42:43 +0200 Subject: [PATCH 20/43] Delete packages/main/test/pages/ToolbarWrappers.html --- packages/main/test/pages/ToolbarWrappers.html | 146 ------------------ 1 file changed, 146 deletions(-) delete mode 100644 packages/main/test/pages/ToolbarWrappers.html diff --git a/packages/main/test/pages/ToolbarWrappers.html b/packages/main/test/pages/ToolbarWrappers.html deleted file mode 100644 index dc0584a86e4b..000000000000 --- a/packages/main/test/pages/ToolbarWrappers.html +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - - Toolbar - - - - - - - -
- Standard Toolbar with ToolbarSelect and ToolbarButton -
-
- - - - - - - - - - - 1 - 2 - 3 - - - - - 123 - -
- Toolbar with ui5-select and ui5-button wrapped in ui5-toolbar-item - Toolbar with various components -
-
- - Left 1 (long) - Left 2 - Left 3 - Left 4 - Mid 1 - Mid 2 - Right 1 - Right 4 - - Toolbar with various components -
-
- - - - - - Simple text - - - - - - - - - - - - - - - - - - Simple title - - - - - -
div with Link and text
-
- -
-
- Toolbar with breadcrumbs (self-overflowed) component at the beginning -
-
- - - - Link1 - Link2 - Link3 - Link4 - Link5 - Link6 - Link7 - Location - - - - - - - - - - -
- Toolbar with breadcrumbs (self-overflowed) component at the middle -
-
- - - - - - - - - - Link1 - Link2 - Link3 - Link4 - Link5 - Link6 - Link7 - Location - - - - -
-
- - - - \ No newline at end of file From 92b262bf95c50867f0947ac16a586e09411b9864 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Wed, 19 Nov 2025 14:20:42 +0200 Subject: [PATCH 21/43] chore(ui5-toolbar): fix tests --- packages/main/src/ToolbarItem.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 75e29739af51..120d6fab7544 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -25,11 +25,6 @@ type IOverflowToolbarItem = { cancelable: true, }) -@event("click", { - bubbles: true, - cancelable: true, -}) - @customElement({ tag: "ui5-toolbar-item", languageAware: true, @@ -193,7 +188,7 @@ class ToolbarItem extends UI5Element { */ onClick(e: Event) { e.stopImmediatePropagation(); - const prevented = e.defaultPrevented || this.supportsChangeEvent(e.target) || !(this.fireDecoratorEvent("click", { targetRef: e.target as HTMLElement })); + const prevented = !(this.fireDecoratorEvent("click", { targetRef: e.target as HTMLElement }) || e.defaultPrevented || this.supportsChangeEvent(e.target)); if (!prevented && !this.preventOverflowClosing) { this.fireDecoratorEvent("close-overflow"); } From 492082ee14afba5278af3ecf61ace7aaab1c346b Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Wed, 19 Nov 2025 15:45:01 +0200 Subject: [PATCH 22/43] chore(ui5-toolbar): fix click event handling in ToolbarItem --- packages/main/src/ToolbarItem.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 120d6fab7544..6f66e728c283 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -188,8 +188,9 @@ class ToolbarItem extends UI5Element { */ onClick(e: Event) { e.stopImmediatePropagation(); - const prevented = !(this.fireDecoratorEvent("click", { targetRef: e.target as HTMLElement }) || e.defaultPrevented || this.supportsChangeEvent(e.target)); - if (!prevented && !this.preventOverflowClosing) { + const prevented = !(this.fireDecoratorEvent("click", { targetRef: e.target as HTMLElement }) || e.defaultPrevented); + const hasChangeEvent = this.supportsChangeEvent(e.target); + if ((!prevented || hasChangeEvent) && !this.preventOverflowClosing) { this.fireDecoratorEvent("close-overflow"); } } From dced6b4794731a43257414002d2fda4804134833 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Mon, 24 Nov 2025 12:04:44 +0200 Subject: [PATCH 23/43] chore(ui5-toolbar): fix toolbar items --- packages/main/src/ToolbarItem.ts | 7 +------ packages/main/src/themes/ToolbarItem.css | 3 --- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 6f66e728c283..707dc096a86a 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -189,15 +189,10 @@ class ToolbarItem extends UI5Element { onClick(e: Event) { e.stopImmediatePropagation(); const prevented = !(this.fireDecoratorEvent("click", { targetRef: e.target as HTMLElement }) || e.defaultPrevented); - const hasChangeEvent = this.supportsChangeEvent(e.target); - if ((!prevented || hasChangeEvent) && !this.preventOverflowClosing) { + if (!prevented && !this.preventOverflowClosing) { this.fireDecoratorEvent("close-overflow"); } } - - supportsChangeEvent(elem: EventTarget | null) { - return elem && ("onchange" in elem); - } } export type { diff --git a/packages/main/src/themes/ToolbarItem.css b/packages/main/src/themes/ToolbarItem.css index dc98ab3dc9e2..c86cfa392b3f 100644 --- a/packages/main/src/themes/ToolbarItem.css +++ b/packages/main/src/themes/ToolbarItem.css @@ -1,7 +1,4 @@ /* This style we need for the element inside Popover to not fire the click event on empty space */ -:host { - display: inline-block; -} :host([expand-in-overflow]) ::slotted(*) { width: 100%; From 991dcc602384133cbb070511041e4e49672d1edb Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Mon, 24 Nov 2025 16:59:03 +0200 Subject: [PATCH 24/43] chore(ui5-toolbar): fix OTB width --- packages/main/src/ToolbarItem.ts | 8 -------- packages/main/src/themes/ToolbarItem.css | 3 +++ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 707dc096a86a..441096e08bd2 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -161,14 +161,6 @@ class ToolbarItem extends UI5Element { return false; } - /** - * Returns if the item is default wrapper for certain component. - * @protected - */ - get isDefaultWrapper() { - return false; - } - get stableDomRef() { return this.getAttribute("stable-dom-ref") || `${this._id}-stable-dom-ref`; } diff --git a/packages/main/src/themes/ToolbarItem.css b/packages/main/src/themes/ToolbarItem.css index c86cfa392b3f..aeb1a4ceafc4 100644 --- a/packages/main/src/themes/ToolbarItem.css +++ b/packages/main/src/themes/ToolbarItem.css @@ -1,4 +1,7 @@ /* This style we need for the element inside Popover to not fire the click event on empty space */ +:host([is-overflowed]) { + display: inline-block; +} :host([expand-in-overflow]) ::slotted(*) { width: 100%; From f302f1510b6db3936785247f4c391fd44321afc7 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Mon, 24 Nov 2025 17:24:16 +0200 Subject: [PATCH 25/43] fix(ui5-toolbar): enhance overflow handling and add new toolbar item examples --- packages/main/src/themes/ToolbarItem.css | 2 +- packages/main/test/pages/ToolbarItems.html | 45 +++++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/packages/main/src/themes/ToolbarItem.css b/packages/main/src/themes/ToolbarItem.css index aeb1a4ceafc4..cbb74d5b483a 100644 --- a/packages/main/src/themes/ToolbarItem.css +++ b/packages/main/src/themes/ToolbarItem.css @@ -1,5 +1,5 @@ /* This style we need for the element inside Popover to not fire the click event on empty space */ -:host([is-overflowed]) { +:host([is-overflowed]:not([expand-in-overflow])) { display: inline-block; } diff --git a/packages/main/test/pages/ToolbarItems.html b/packages/main/test/pages/ToolbarItems.html index 7e51a1e3cfb5..725fbe7cc4f6 100644 --- a/packages/main/test/pages/ToolbarItems.html +++ b/packages/main/test/pages/ToolbarItems.html @@ -60,7 +60,7 @@ Simple text - + @@ -137,6 +137,49 @@ + Toolbar with Various Form Controls +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
From 82a3e17987ff0fa15ccd26a2f91c3aafbbe6dfde Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Mon, 1 Dec 2025 14:21:36 +0200 Subject: [PATCH 26/43] chore(ui5-toolbar): fix reviwe comments --- packages/main/src/Breadcrumbs.ts | 7 ++- packages/main/src/Button.ts | 5 +- packages/main/src/Toolbar.ts | 5 +- packages/main/src/ToolbarButton.ts | 8 +++ packages/main/src/ToolbarItem.ts | 63 ++++++++++++++++------ packages/main/src/ToolbarItemTemplate.tsx | 2 +- packages/main/src/ToolbarTemplate.tsx | 5 +- packages/main/test/pages/ToolbarItems.html | 16 +++--- 8 files changed, 79 insertions(+), 32 deletions(-) diff --git a/packages/main/src/Breadcrumbs.ts b/packages/main/src/Breadcrumbs.ts index bd23b8976380..0695b25f596d 100644 --- a/packages/main/src/Breadcrumbs.ts +++ b/packages/main/src/Breadcrumbs.ts @@ -24,6 +24,7 @@ import BreadcrumbsDesign from "./types/BreadcrumbsDesign.js"; import "./BreadcrumbsItem.js"; import type BreadcrumbsItem from "./BreadcrumbsItem.js"; import type BreadcrumbsSeparator from "./types/BreadcrumbsSeparator.js"; +import type { IOverflowToolbarItem } from "./ToolbarItem.js"; import { BREADCRUMB_ITEM_POS, @@ -84,6 +85,7 @@ type FocusAdaptor = ITabbable & { * - [End] - Navigates to the last item. * @constructor * @extends UI5Element + * @implements {IOverflowToolbarItem} * @public * @since 1.0.0-rc.15 */ @@ -109,7 +111,7 @@ type FocusAdaptor = ITabbable & { bubbles: true, cancelable: true, }) -class Breadcrumbs extends UI5Element { +class Breadcrumbs extends UI5Element implements IOverflowToolbarItem { eventDetails!: { "item-click": BreadcrumbsItemClickEventDetail, } @@ -642,6 +644,9 @@ class Breadcrumbs extends UI5Element { get _cancelButtonText() { return Breadcrumbs.i18nBundle.getText(BREADCRUMBS_CANCEL_BUTTON); } + get _selfOverflowed() { + return true; + } } Breadcrumbs.define(); diff --git a/packages/main/src/Button.ts b/packages/main/src/Button.ts index 2ca95ca49eeb..68c95a19603b 100644 --- a/packages/main/src/Button.ts +++ b/packages/main/src/Button.ts @@ -31,6 +31,7 @@ import ButtonType from "./types/ButtonType.js"; import ButtonBadgeDesign from "./types/ButtonBadgeDesign.js"; import type ButtonAccessibleRole from "./types/ButtonAccessibleRole.js"; import type ButtonBadge from "./ButtonBadge.js"; +import type { IOverflowToolbarItem } from "./ToolbarItem.js"; import ButtonTemplate from "./ButtonTemplate.js"; import { BUTTON_ARIA_TYPE_ACCEPT, @@ -132,7 +133,7 @@ type ButtonClickEventDetail = { bubbles: true, cancelable: true, }) -class Button extends UI5Element implements IButton { +class Button extends UI5Element implements IButton, IOverflowToolbarItem { eventDetails!: { "click": ButtonClickEventDetail, "active-state-change": void, @@ -692,7 +693,7 @@ class Button extends UI5Element implements IButton { // Method called by ui5-toolbar to inform about the existing toolbar wrapper get hasToolbarWrapper() { - return "ToolbarSelect"; + return "ToolbarButton"; } } diff --git a/packages/main/src/Toolbar.ts b/packages/main/src/Toolbar.ts index 411ab66e4ed8..77baae33be22 100644 --- a/packages/main/src/Toolbar.ts +++ b/packages/main/src/Toolbar.ts @@ -310,7 +310,7 @@ class Toolbar extends UI5Element { addItemsAdditionalProperties(item: ToolbarItem) { item.isOverflowed = this.overflowItems.map(overflowItem => overflowItem).indexOf(item) !== -1; const itemWrapper = this.shadowRoot!.querySelector(`#${item._individualSlot}`) as HTMLElement; - if (item.selfOverflowed && !item.isOverflowed && itemWrapper) { + if (item._selfOverflowed && !item.isOverflowed && itemWrapper) { // We need to set the max-width to the self-overflow element in order ot prevent it from taking all the available space, // since, unlike the other items, it is allowed to grow and shrink // We need to set the max-width to none and its position to absolute to allow the item to grow and measure its width, @@ -476,8 +476,7 @@ class Toolbar extends UI5Element { this.popoverOpen = false; } - onBeforeClose(e: UI5CustomEvent) { - e.preventDefault(); + onOverflowPopoverBeforeClose() { } onOverflowPopoverOpened() { diff --git a/packages/main/src/ToolbarButton.ts b/packages/main/src/ToolbarButton.ts index ba58ac531e08..4ee3fcc48eb0 100644 --- a/packages/main/src/ToolbarButton.ts +++ b/packages/main/src/ToolbarButton.ts @@ -194,6 +194,14 @@ class ToolbarButton extends ToolbarItem { return this.text; } + onClick(e: Event) { + e.stopImmediatePropagation(); + const prevented = !this.fireDecoratorEvent("click", { targetRef: e.target as HTMLElement }); + if (!prevented && !this.preventOverflowClosing) { + this.fireDecoratorEvent("close-overflow"); + } + } + /** * @override */ diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 441096e08bd2..bc206273b516 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -16,8 +16,10 @@ type ToolbarItemEventDetail = { targetRef: HTMLElement; } -type IOverflowToolbarItem = { +interface IOverflowToolbarItem extends HTMLElement { hasToolbarWrapper?: string | undefined; + eventsToCloseOverflow?: string[] | undefined; + _selfOverflowed?: boolean | undefined; } @event("close-overflow", { @@ -79,11 +81,11 @@ class ToolbarItem extends UI5Element { /** * Defines if the component, wrapped in the toolbar item, has his own overflow mechanism. * @default false - * @public + * @private * @since 2.17.0 */ @property({ type: Boolean }) - selfOverflowed: boolean = false; + _selfOverflowed: boolean = false; /** * Defines if the component, wrapped in the toolbar item, should be expanded in the overflow popover. @@ -98,14 +100,31 @@ class ToolbarItem extends UI5Element { _isRendering = true; _maxWidth = 0; + eventsToCloseOverflow: string[] = []; + + closeOverflowSet = { + "ui5-button": ["click"], + "ui5-select": ["change"], + "ui5-combobox": ["change"], + "ui5-multi-combobox": ["change"], + "ui5-date-picker": ["change"], + "ui5-switch": ["change"], + } onBeforeRendering(): void { + const slottedItem = this.getSlottedNodes("item")![0] as IOverflowToolbarItem; this.checkForWrapper(); + this.attachCloseOverflowHandlers(); + this._selfOverflowed = !!slottedItem?._selfOverflowed; } onAfterRendering(): void { this._isRendering = false; } + onExitDOM(): void { + this.detachCloseOverflowHandlers(); + } + /** * Wrapped component slot. * @public @@ -126,6 +145,31 @@ class ToolbarItem extends UI5Element { console.warn(`This UI5 web component has its predefined toolbar wrapper called ${this.getSlottedNodes("item")[0]!.hasToolbarWrapper}.`); } } + + // We want to close the overflow popover, when closing event is being executed + getClosingEvents(): string[] { + const ctor = this.getSlottedNodes("item")[0]?.constructor as typeof ToolbarItem; + const tagName = ctor?.getMetadata().getPureTag(); + return [...(this.closeOverflowSet[tagName as keyof typeof this.closeOverflowSet] || []), ...this.eventsToCloseOverflow]; + } + + attachCloseOverflowHandlers() { + const closingEvents = this.getClosingEvents(); + closingEvents.forEach(clEvent => { + this.addEventListener(clEvent, () => { + this.fireDecoratorEvent("close-overflow"); + }); + }); + } + + detachCloseOverflowHandlers() { + const closingEvents = this.getClosingEvents(); + closingEvents.forEach(clEvent => { + this.removeEventListener(clEvent, () => { + this.fireDecoratorEvent("close-overflow"); + }); + }); + } /** * Defines if the width of the item should be ignored in calculating the whole width of the toolbar * @protected @@ -173,23 +217,12 @@ class ToolbarItem extends UI5Element { }, }; } - - /** - * Handles the click event on the toolbar item. - * If `preventOverflowClosing` is false, it will fire a "close-overflow" event. - */ - onClick(e: Event) { - e.stopImmediatePropagation(); - const prevented = !(this.fireDecoratorEvent("click", { targetRef: e.target as HTMLElement }) || e.defaultPrevented); - if (!prevented && !this.preventOverflowClosing) { - this.fireDecoratorEvent("close-overflow"); - } - } } export type { IEventOptions, ToolbarItemEventDetail, + IOverflowToolbarItem, }; ToolbarItem.define(); diff --git a/packages/main/src/ToolbarItemTemplate.tsx b/packages/main/src/ToolbarItemTemplate.tsx index 12126b0b4f48..123b8d6f8bb9 100644 --- a/packages/main/src/ToolbarItemTemplate.tsx +++ b/packages/main/src/ToolbarItemTemplate.tsx @@ -2,7 +2,7 @@ import type ToolbarItem from "./ToolbarItem.js"; export default function ToolbarItemTemplate(this: ToolbarItem) { return ( -
+
); diff --git a/packages/main/src/ToolbarTemplate.tsx b/packages/main/src/ToolbarTemplate.tsx index 0b5daa7c8d44..3309e0d43a6b 100644 --- a/packages/main/src/ToolbarTemplate.tsx +++ b/packages/main/src/ToolbarTemplate.tsx @@ -14,7 +14,7 @@ export default function ToolbarTemplate(this: Toolbar) { aria-label={this.accInfo.root.accessibleName} > {this.standardItems.map(item => { - const selfOverflowClass = item.selfOverflowed ? "ui5-tb-self-overflow" : ""; + const selfOverflowClass = item._selfOverflowed ? "ui5-tb-self-overflow" : ""; const classes = `ui5-tb-item ${selfOverflowClass}`; return (
@@ -44,6 +44,7 @@ export default function ToolbarTemplate(this: Toolbar) { horizontalAlign="End" onClose={this.onOverflowPopoverClosed} onOpen={this.onOverflowPopoverOpened} + onBeforeClose={this.onOverflowPopoverBeforeClose} accessibleName={this.accInfo.popover.accessibleName} hideArrow={true} > @@ -52,7 +53,7 @@ export default function ToolbarTemplate(this: Toolbar) { }}> {this.overflowItems.map(item => { const separatorClass = item.isSeparator ? " ui5-tb-separator ui5-tb-separator-in-overflow" : ""; - const selfOverflowClass = item.selfOverflowed ? " ui5-tb-popover-self-overflow" : ""; + const selfOverflowClass = item._selfOverflowed ? " ui5-tb-popover-self-overflow" : ""; const classes = `ui5-tb-popover-item${separatorClass}${selfOverflowClass}`; return (
diff --git a/packages/main/test/pages/ToolbarItems.html b/packages/main/test/pages/ToolbarItems.html index 725fbe7cc4f6..5499b262b6ab 100644 --- a/packages/main/test/pages/ToolbarItems.html +++ b/packages/main/test/pages/ToolbarItems.html @@ -91,7 +91,7 @@
- + Link1 Link2 @@ -122,7 +122,7 @@ - + Link1 Link2 @@ -142,26 +142,26 @@
- + - + - + - + @@ -170,12 +170,12 @@ - + - + From b2443a327133ea22ca90fd1bccf14d112e801fc8 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Tue, 2 Dec 2025 09:27:39 +0200 Subject: [PATCH 27/43] chore(ui5-toolbar): fix review comments --- packages/main/src/Toolbar.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/main/src/Toolbar.ts b/packages/main/src/Toolbar.ts index 77baae33be22..87f0c5893bc7 100644 --- a/packages/main/src/Toolbar.ts +++ b/packages/main/src/Toolbar.ts @@ -5,7 +5,6 @@ import property from "@ui5/webcomponents-base/dist/decorators/property.js"; import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js"; import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js"; -import type { UI5CustomEvent } from "@ui5/webcomponents-base"; import { renderFinished } from "@ui5/webcomponents-base/dist/Render.js"; import ResizeHandler from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js"; import type { ResizeObserverCallback } from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js"; From d514738904f7bc0685eff72626f17d1d490d0540 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Tue, 2 Dec 2025 14:41:05 +0200 Subject: [PATCH 28/43] fix(ui5-toolbar): fix tests --- packages/main/src/ToolbarItem.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index bc206273b516..1fcbad9a3db5 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -149,16 +149,18 @@ class ToolbarItem extends UI5Element { // We want to close the overflow popover, when closing event is being executed getClosingEvents(): string[] { const ctor = this.getSlottedNodes("item")[0]?.constructor as typeof ToolbarItem; - const tagName = ctor?.getMetadata().getPureTag(); + const tagName = ctor?.getMetadata ? ctor.getMetadata().getPureTag() : this.getSlottedNodes("item")[0]?.tagName; return [...(this.closeOverflowSet[tagName as keyof typeof this.closeOverflowSet] || []), ...this.eventsToCloseOverflow]; } attachCloseOverflowHandlers() { const closingEvents = this.getClosingEvents(); closingEvents.forEach(clEvent => { - this.addEventListener(clEvent, () => { - this.fireDecoratorEvent("close-overflow"); - }); + if (!this.preventOverflowClosing) { + this.addEventListener(clEvent, () => { + this.fireDecoratorEvent("close-overflow"); + }); + } }); } From a5adb65449653e7dde2915979526d0cf96be2b96 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Tue, 9 Dec 2025 15:28:04 +0200 Subject: [PATCH 29/43] chore(ui5-toolbar): review comments --- packages/main/src/Button.ts | 2 +- packages/main/src/Select.ts | 4 +++- packages/main/src/ToolbarItem.ts | 19 ++++++++++--------- packages/main/test/pages/Toolbar.html | 2 -- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/main/src/Button.ts b/packages/main/src/Button.ts index 7c6311ad8c76..85cc4f0410d9 100644 --- a/packages/main/src/Button.ts +++ b/packages/main/src/Button.ts @@ -98,7 +98,7 @@ type ButtonClickEventDetail = { * @csspart endIcon - Used to style the end icon in the native button element * @constructor * @extends UI5Element - * @implements { IButton } + * @implements { IButton, IOverflowToolbarItem } * @public */ @customElement({ diff --git a/packages/main/src/Select.ts b/packages/main/src/Select.ts index b51d3dcb3c5b..3e6463b94a80 100644 --- a/packages/main/src/Select.ts +++ b/packages/main/src/Select.ts @@ -39,6 +39,7 @@ import InvisibleMessageMode from "@ui5/webcomponents-base/dist/types/InvisibleMe import { getScopedVarName } from "@ui5/webcomponents-base/dist/CustomElementsScope.js"; import type { IFormInputElement } from "@ui5/webcomponents-base/dist/features/InputElementsFormSupport.js"; import List from "./List.js"; +import type { IOverflowToolbarItem } from "./ToolbarItem.js"; import type { ListItemClickEventDetail } from "./List.js"; import { VALUE_STATE_SUCCESS, @@ -143,6 +144,7 @@ type SelectLiveChangeEventDetail = { * `import "@ui5/webcomponents/dist/OptionCustom";` * @constructor * @extends UI5Element + * @implements {IOverflowToolbarItem} * @public * @csspart popover - Used to style the popover element * @since 0.8.0 @@ -215,7 +217,7 @@ type SelectLiveChangeEventDetail = { bubbles: true, }) -class Select extends UI5Element implements IFormInputElement { +class Select extends UI5Element implements IFormInputElement, IOverflowToolbarItem { eventDetails!: { "change": SelectChangeEventDetail, "live-change": SelectLiveChangeEventDetail, diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 1fcbad9a3db5..cc5a98cd1af3 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -99,6 +99,7 @@ class ToolbarItem extends UI5Element { _isRendering = true; _maxWidth = 0; + fireCloseOverflowRef = this.fireCloseOverflow.bind(this); eventsToCloseOverflow: string[] = []; @@ -111,7 +112,7 @@ class ToolbarItem extends UI5Element { "ui5-switch": ["change"], } onBeforeRendering(): void { - const slottedItem = this.getSlottedNodes("item")![0] as IOverflowToolbarItem; + const slottedItem = this.getSlottedNodes("item")[0] as IOverflowToolbarItem; this.checkForWrapper(); this.attachCloseOverflowHandlers(); this._selfOverflowed = !!slottedItem?._selfOverflowed; @@ -140,9 +141,9 @@ class ToolbarItem extends UI5Element { checkForWrapper() { if (this.tagName === "UI5-TOOLBAR-ITEM" && this.getSlottedNodes("item").length - && this.getSlottedNodes("item")[0]!.hasToolbarWrapper) { + && this.getSlottedNodes("item")[0].hasToolbarWrapper) { // eslint-disable-next-line no-console - console.warn(`This UI5 web component has its predefined toolbar wrapper called ${this.getSlottedNodes("item")[0]!.hasToolbarWrapper}.`); + console.warn(`This UI5 web component has its predefined toolbar wrapper called ${this.getSlottedNodes("item")[0].hasToolbarWrapper}.`); } } @@ -157,9 +158,7 @@ class ToolbarItem extends UI5Element { const closingEvents = this.getClosingEvents(); closingEvents.forEach(clEvent => { if (!this.preventOverflowClosing) { - this.addEventListener(clEvent, () => { - this.fireDecoratorEvent("close-overflow"); - }); + this.addEventListener(clEvent, this.fireCloseOverflowRef); } }); } @@ -167,11 +166,13 @@ class ToolbarItem extends UI5Element { detachCloseOverflowHandlers() { const closingEvents = this.getClosingEvents(); closingEvents.forEach(clEvent => { - this.removeEventListener(clEvent, () => { - this.fireDecoratorEvent("close-overflow"); - }); + this.removeEventListener(clEvent, this.fireCloseOverflowRef); }); } + + fireCloseOverflow() { + this.fireDecoratorEvent("close-overflow"); + } /** * Defines if the width of the item should be ignored in calculating the whole width of the toolbar * @protected diff --git a/packages/main/test/pages/Toolbar.html b/packages/main/test/pages/Toolbar.html index 013852c9031c..9e26be4475fb 100644 --- a/packages/main/test/pages/Toolbar.html +++ b/packages/main/test/pages/Toolbar.html @@ -1,5 +1,3 @@ - - Call me later From 7ca80af99ee7db2ae2392546476eece622f09d72 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Tue, 6 Jan 2026 19:09:49 +0200 Subject: [PATCH 30/43] fix(ui5-toolbar): migrate ToolbarItem to UI5 Web Component --- packages/main/src/Toolbar.ts | 5 +---- packages/main/src/ToolbarButton.ts | 2 ++ packages/main/src/ToolbarItem.ts | 14 +++++++------- packages/main/src/ToolbarItemTemplate.tsx | 4 +--- packages/main/src/ToolbarSelect.ts | 1 + packages/main/src/ToolbarSeparator.ts | 1 + packages/main/src/ToolbarSpacer.ts | 2 ++ packages/main/src/ToolbarTemplate.tsx | 8 ++++---- 8 files changed, 19 insertions(+), 18 deletions(-) diff --git a/packages/main/src/Toolbar.ts b/packages/main/src/Toolbar.ts index 87f0c5893bc7..143804aa2473 100644 --- a/packages/main/src/Toolbar.ts +++ b/packages/main/src/Toolbar.ts @@ -307,7 +307,7 @@ class Toolbar extends UI5Element { } addItemsAdditionalProperties(item: ToolbarItem) { - item.isOverflowed = this.overflowItems.map(overflowItem => overflowItem).indexOf(item) !== -1; + item.isOverflowed = this.overflowItems.indexOf(item) !== -1; const itemWrapper = this.shadowRoot!.querySelector(`#${item._individualSlot}`) as HTMLElement; if (item._selfOverflowed && !item.isOverflowed && itemWrapper) { // We need to set the max-width to the self-overflow element in order ot prevent it from taking all the available space, @@ -475,9 +475,6 @@ class Toolbar extends UI5Element { this.popoverOpen = false; } - onOverflowPopoverBeforeClose() { - } - onOverflowPopoverOpened() { this.popoverOpen = true; } diff --git a/packages/main/src/ToolbarButton.ts b/packages/main/src/ToolbarButton.ts index 4ee3fcc48eb0..e8d315eb1dba 100644 --- a/packages/main/src/ToolbarButton.ts +++ b/packages/main/src/ToolbarButton.ts @@ -170,6 +170,8 @@ class ToolbarButton extends ToolbarItem { @property() width?: string; + _kind = "ToolbarButton"; + get styles() { return { width: this.width, diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index cc5a98cd1af3..a8a6eba14977 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -82,7 +82,7 @@ class ToolbarItem extends UI5Element { * Defines if the component, wrapped in the toolbar item, has his own overflow mechanism. * @default false * @private - * @since 2.17.0 + * @since 2.19.0 */ @property({ type: Boolean }) _selfOverflowed: boolean = false; @@ -91,7 +91,7 @@ class ToolbarItem extends UI5Element { * Defines if the component, wrapped in the toolbar item, should be expanded in the overflow popover. * @default false * @public - * @since 2.17.0 + * @since 2.19.0 */ @property({ type: Boolean }) @@ -100,6 +100,7 @@ class ToolbarItem extends UI5Element { _isRendering = true; _maxWidth = 0; fireCloseOverflowRef = this.fireCloseOverflow.bind(this); + _kind = "ToolbarItem"; eventsToCloseOverflow: string[] = []; @@ -129,19 +130,18 @@ class ToolbarItem extends UI5Element { /** * Wrapped component slot. * @public - * @since 2.17.0 + * @since 2.19.0 */ @slot({ "default": true, type: HTMLElement, invalidateOnChildChange: true, }) - item!: HTMLElement | undefined; + item!: IOverflowToolbarItem[]; // here // Method called by ui5-toolbar to inform about the existing toolbar wrapper checkForWrapper() { - if (this.tagName === "UI5-TOOLBAR-ITEM" - && this.getSlottedNodes("item").length - && this.getSlottedNodes("item")[0].hasToolbarWrapper) { + if (this._kind === "ToolbarItem" + && this.item?.[0].hasToolbarWrapper) { // eslint-disable-next-line no-console console.warn(`This UI5 web component has its predefined toolbar wrapper called ${this.getSlottedNodes("item")[0].hasToolbarWrapper}.`); } diff --git a/packages/main/src/ToolbarItemTemplate.tsx b/packages/main/src/ToolbarItemTemplate.tsx index 123b8d6f8bb9..60bd9f2637b9 100644 --- a/packages/main/src/ToolbarItemTemplate.tsx +++ b/packages/main/src/ToolbarItemTemplate.tsx @@ -2,8 +2,6 @@ import type ToolbarItem from "./ToolbarItem.js"; export default function ToolbarItemTemplate(this: ToolbarItem) { return ( -
- -
+ ); } diff --git a/packages/main/src/ToolbarSelect.ts b/packages/main/src/ToolbarSelect.ts index ff3011be84ac..db32447c8a84 100644 --- a/packages/main/src/ToolbarSelect.ts +++ b/packages/main/src/ToolbarSelect.ts @@ -163,6 +163,7 @@ class ToolbarSelect extends ToolbarItem { // Internal value storage, in case the composite select is not rendered on the the assignment happens _value: string = ""; + _kind = "ToolbarSelect"; onClick(e: Event): void { e.stopImmediatePropagation(); diff --git a/packages/main/src/ToolbarSeparator.ts b/packages/main/src/ToolbarSeparator.ts index d7279d6fecec..8b6326c3508e 100644 --- a/packages/main/src/ToolbarSeparator.ts +++ b/packages/main/src/ToolbarSeparator.ts @@ -38,6 +38,7 @@ class ToolbarSeparator extends ToolbarItem { get isInteractive() { return false; } + _kind = "ToolbarSeparator"; } ToolbarSeparator.define(); diff --git a/packages/main/src/ToolbarSpacer.ts b/packages/main/src/ToolbarSpacer.ts index 40b0cff7aa97..1e30c0e7c8dc 100644 --- a/packages/main/src/ToolbarSpacer.ts +++ b/packages/main/src/ToolbarSpacer.ts @@ -48,6 +48,8 @@ class ToolbarSpacer extends ToolbarItem { get isInteractive() { return false; } + + _kind = "ToolbarSpacer"; } ToolbarSpacer.define(); diff --git a/packages/main/src/ToolbarTemplate.tsx b/packages/main/src/ToolbarTemplate.tsx index 3309e0d43a6b..9e406c798f84 100644 --- a/packages/main/src/ToolbarTemplate.tsx +++ b/packages/main/src/ToolbarTemplate.tsx @@ -14,10 +14,11 @@ export default function ToolbarTemplate(this: Toolbar) { aria-label={this.accInfo.root.accessibleName} > {this.standardItems.map(item => { - const selfOverflowClass = item._selfOverflowed ? "ui5-tb-self-overflow" : ""; - const classes = `ui5-tb-item ${selfOverflowClass}`; return ( -
+
); @@ -44,7 +45,6 @@ export default function ToolbarTemplate(this: Toolbar) { horizontalAlign="End" onClose={this.onOverflowPopoverClosed} onOpen={this.onOverflowPopoverOpened} - onBeforeClose={this.onOverflowPopoverBeforeClose} accessibleName={this.accInfo.popover.accessibleName} hideArrow={true} > From fff9c29a8b41972b0ed16c5e1b2c763350805e2d Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Tue, 6 Jan 2026 19:22:33 +0200 Subject: [PATCH 31/43] fix(ui5-toolbar-item): item wrapper introduced --- packages/main/src/ToolbarItem.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index a8a6eba14977..1a313849d3ee 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -141,7 +141,7 @@ class ToolbarItem extends UI5Element { // Method called by ui5-toolbar to inform about the existing toolbar wrapper checkForWrapper() { if (this._kind === "ToolbarItem" - && this.item?.[0].hasToolbarWrapper) { + && this.item?.[0]?.hasToolbarWrapper) { // eslint-disable-next-line no-console console.warn(`This UI5 web component has its predefined toolbar wrapper called ${this.getSlottedNodes("item")[0].hasToolbarWrapper}.`); } From a083b755eeda622cc4e47ec6f9986a2f9846bd2d Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Wed, 7 Jan 2026 19:29:13 +0200 Subject: [PATCH 32/43] fix(ui5-toolbar-item): general wrapper introduced --- packages/main/src/Button.ts | 8 +------- packages/main/src/Select.ts | 8 +------- packages/main/src/ToolbarItem.ts | 12 +++++++++--- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/packages/main/src/Button.ts b/packages/main/src/Button.ts index a0d6d23e22c2..d60ba9307016 100644 --- a/packages/main/src/Button.ts +++ b/packages/main/src/Button.ts @@ -31,7 +31,6 @@ import ButtonType from "./types/ButtonType.js"; import ButtonBadgeDesign from "./types/ButtonBadgeDesign.js"; import ButtonAccessibleRole from "./types/ButtonAccessibleRole.js"; import type ButtonBadge from "./ButtonBadge.js"; -import type { IOverflowToolbarItem } from "./ToolbarItem.js"; import ButtonTemplate from "./ButtonTemplate.js"; import { BUTTON_ARIA_TYPE_ACCEPT, @@ -135,7 +134,7 @@ type ButtonClickEventDetail = { bubbles: true, cancelable: true, }) -class Button extends UI5Element implements IButton, IOverflowToolbarItem { +class Button extends UI5Element implements IButton { eventDetails!: { "click": ButtonClickEventDetail, "active-state-change": void, @@ -714,11 +713,6 @@ class Button extends UI5Element implements IButton, IOverflowToolbarItem { get shouldRenderBadge() { return !!this.badge.length && (!!this.badge[0].text.length || this.badge[0].design === ButtonBadgeDesign.AttentionDot); } - - // Method called by ui5-toolbar to inform about the existing toolbar wrapper - get hasToolbarWrapper() { - return "ToolbarButton"; - } } Button.define(); diff --git a/packages/main/src/Select.ts b/packages/main/src/Select.ts index 496c3d492af5..ca545bc87d1f 100644 --- a/packages/main/src/Select.ts +++ b/packages/main/src/Select.ts @@ -39,7 +39,6 @@ import InvisibleMessageMode from "@ui5/webcomponents-base/dist/types/InvisibleMe import { getScopedVarName } from "@ui5/webcomponents-base/dist/CustomElementsScope.js"; import type { IFormInputElement } from "@ui5/webcomponents-base/dist/features/InputElementsFormSupport.js"; import List from "./List.js"; -import type { IOverflowToolbarItem } from "./ToolbarItem.js"; import type { ListItemClickEventDetail } from "./List.js"; import { VALUE_STATE_SUCCESS, @@ -217,7 +216,7 @@ type SelectLiveChangeEventDetail = { bubbles: true, }) -class Select extends UI5Element implements IFormInputElement, IOverflowToolbarItem { +class Select extends UI5Element implements IFormInputElement { eventDetails!: { "change": SelectChangeEventDetail, "live-change": SelectLiveChangeEventDetail, @@ -1195,11 +1194,6 @@ class Select extends UI5Element implements IFormInputElement, IOverflowToolbarIt _getPopover() { return this.shadowRoot!.querySelector("[ui5-popover]"); } - - // Method called by ui5-toolbar to inform about the existing toolbar wrapper - get hasToolbarWrapper() { - return "ToolbarSelect"; - } } Select.define(); diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 1a313849d3ee..fe2f666adb2e 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -17,7 +17,6 @@ type ToolbarItemEventDetail = { } interface IOverflowToolbarItem extends HTMLElement { - hasToolbarWrapper?: string | undefined; eventsToCloseOverflow?: string[] | undefined; _selfOverflowed?: boolean | undefined; } @@ -112,6 +111,12 @@ class ToolbarItem extends UI5Element { "ui5-date-picker": ["change"], "ui5-switch": ["change"], } + + predefinedWrapperSet = { + "ui5-button": "ToolbarButton", + "ui5-select": "ToolbarSelect", + } + onBeforeRendering(): void { const slottedItem = this.getSlottedNodes("item")[0] as IOverflowToolbarItem; this.checkForWrapper(); @@ -140,10 +145,11 @@ class ToolbarItem extends UI5Element { // Method called by ui5-toolbar to inform about the existing toolbar wrapper checkForWrapper() { + const tagName = this.item?.[0]?.localName as keyof typeof this.predefinedWrapperSet; if (this._kind === "ToolbarItem" - && this.item?.[0]?.hasToolbarWrapper) { + && this.predefinedWrapperSet[tagName]) { // eslint-disable-next-line no-console - console.warn(`This UI5 web component has its predefined toolbar wrapper called ${this.getSlottedNodes("item")[0].hasToolbarWrapper}.`); + console.warn(`This UI5 web component has its predefined toolbar wrapper called ${this.predefinedWrapperSet[tagName]}.`); } } From fc276054b8852f28c5f5925d1587cdda7294cb1e Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Thu, 8 Jan 2026 09:48:21 +0200 Subject: [PATCH 33/43] fix(ui5-toolbar-item): remove code from components that is not needed --- packages/main/src/Button.ts | 2 +- packages/main/src/Select.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/main/src/Button.ts b/packages/main/src/Button.ts index d60ba9307016..9dc49b0bbb10 100644 --- a/packages/main/src/Button.ts +++ b/packages/main/src/Button.ts @@ -97,7 +97,7 @@ type ButtonClickEventDetail = { * @csspart endIcon - Used to style the end icon in the native button element * @constructor * @extends UI5Element - * @implements { IButton, IOverflowToolbarItem } + * @implements { IButton } * @public */ @customElement({ diff --git a/packages/main/src/Select.ts b/packages/main/src/Select.ts index ca545bc87d1f..301a52b4254c 100644 --- a/packages/main/src/Select.ts +++ b/packages/main/src/Select.ts @@ -143,7 +143,6 @@ type SelectLiveChangeEventDetail = { * `import "@ui5/webcomponents/dist/OptionCustom";` * @constructor * @extends UI5Element - * @implements {IOverflowToolbarItem} * @public * @csspart popover - Used to style the popover element * @since 0.8.0 From 679d062f726100eb159b59ec5dd1ae4d904f2099 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Mon, 19 Jan 2026 09:59:54 +0200 Subject: [PATCH 34/43] fix(ui5-toolbar-item): code-review comments addressed --- packages/main/src/Breadcrumbs.ts | 2 +- packages/main/src/Toolbar.ts | 2 +- packages/main/src/ToolbarButton.ts | 2 -- packages/main/src/ToolbarItem.ts | 39 ++++++++++------------ packages/main/src/ToolbarSelect.ts | 1 - packages/main/src/ToolbarSeparator.ts | 1 - packages/main/src/ToolbarSpacer.ts | 2 -- packages/main/src/ToolbarTemplate.tsx | 11 +++--- packages/main/src/themes/ToolbarItem.css | 6 ++-- packages/main/test/pages/ToolbarItems.html | 10 +++--- 10 files changed, 34 insertions(+), 42 deletions(-) diff --git a/packages/main/src/Breadcrumbs.ts b/packages/main/src/Breadcrumbs.ts index 0695b25f596d..e85c88cae160 100644 --- a/packages/main/src/Breadcrumbs.ts +++ b/packages/main/src/Breadcrumbs.ts @@ -644,7 +644,7 @@ class Breadcrumbs extends UI5Element implements IOverflowToolbarItem { get _cancelButtonText() { return Breadcrumbs.i18nBundle.getText(BREADCRUMBS_CANCEL_BUTTON); } - get _selfOverflowed() { + get hasOverflow() { return true; } } diff --git a/packages/main/src/Toolbar.ts b/packages/main/src/Toolbar.ts index 143804aa2473..2b86041240ba 100644 --- a/packages/main/src/Toolbar.ts +++ b/packages/main/src/Toolbar.ts @@ -309,7 +309,7 @@ class Toolbar extends UI5Element { addItemsAdditionalProperties(item: ToolbarItem) { item.isOverflowed = this.overflowItems.indexOf(item) !== -1; const itemWrapper = this.shadowRoot!.querySelector(`#${item._individualSlot}`) as HTMLElement; - if (item._selfOverflowed && !item.isOverflowed && itemWrapper) { + if (item.hasOverflow && !item.isOverflowed && itemWrapper) { // We need to set the max-width to the self-overflow element in order ot prevent it from taking all the available space, // since, unlike the other items, it is allowed to grow and shrink // We need to set the max-width to none and its position to absolute to allow the item to grow and measure its width, diff --git a/packages/main/src/ToolbarButton.ts b/packages/main/src/ToolbarButton.ts index e8d315eb1dba..4ee3fcc48eb0 100644 --- a/packages/main/src/ToolbarButton.ts +++ b/packages/main/src/ToolbarButton.ts @@ -170,8 +170,6 @@ class ToolbarButton extends ToolbarItem { @property() width?: string; - _kind = "ToolbarButton"; - get styles() { return { width: this.width, diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index fe2f666adb2e..4c3286326676 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -18,7 +18,7 @@ type ToolbarItemEventDetail = { interface IOverflowToolbarItem extends HTMLElement { eventsToCloseOverflow?: string[] | undefined; - _selfOverflowed?: boolean | undefined; + hasOverflow?: boolean | undefined; } @event("close-overflow", { @@ -77,15 +77,6 @@ class ToolbarItem extends UI5Element { @property({ type: Boolean }) isOverflowed: boolean = false; - /** - * Defines if the component, wrapped in the toolbar item, has his own overflow mechanism. - * @default false - * @private - * @since 2.19.0 - */ - @property({ type: Boolean }) - _selfOverflowed: boolean = false; - /** * Defines if the component, wrapped in the toolbar item, should be expanded in the overflow popover. * @default false @@ -94,14 +85,13 @@ class ToolbarItem extends UI5Element { */ @property({ type: Boolean }) - expandInOverflow: boolean = false; + shrinkInOverflow: boolean = false; _isRendering = true; _maxWidth = 0; fireCloseOverflowRef = this.fireCloseOverflow.bind(this); - _kind = "ToolbarItem"; - eventsToCloseOverflow: string[] = []; + overflowCloseEvents: string[] = []; closeOverflowSet = { "ui5-button": ["click"], @@ -118,10 +108,8 @@ class ToolbarItem extends UI5Element { } onBeforeRendering(): void { - const slottedItem = this.getSlottedNodes("item")[0] as IOverflowToolbarItem; this.checkForWrapper(); this.attachCloseOverflowHandlers(); - this._selfOverflowed = !!slottedItem?._selfOverflowed; } onAfterRendering(): void { @@ -141,12 +129,14 @@ class ToolbarItem extends UI5Element { @slot({ "default": true, type: HTMLElement, invalidateOnChildChange: true, }) - item!: IOverflowToolbarItem[]; // here + item!: IOverflowToolbarItem[]; // Method called by ui5-toolbar to inform about the existing toolbar wrapper checkForWrapper() { - const tagName = this.item?.[0]?.localName as keyof typeof this.predefinedWrapperSet; - if (this._kind === "ToolbarItem" + const tagName = this.itemTagName as keyof typeof this.predefinedWrapperSet; + const ctor = this.constructor as typeof UI5Element; + const wrapperName = ctor?.getMetadata ? ctor.getMetadata().getPureTag() : this.tagName; + if (wrapperName === "ToolbarItem" && this.predefinedWrapperSet[tagName]) { // eslint-disable-next-line no-console console.warn(`This UI5 web component has its predefined toolbar wrapper called ${this.predefinedWrapperSet[tagName]}.`); @@ -155,9 +145,7 @@ class ToolbarItem extends UI5Element { // We want to close the overflow popover, when closing event is being executed getClosingEvents(): string[] { - const ctor = this.getSlottedNodes("item")[0]?.constructor as typeof ToolbarItem; - const tagName = ctor?.getMetadata ? ctor.getMetadata().getPureTag() : this.getSlottedNodes("item")[0]?.tagName; - return [...(this.closeOverflowSet[tagName as keyof typeof this.closeOverflowSet] || []), ...this.eventsToCloseOverflow]; + return [...(this.closeOverflowSet[this.itemTagName as keyof typeof this.closeOverflowSet] || []), ...this.overflowCloseEvents]; } attachCloseOverflowHandlers() { @@ -206,6 +194,15 @@ class ToolbarItem extends UI5Element { return true; } + get itemTagName() { + const ctor = this.getSlottedNodes("item")[0]?.constructor as typeof UI5Element; + return ctor?.getMetadata ? ctor.getMetadata().getPureTag() : this.getSlottedNodes("item")[0]?.tagName; + } + + get hasOverflow(): boolean { + return this.item[0].hasOverflow ?? false; + } + /** * Returns if the item is separator. * @protected diff --git a/packages/main/src/ToolbarSelect.ts b/packages/main/src/ToolbarSelect.ts index db32447c8a84..ff3011be84ac 100644 --- a/packages/main/src/ToolbarSelect.ts +++ b/packages/main/src/ToolbarSelect.ts @@ -163,7 +163,6 @@ class ToolbarSelect extends ToolbarItem { // Internal value storage, in case the composite select is not rendered on the the assignment happens _value: string = ""; - _kind = "ToolbarSelect"; onClick(e: Event): void { e.stopImmediatePropagation(); diff --git a/packages/main/src/ToolbarSeparator.ts b/packages/main/src/ToolbarSeparator.ts index 8b6326c3508e..d7279d6fecec 100644 --- a/packages/main/src/ToolbarSeparator.ts +++ b/packages/main/src/ToolbarSeparator.ts @@ -38,7 +38,6 @@ class ToolbarSeparator extends ToolbarItem { get isInteractive() { return false; } - _kind = "ToolbarSeparator"; } ToolbarSeparator.define(); diff --git a/packages/main/src/ToolbarSpacer.ts b/packages/main/src/ToolbarSpacer.ts index 1e30c0e7c8dc..40b0cff7aa97 100644 --- a/packages/main/src/ToolbarSpacer.ts +++ b/packages/main/src/ToolbarSpacer.ts @@ -48,8 +48,6 @@ class ToolbarSpacer extends ToolbarItem { get isInteractive() { return false; } - - _kind = "ToolbarSpacer"; } ToolbarSpacer.define(); diff --git a/packages/main/src/ToolbarTemplate.tsx b/packages/main/src/ToolbarTemplate.tsx index 9e406c798f84..232bb74a172e 100644 --- a/packages/main/src/ToolbarTemplate.tsx +++ b/packages/main/src/ToolbarTemplate.tsx @@ -17,7 +17,7 @@ export default function ToolbarTemplate(this: Toolbar) { return (
@@ -52,11 +52,12 @@ export default function ToolbarTemplate(this: Toolbar) { "ui5-overflow-list": true }}> {this.overflowItems.map(item => { - const separatorClass = item.isSeparator ? " ui5-tb-separator ui5-tb-separator-in-overflow" : ""; - const selfOverflowClass = item._selfOverflowed ? " ui5-tb-popover-self-overflow" : ""; - const classes = `ui5-tb-popover-item${separatorClass}${selfOverflowClass}`; return ( -
+
); diff --git a/packages/main/src/themes/ToolbarItem.css b/packages/main/src/themes/ToolbarItem.css index cbb74d5b483a..c869a5a617ce 100644 --- a/packages/main/src/themes/ToolbarItem.css +++ b/packages/main/src/themes/ToolbarItem.css @@ -1,8 +1,8 @@ -/* This style we need for the element inside Popover to not fire the click event on empty space */ -:host([is-overflowed]:not([expand-in-overflow])) { +/* This style we need for the element inside Popover with shrinikInOverflow property to not fire the click event on empty space */ +:host([is-overflowed][shrink-in-overflow]) { display: inline-block; } -:host([expand-in-overflow]) ::slotted(*) { +:host([is-overflowed]) ::slotted(*) { width: 100%; } \ No newline at end of file diff --git a/packages/main/test/pages/ToolbarItems.html b/packages/main/test/pages/ToolbarItems.html index 5499b262b6ab..1dee29a62632 100644 --- a/packages/main/test/pages/ToolbarItems.html +++ b/packages/main/test/pages/ToolbarItems.html @@ -41,8 +41,8 @@
- Left 1 (long) - Left 2 + Left 1 (long) + Left 2 Left 3 Left 4 Mid 1 @@ -156,12 +156,12 @@ - + - + @@ -175,7 +175,7 @@ - + From 0bc2aa08cc18b02df0629f8640043998308ec67a Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Mon, 19 Jan 2026 10:11:41 +0200 Subject: [PATCH 35/43] fix(ui5-toolbar-item): code-review comments addressed --- packages/main/src/ToolbarItem.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 4c3286326676..d9957ba9de07 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -200,7 +200,7 @@ class ToolbarItem extends UI5Element { } get hasOverflow(): boolean { - return this.item[0].hasOverflow ?? false; + return this.item[0].hasOverflow! ?? false; } /** From b12a993b5cb20735891d947d7adfb635df0f4a28 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Mon, 19 Jan 2026 10:19:44 +0200 Subject: [PATCH 36/43] fix(ui5-toolbar-item): correct code issue --- packages/main/src/ToolbarItem.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index d9957ba9de07..bf06d6d2ab5a 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -200,7 +200,7 @@ class ToolbarItem extends UI5Element { } get hasOverflow(): boolean { - return this.item[0].hasOverflow! ?? false; + return this.item[0]?.hasOverflow ?? false; } /** From ba4b1883719199cff80835d3039e4bd5e952445e Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Mon, 19 Jan 2026 10:34:22 +0200 Subject: [PATCH 37/43] fix(ui5-toolbar-item): checkForWrapper fixed --- packages/main/src/ToolbarItem.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index bf06d6d2ab5a..5d1e40b69424 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -136,7 +136,7 @@ class ToolbarItem extends UI5Element { const tagName = this.itemTagName as keyof typeof this.predefinedWrapperSet; const ctor = this.constructor as typeof UI5Element; const wrapperName = ctor?.getMetadata ? ctor.getMetadata().getPureTag() : this.tagName; - if (wrapperName === "ToolbarItem" + if (wrapperName === "ui5-toolbar-item" && this.predefinedWrapperSet[tagName]) { // eslint-disable-next-line no-console console.warn(`This UI5 web component has its predefined toolbar wrapper called ${this.predefinedWrapperSet[tagName]}.`); From b290e9c99bf9fe9a5809b85989766fc0c667dcda Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Fri, 30 Jan 2026 13:25:27 +0200 Subject: [PATCH 38/43] fix(ui5-toolbar-item): code reviews feedback implementation --- packages/main/cypress/specs/Toolbar.cy.tsx | 138 +--- .../main/cypress/specs/ToolbarItem.cy.tsx | 761 ++++++++++++++++++ packages/main/src/ToolbarItem.ts | 32 +- packages/main/src/themes/ToolbarItem.css | 4 +- packages/main/test/pages/ToolbarItems.html | 18 +- .../main/Toolbar/Toolbar.mdx | 5 + .../main/Toolbar/ToolbarItem.mdx | 7 + .../main/Toolbar/ToolbarItem/ToolbarItem.md | 4 + .../_samples/main/Toolbar/ToolbarItem/main.js | 9 + .../main/Toolbar/ToolbarItem/sample.html | 69 ++ 10 files changed, 887 insertions(+), 160 deletions(-) create mode 100644 packages/main/cypress/specs/ToolbarItem.cy.tsx create mode 100644 packages/website/docs/_components_pages/main/Toolbar/ToolbarItem.mdx create mode 100644 packages/website/docs/_samples/main/Toolbar/ToolbarItem/ToolbarItem.md create mode 100644 packages/website/docs/_samples/main/Toolbar/ToolbarItem/main.js create mode 100644 packages/website/docs/_samples/main/Toolbar/ToolbarItem/sample.html diff --git a/packages/main/cypress/specs/Toolbar.cy.tsx b/packages/main/cypress/specs/Toolbar.cy.tsx index 754c7006c0fd..719dba32be02 100644 --- a/packages/main/cypress/specs/Toolbar.cy.tsx +++ b/packages/main/cypress/specs/Toolbar.cy.tsx @@ -123,7 +123,7 @@ describe("Toolbar general interaction", () => { .should("exist", "hidden class attached to tb button, meaning it's not shown as expected"); }); - it("Should call event handlers on abstract item", () => { + it("Should call event handlers on item", () => { cy.mount( @@ -666,139 +666,3 @@ describe("ToolbarButton", () => { }); }); -describe("Toolbar Item", () => { - it("Should render ui5-toolbar-item with correct properties and not suppress events", () => { - // Mount the Toolbar with a ui5-toolbar-item wrapping a web component - cy.mount( - - - - - - ); - - // Verify the ui5-toolbar-item has the correct properties - cy.get("ui5-toolbar-item").should((item) => { - expect(item).to.have.attr("prevent-overflow-closing"); - expect(item).to.have.attr("overflow-priority", "AlwaysOverflow"); - }); - - // Verify the inner component (ui5-button) is rendered - cy.get("ui5-toolbar-item") - .find("ui5-button").should((button) => { - expect(button).to.exist; - expect(button).to.contain.text("User Menu"); - }); - - - // Attach a click event to the inner button - cy.get("ui5-button#innerButton") - .then(button => { - button.get(0).addEventListener("click", cy.stub().as("buttonClicked")); - }); - - cy.get('ui5-toolbar') // Select the toolbar - .shadow() // Access the shadow DOM of the toolbar - .find('.ui5-tb-overflow-btn') // Find the overflow button inside the shadow DOM - .realClick(); - - cy.get("ui5-toolbar") - .shadow() - .find("[ui5-popover]") - .as("popover") - - cy.get("@popover") - .should("have.prop", "open", true); - - // Trigger a click event on the inner button - cy.get("ui5-button#innerButton").realClick(); - - // Verify the click event was triggered - cy.get("@buttonClicked").should("have.been.calledOnce"); - }); - - it("Should respect prevent-overflow-closing property", () => { - // Mount the Toolbar with constrained width to force overflow - cy.mount( -
- - - - - - - - -
- ); - - // Wait for overflow processing - cy.wait(500); - - // Click the overflow button to open the popover - cy.get("ui5-toolbar") - .shadow() - .find(".ui5-tb-overflow-btn") - .click(); - - // Verify the popover is open - cy.get("ui5-toolbar") - .shadow() - .find(".ui5-overflow-popover") - .should("have.prop", "open", true); - - // Click on the item with prevent-overflow-closing - cy.get("ui5-toolbar-item[prevent-overflow-closing]") - .find("ui5-button") - .click(); - - // Verify the popover remains open - cy.get("ui5-toolbar") - .shadow() - .find(".ui5-overflow-popover") - .should("have.prop", "open", true); - - // Optional: Test that normal items still close the popover - cy.get("ui5-toolbar-item:not([prevent-overflow-closing])") - .find("ui5-button") - .click(); - - // Verify the popover closes - cy.get("ui5-toolbar") - .shadow() - .find(".ui5-overflow-popover") - .should("have.prop", "open", false); - }); - - it("Should respect overflow-priority property", () => { - // Mount the Toolbar with multiple ui5-toolbar-items - cy.mount( - - - - - - - - - ); - - // Verify the overflow-priority property is respected - cy.get("ui5-toolbar-item[overflow-priority='AlwaysOverflow']") - .should("exist") - .should("have.attr", "overflow-priority", "AlwaysOverflow"); - - cy.get("ui5-toolbar-item[overflow-priority='NeverOverflow']") - .should("exist") - .should("have.attr", "overflow-priority", "NeverOverflow"); - - // Simulate overflow behavior and ensure high-priority item remains visible - cy.viewport(300, 1080); // Simulate a smaller viewport - cy.get("ui5-toolbar-item[overflow-priority='NeverOverflow']") - .should("be.visible"); - - // Ensure low-priority item is hidden or moved to overflow - cy.get("ui5-toolbar-item[overflow-priority='AlwaysOverflow']") - .should("not.be.visible"); - }); -}) diff --git a/packages/main/cypress/specs/ToolbarItem.cy.tsx b/packages/main/cypress/specs/ToolbarItem.cy.tsx new file mode 100644 index 000000000000..4c58ebeb6d7c --- /dev/null +++ b/packages/main/cypress/specs/ToolbarItem.cy.tsx @@ -0,0 +1,761 @@ +import Toolbar from "../../src/Toolbar.js"; +import ToolbarItem from "../../src/ToolbarItem.js"; +import Button from "../../src/Button.js"; +import Switch from "../../src/Switch.js"; +import MultiComboBox from "../../src/MultiComboBox.js"; +import MultiComboBoxItem from "../../src/MultiComboBoxItem.js"; +import ComboBox from "../../src/ComboBox.js"; +import ComboBoxItem from "../../src/ComboBoxItem.js"; +import DatePicker from "../../src/DatePicker.js"; +import Select from "../../src/Select.js"; +import Option from "../../src/Option.js"; + +describe("Toolbar Item Properties", () => { + it("Should render ui5-toolbar-item with correct properties and not suppress events", () => { + // Mount the Toolbar with a ui5-toolbar-item wrapping a web component + cy.mount( + + + + + + ); + + // Verify the ui5-toolbar-item has the correct properties + cy.get("[ui5-toolbar-item]").should((item) => { + expect(item).to.have.attr("prevent-overflow-closing"); + expect(item).to.have.attr("overflow-priority", "AlwaysOverflow"); + }); + + // Verify the inner component (ui5-button) is rendered + cy.get("[ui5-toolbar-item]") + .find("[ui5-button]").should((button) => { + expect(button).to.exist; + expect(button).to.contain.text("User Menu"); + }); + + // Attach a click event to the inner button + cy.get("[ui5-button]#innerButton") + .then(button => { + // Add your event logic here + }); + }); +}); + +describe("Toolbar Item Closing Events - closeOverflowSet functionality", () => { + it("Should close overflow popover when ui5-button 'click' event is fired", () => { + cy.viewport(300, 600); + + cy.mount( + + + + + + ); + + // Open overflow popover + cy.get("#toolbar-button-close") + .shadow() + .find(".ui5-tb-overflow-btn") + .realClick(); + + // Verify popover is open + cy.get("#toolbar-button-close") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", true); + + // Click the button inside the overflow + cy.get("[ui5-toolbar-item]") + .find("[ui5-button]") + .realClick(); + + // Verify popover is closed + cy.get("#toolbar-button-close") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", false); + }); + + it("Should close overflow popover when ui5-select 'change' event is fired", () => { + cy.viewport(300, 600); + + cy.mount( + + + + + + ); + + // Open overflow popover + cy.get("#toolbar-select-close") + .shadow() + .find(".ui5-tb-overflow-btn") + .realClick(); + + // Verify popover is open + cy.get("#toolbar-select-close") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", true); + + // Open the select and choose an option + cy.get("[ui5-toolbar-item]") + .find("[ui5-select]") + .realClick(); + + // Select a different option + cy.get("[ui5-option]") + .eq(1) + .realClick(); + + // Verify popover is closed after select change + cy.get("#toolbar-select-close") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", false); + }); + + it("Should close overflow popover when ui5-combobox 'change' event is fired", () => { + cy.viewport(300, 600); + + cy.mount( + + + + + + + + + + ); + + // Open overflow popover + cy.get("#toolbar-combobox-close") + .shadow() + .find(".ui5-tb-overflow-btn") + .realClick(); + + // Verify popover is open + cy.get("#toolbar-combobox-close") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", true); + + // Open the combobox by clicking the dropdown arrow icon + cy.get("[ui5-toolbar-item]") + .find("[ui5-combobox]") + .shadow() + .find("[ui5-icon]") + .realClick(); + + // Wait for combobox popover to open and select an item + cy.get("[ui5-combobox]") + .find("[ui5-cb-item]") + .first() + .realClick(); + + // Verify popover is closed after combobox change + cy.get("#toolbar-combobox-close") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", false); + }); + + it("Should close overflow popover when ui5-multi-combobox 'selection-change' event is fired", () => { + cy.viewport(300, 600); + + cy.mount( + + + + + + + + + + ); + + // Open overflow popover + cy.get("#toolbar-multi-combobox-close") + .shadow() + .find(".ui5-tb-overflow-btn") + .realClick(); + + // Verify popover is open + cy.get("#toolbar-multi-combobox-close") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", true); + + // Open the multi-combobox + cy.get("[ui5-toolbar-item]") + .find("[ui5-multi-combobox]") + .shadow() + .find("[ui5-icon]") + .realClick(); + + // Check an item - this triggers selection-change event + cy.get("[ui5-multi-combobox]") + .find("[ui5-mcb-item]") + .first() + .realClick(); + + // Verify toolbar popover is closed after multi-combobox selection-change + cy.get("#toolbar-multi-combobox-close") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", false); + }); + + it("Should close overflow popover when ui5-date-picker 'change' event is fired", () => { + cy.viewport(300, 600); + + cy.mount( + + + + + + ); + + // Open overflow popover + cy.get("#toolbar-datepicker-close") + .shadow() + .find(".ui5-tb-overflow-btn") + .realClick(); + + // Verify popover is open + cy.get("#toolbar-datepicker-close") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", true); + + // Open date picker + cy.get("[ui5-toolbar-item]") + .find("[ui5-date-picker]") + .shadow() + .find("[ui5-icon]") + .realClick(); + + // Select today's date by clicking on today cell + cy.get("[ui5-calendar]", { includeShadowDom: true }) + .find("[ui5-daypicker]", { includeShadowDom: true }) + .shadow() + .find(".ui5-dp-item--now") + .realClick(); + + // Verify popover is closed after date picker change + cy.get("#toolbar-datepicker-close") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", false); + }); + + it("Should close overflow popover when ui5-switch 'change' event is fired", () => { + cy.viewport(300, 600); + + cy.mount( + + + + + + ); + + // Open overflow popover + cy.get("#toolbar-switch-close") + .shadow() + .find(".ui5-tb-overflow-btn") + .realClick(); + + // Verify popover is open + cy.get("#toolbar-switch-close") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", true); + + // Toggle the switch using realClick + cy.get("[ui5-toolbar-item]") + .find("[ui5-switch]") + .realClick(); + + // Verify popover is closed after switch change + cy.get("#toolbar-switch-close") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", false); + }); + + it("Should NOT close overflow popover when preventOverflowClosing is set", () => { + cy.viewport(300, 600); + + cy.mount( + + + + + + ); + + // Open overflow popover + cy.get("#toolbar-prevent-close") + .shadow() + .find(".ui5-tb-overflow-btn") + .realClick(); + + // Verify popover is open + cy.get("#toolbar-prevent-close") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", true); + + // Click the button inside the overflow + cy.get("[ui5-toolbar-item]") + .find("[ui5-button]") + .realClick(); + + // Verify popover is still open + cy.get("#toolbar-prevent-close") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", true); + }); + + it("Should NOT close overflow popover when preventOverflowClosing is set on ui5-switch", () => { + cy.viewport(300, 600); + + cy.mount( + + + + + + ); + + // Open overflow popover + cy.get("#toolbar-switch-prevent-close") + .shadow() + .find(".ui5-tb-overflow-btn") + .realClick(); + + // Verify popover is open + cy.get("#toolbar-switch-prevent-close") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", true); + + // Toggle the switch + cy.get("[ui5-toolbar-item]") + .find("[ui5-switch]") + .realClick(); + + // Verify popover is still open since preventOverflowClosing is set + cy.get("#toolbar-switch-prevent-close") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", true); + }); + + it("Should fire close-overflow event when closing event is triggered", () => { + cy.viewport(300, 600); + + cy.mount( + + + + + + ); + + // Add event listener for close-overflow + cy.get("#testToolbarItem") + .then($item => { + $item.get(0).addEventListener("close-overflow", cy.stub().as("closeOverflowStub")); + }); + + // Open overflow popover + cy.get("#toolbar-close-event") + .shadow() + .find(".ui5-tb-overflow-btn") + .realClick(); + + // Click the button to trigger closing + cy.get("[ui5-toolbar-item]") + .find("[ui5-button]") + .realClick(); + + // Verify close-overflow event was fired + cy.get("@closeOverflowStub") + .should("have.been.calledOnce"); + }); + + it("Should handle multiple components with different closing events in toolbar", () => { + cy.viewport(200, 600); + + cy.mount( + + + + + + + + + ); + + // Open overflow popover + cy.get("#toolbar-multiple-components") + .shadow() + .find(".ui5-tb-overflow-btn") + .realClick(); + + // Verify popover is open + cy.get("#toolbar-multiple-components") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", true); + + // Toggle the switch (fires 'change' event) + cy.get("[ui5-toolbar-item]") + .find("[ui5-switch]") + .realClick(); + + // Verify popover is closed + cy.get("#toolbar-multiple-components") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", false); + + // Re-open overflow popover + cy.get("#toolbar-multiple-components") + .shadow() + .find(".ui5-tb-overflow-btn") + .realClick(); + + // Verify popover is open again + cy.get("#toolbar-multiple-components") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", true); + + // Click the button (fires 'click' event) + cy.get("[ui5-toolbar-item]") + .find("[ui5-button]") + .realClick(); + + // Verify popover is closed + cy.get("#toolbar-multiple-components") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", false); + }); + + it("Should return correct closing events via getClosingEvents for button", () => { + cy.mount( + + + + + + ); + + cy.get("#buttonToolbarItem") + .then($item => { + const toolbarItem = $item.get(0) as ToolbarItem; + const closingEvents = toolbarItem.getClosingEvents(); + expect(closingEvents).to.include("click"); + }); + }); + + it("Should return correct closing events via getClosingEvents for select", () => { + cy.mount( + + + + + + ); + + cy.get("#selectToolbarItem") + .then($item => { + const toolbarItem = $item.get(0) as ToolbarItem; + const closingEvents = toolbarItem.getClosingEvents(); + expect(closingEvents).to.include("change"); + }); + }); + + it("Should return correct closing events via getClosingEvents for switch", () => { + cy.mount( + + + + + + ); + + cy.get("#switchToolbarItem") + .then($item => { + const toolbarItem = $item.get(0) as ToolbarItem; + const closingEvents = toolbarItem.getClosingEvents(); + expect(closingEvents).to.include("change"); + }); + }); +}); + +/** + * Custom component that implements IOverflowToolbarItem with overflowCloseEvents + * This simulates a component that defines its own custom closing events + */ +class CustomOverflowComponent extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: "open" }); + this.shadowRoot!.innerHTML = ` + + `; + } + + // Implements IOverflowToolbarItem.overflowCloseEvents + get overflowCloseEvents(): string[] { + return ["custom-action", "custom-change"]; + } + + fireCustomAction() { + this.dispatchEvent(new CustomEvent("custom-action", { bubbles: true })); + } + + fireCustomChange() { + this.dispatchEvent(new CustomEvent("custom-change", { bubbles: true })); + } +} + +// Register the custom element if not already registered +if (!customElements.get("custom-overflow-component")) { + customElements.define("custom-overflow-component", CustomOverflowComponent); +} + +/** + * Custom component that implements IOverflowToolbarItem with additional custom events + * Used to test combining predefined closeOverflowSet events with component's overflowCloseEvents + */ +class CustomButtonWithExtraEvents extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: "open" }); + this.shadowRoot!.innerHTML = ``; + } + + get overflowCloseEvents(): string[] { + return ["extra-event"]; + } + + fireExtraEvent() { + this.dispatchEvent(new CustomEvent("extra-event", { bubbles: true })); + } +} + +// Register the custom element if not already registered +if (!customElements.get("custom-button-extra")) { + customElements.define("custom-button-extra", CustomButtonWithExtraEvents); +} + +// @ts-ignore - Custom element JSX type +const CustomOverflowComponentRenderer = (props: { id: string }) => ; + +describe("Toolbar Item Closing Events - overflowCloseEvents functionality (IOverflowToolbarItem)", () => { + it("Should close overflow popover when custom component fires 'custom-action' event from overflowCloseEvents", () => { + cy.viewport(300, 600); + + cy.mount( + + + + + + ); + + // Open overflow popover + cy.get("#toolbar-custom-action") + .shadow() + .find(".ui5-tb-overflow-btn") + .realClick(); + + // Verify popover is open + cy.get("#toolbar-custom-action") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", true); + + // Fire the custom-action event from the component + cy.get("#customComponent1") + .then($el => { + const component = $el.get(0) as CustomOverflowComponent; + component.fireCustomAction(); + }); + + // Verify popover is closed + cy.get("#toolbar-custom-action") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", false); + }); + + it("Should close overflow popover when custom component fires 'custom-change' event from overflowCloseEvents", () => { + cy.viewport(300, 600); + + cy.mount( + + + + + + ); + + // Open overflow popover + cy.get("#toolbar-custom-change") + .shadow() + .find(".ui5-tb-overflow-btn") + .realClick(); + + // Verify popover is open + cy.get("#toolbar-custom-change") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", true); + + // Fire the custom-change event from the component + cy.get("#customComponent2") + .then($el => { + const component = $el.get(0) as CustomOverflowComponent; + component.fireCustomChange(); + }); + + // Verify popover is closed + cy.get("#toolbar-custom-change") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", false); + }); + + it("Should return custom closing events via getClosingEvents for component with overflowCloseEvents", () => { + cy.mount( + + + + + + ); + + cy.get("#customToolbarItem") + .then($item => { + const toolbarItem = $item.get(0) as ToolbarItem; + const closingEvents = toolbarItem.getClosingEvents(); + expect(closingEvents).to.include("custom-action"); + expect(closingEvents).to.include("custom-change"); + }); + }); + + it("Should NOT close overflow popover with custom events when preventOverflowClosing is set", () => { + cy.viewport(300, 600); + + cy.mount( + + + + + + ); + + // Open overflow popover + cy.get("#toolbar-custom-prevent") + .shadow() + .find(".ui5-tb-overflow-btn") + .realClick(); + + // Verify popover is open + cy.get("#toolbar-custom-prevent") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", true); + + // Fire the custom-action event from the component + cy.get("#customComponent4") + .then($el => { + const component = $el.get(0) as CustomOverflowComponent; + component.fireCustomAction(); + }); + + // Verify popover is still open since preventOverflowClosing is set + cy.get("#toolbar-custom-prevent") + .shadow() + .find("[ui5-popover]") + .should("have.prop", "open", true); + }); + + it("Should fire close-overflow event when custom overflowCloseEvents event is triggered", () => { + cy.viewport(300, 600); + + cy.mount( + + + + + + ); + + // Add event listener for close-overflow + cy.get("#customEventItem") + .then($item => { + $item.get(0).addEventListener("close-overflow", cy.stub().as("closeOverflowStub")); + }); + + // Open overflow popover + cy.get("#toolbar-custom-event-fire") + .shadow() + .find(".ui5-tb-overflow-btn") + .realClick(); + + // Fire the custom-action event from the component + cy.get("#customComponent5") + .then($el => { + const component = $el.get(0) as CustomOverflowComponent; + component.fireCustomAction(); + }); + + // Verify close-overflow event was fired + cy.get("@closeOverflowStub") + .should("have.been.calledOnce"); + }); + + it("Should combine predefined closeOverflowSet events with component's overflowCloseEvents", () => { + // @ts-ignore - Custom element JSX type + const CustomButtonExtraRenderer = (props: { id: string }) => ; + + cy.mount( + + + + + + ); + + cy.get("#combinedEventsItem") + .then($item => { + const toolbarItem = $item.get(0) as ToolbarItem; + const closingEvents = toolbarItem.getClosingEvents(); + // Should include the custom overflowCloseEvents + expect(closingEvents).to.include("extra-event"); + }); + }); +}); \ No newline at end of file diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 5d1e40b69424..def1342bbc72 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -17,10 +17,14 @@ type ToolbarItemEventDetail = { } interface IOverflowToolbarItem extends HTMLElement { - eventsToCloseOverflow?: string[] | undefined; + overflowCloseEvents?: string[] | undefined; hasOverflow?: boolean | undefined; } - +/** + * Fired when the overflow popover is closed. + * @public + * @since 1.17.0 + */ @event("close-overflow", { bubbles: true, cancelable: true, @@ -77,27 +81,15 @@ class ToolbarItem extends UI5Element { @property({ type: Boolean }) isOverflowed: boolean = false; - /** - * Defines if the component, wrapped in the toolbar item, should be expanded in the overflow popover. - * @default false - * @public - * @since 2.19.0 - */ - - @property({ type: Boolean }) - shrinkInOverflow: boolean = false; - _isRendering = true; _maxWidth = 0; fireCloseOverflowRef = this.fireCloseOverflow.bind(this); - overflowCloseEvents: string[] = []; - closeOverflowSet = { "ui5-button": ["click"], "ui5-select": ["change"], "ui5-combobox": ["change"], - "ui5-multi-combobox": ["change"], + "ui5-multi-combobox": ["selection-change"], "ui5-date-picker": ["change"], "ui5-switch": ["change"], } @@ -145,7 +137,15 @@ class ToolbarItem extends UI5Element { // We want to close the overflow popover, when closing event is being executed getClosingEvents(): string[] { - return [...(this.closeOverflowSet[this.itemTagName as keyof typeof this.closeOverflowSet] || []), ...this.overflowCloseEvents]; + const item = Array.isArray(this.item) ? this.item[0] : this.item; + + const closeEvents = this.closeOverflowSet[this.itemTagName as keyof typeof this.closeOverflowSet] || []; + if (!item) { + return [...closeEvents]; + } + const overflowCloseEvents = Array.isArray(item.overflowCloseEvents) ? item.overflowCloseEvents : []; + + return [...closeEvents, ...overflowCloseEvents]; } attachCloseOverflowHandlers() { diff --git a/packages/main/src/themes/ToolbarItem.css b/packages/main/src/themes/ToolbarItem.css index c869a5a617ce..1c61f4911d62 100644 --- a/packages/main/src/themes/ToolbarItem.css +++ b/packages/main/src/themes/ToolbarItem.css @@ -1,6 +1,6 @@ -/* This style we need for the element inside Popover with shrinikInOverflow property to not fire the click event on empty space */ -:host([is-overflowed][shrink-in-overflow]) { +:host() { display: inline-block; + height: 100%; } :host([is-overflowed]) ::slotted(*) { diff --git a/packages/main/test/pages/ToolbarItems.html b/packages/main/test/pages/ToolbarItems.html index 1dee29a62632..31c810c2789c 100644 --- a/packages/main/test/pages/ToolbarItems.html +++ b/packages/main/test/pages/ToolbarItems.html @@ -41,8 +41,8 @@
- Left 1 (long) - Left 2 + Left 1 (long) + Left 2 Left 3 Left 4 Mid 1 @@ -156,12 +156,12 @@ - + - + @@ -169,13 +169,21 @@ + + + + + + + + - + diff --git a/packages/website/docs/_components_pages/main/Toolbar/Toolbar.mdx b/packages/website/docs/_components_pages/main/Toolbar/Toolbar.mdx index 44c3551eab17..dd2cd194baa1 100644 --- a/packages/website/docs/_components_pages/main/Toolbar/Toolbar.mdx +++ b/packages/website/docs/_components_pages/main/Toolbar/Toolbar.mdx @@ -7,6 +7,8 @@ import AlwaysOverflowingItems from "../../../_samples/main/Toolbar/AlwaysOverflo import NeverOverflowingItems from "../../../_samples/main/Toolbar/NeverOverflowingItems/NeverOverflowingItems.md"; import SpacerAndSeparator from "../../../_samples/main/Toolbar/SpacerAndSeparator/SpacerAndSeparator.md"; import ItemsAlignment from "../../../_samples/main/Toolbar/ItemsAlignment/ItemsAlignment.md"; +import ToolbarItem from "../../../_samples/main/Toolbar/ToolbarItem/ToolbarItem.md"; + <%COMPONENT_OVERVIEW%> @@ -34,3 +36,6 @@ To force items staying always visible and never overflow, set "overflow-priority You can align items to the Start, or to the End via the "align-content" property +### ToolbarItem +ToolbarItem wrapper used to add any component to Toolbar + diff --git a/packages/website/docs/_components_pages/main/Toolbar/ToolbarItem.mdx b/packages/website/docs/_components_pages/main/Toolbar/ToolbarItem.mdx new file mode 100644 index 000000000000..59e1b2edd659 --- /dev/null +++ b/packages/website/docs/_components_pages/main/Toolbar/ToolbarItem.mdx @@ -0,0 +1,7 @@ +--- +slug: ../../ToolbarItem +--- + +<%COMPONENT_OVERVIEW%> + +<%COMPONENT_METADATA%> \ No newline at end of file diff --git a/packages/website/docs/_samples/main/Toolbar/ToolbarItem/ToolbarItem.md b/packages/website/docs/_samples/main/Toolbar/ToolbarItem/ToolbarItem.md new file mode 100644 index 000000000000..ffccbf6dd13e --- /dev/null +++ b/packages/website/docs/_samples/main/Toolbar/ToolbarItem/ToolbarItem.md @@ -0,0 +1,4 @@ +import html from '!!raw-loader!./sample.html'; +import js from '!!raw-loader!./main.js'; + + \ No newline at end of file diff --git a/packages/website/docs/_samples/main/Toolbar/ToolbarItem/main.js b/packages/website/docs/_samples/main/Toolbar/ToolbarItem/main.js new file mode 100644 index 000000000000..1a6881f7a597 --- /dev/null +++ b/packages/website/docs/_samples/main/Toolbar/ToolbarItem/main.js @@ -0,0 +1,9 @@ +import "@ui5/webcomponents/dist/Toolbar.js"; // For ui5-toolbar +import "@ui5/webcomponents/dist/ToolbarItem.js"; // For ui5-toolbar-item +import "@ui5/webcomponents/dist/ComboBox.js"; // For ui5-combobox +import "@ui5/webcomponents/dist/ComboBoxItem.js"; // For ui5-combobox-item +import "@ui5/webcomponents/dist/MultiComboBox.js"; // For ui5-multi-combobox +import "@ui5/webcomponents/dist/MultiComboBoxItem.js"; // For ui5-mcb-item +import "@ui5/webcomponents/dist/DatePicker.js"; // For ui5-datepicker +import "@ui5/webcomponents/dist/Switch.js"; // For ui5-switch +import "@ui5/webcomponents/dist/Popover.js"; // For ui5-popover \ No newline at end of file diff --git a/packages/website/docs/_samples/main/Toolbar/ToolbarItem/sample.html b/packages/website/docs/_samples/main/Toolbar/ToolbarItem/sample.html new file mode 100644 index 000000000000..4ff4836055c5 --- /dev/null +++ b/packages/website/docs/_samples/main/Toolbar/ToolbarItem/sample.html @@ -0,0 +1,69 @@ + + + + + + + Sample + + + + + + + +
+ + + +
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From e3d36422e6f7c06be4bb79d31f6e991ea27cbc0a Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Tue, 3 Feb 2026 11:41:34 +0200 Subject: [PATCH 39/43] fix(ui5-toolbar): typo in sample fixed --- .../website/docs/_samples/main/Toolbar/ToolbarItem/sample.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/docs/_samples/main/Toolbar/ToolbarItem/sample.html b/packages/website/docs/_samples/main/Toolbar/ToolbarItem/sample.html index 4ff4836055c5..c111722bcf34 100644 --- a/packages/website/docs/_samples/main/Toolbar/ToolbarItem/sample.html +++ b/packages/website/docs/_samples/main/Toolbar/ToolbarItem/sample.html @@ -21,7 +21,7 @@ - +
From ff66797d5fc27dd76670340bee3161fa2a8700f9 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Mon, 9 Feb 2026 11:59:44 +0200 Subject: [PATCH 40/43] chore(ui5-toolbar): experimental flag added --- packages/main/src/ToolbarItem.ts | 9 ++++++++- .../docs/_components_pages/main/Toolbar/ToolbarItem.mdx | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index def1342bbc72..3beb0a6660cd 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -45,6 +45,7 @@ interface IOverflowToolbarItem extends HTMLElement { * @constructor * @extends UI5Element * @public + * @experimental This module is experimental and its API might change significantly in future. * @since 1.17.0 */ class ToolbarItem extends UI5Element { @@ -83,6 +84,7 @@ class ToolbarItem extends UI5Element { _isRendering = true; _maxWidth = 0; + _wrapperChecked = false; fireCloseOverflowRef = this.fireCloseOverflow.bind(this); closeOverflowSet = { @@ -115,7 +117,7 @@ class ToolbarItem extends UI5Element { /** * Wrapped component slot. * @public - * @since 2.19.0 + * @since 2.20.0 */ @slot({ @@ -125,6 +127,11 @@ class ToolbarItem extends UI5Element { // Method called by ui5-toolbar to inform about the existing toolbar wrapper checkForWrapper() { + if (this._wrapperChecked) { + return; + } + this._wrapperChecked = true; + const tagName = this.itemTagName as keyof typeof this.predefinedWrapperSet; const ctor = this.constructor as typeof UI5Element; const wrapperName = ctor?.getMetadata ? ctor.getMetadata().getPureTag() : this.tagName; diff --git a/packages/website/docs/_components_pages/main/Toolbar/ToolbarItem.mdx b/packages/website/docs/_components_pages/main/Toolbar/ToolbarItem.mdx index 59e1b2edd659..e4267eefb27a 100644 --- a/packages/website/docs/_components_pages/main/Toolbar/ToolbarItem.mdx +++ b/packages/website/docs/_components_pages/main/Toolbar/ToolbarItem.mdx @@ -1,5 +1,6 @@ --- slug: ../../ToolbarItem +sidebar_class_name: newComponentBadge --- <%COMPONENT_OVERVIEW%> From 4d62c86787ff278d06708a5a7a21c90d5dfa568b Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Mon, 9 Feb 2026 14:16:05 +0200 Subject: [PATCH 41/43] chore(ui5-toolbar): failing tests fixed --- packages/main/cypress/specs/ToolbarItem.cy.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/main/cypress/specs/ToolbarItem.cy.tsx b/packages/main/cypress/specs/ToolbarItem.cy.tsx index 4c58ebeb6d7c..bcc34dc29dd8 100644 --- a/packages/main/cypress/specs/ToolbarItem.cy.tsx +++ b/packages/main/cypress/specs/ToolbarItem.cy.tsx @@ -114,6 +114,8 @@ describe("Toolbar Item Closing Events - closeOverflowSet functionality", () => { cy.get("[ui5-option]") .eq(1) .realClick(); + + cy.wait(500); // Wait for the change event to propagate and popover to close // Verify popover is closed after select change cy.get("#toolbar-select-close") @@ -427,7 +429,7 @@ describe("Toolbar Item Closing Events - closeOverflowSet functionality", () => { // Toggle the switch (fires 'change' event) cy.get("[ui5-toolbar-item]") .find("[ui5-switch]") - .realClick(); + .click(); // Verify popover is closed cy.get("#toolbar-multiple-components") From 8b69cba5c3cde0e204c2c7bfe868048fdd7ff723 Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Mon, 9 Feb 2026 14:51:24 +0200 Subject: [PATCH 42/43] chore(ui5-toolbar): failing tests fixed --- packages/main/cypress/specs/ToolbarItem.cy.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/main/cypress/specs/ToolbarItem.cy.tsx b/packages/main/cypress/specs/ToolbarItem.cy.tsx index bcc34dc29dd8..7d4465574660 100644 --- a/packages/main/cypress/specs/ToolbarItem.cy.tsx +++ b/packages/main/cypress/specs/ToolbarItem.cy.tsx @@ -288,6 +288,8 @@ describe("Toolbar Item Closing Events - closeOverflowSet functionality", () => { // Toggle the switch using realClick cy.get("[ui5-toolbar-item]") .find("[ui5-switch]") + .shadow() + .find(".ui5-switch-root") .realClick(); // Verify popover is closed after switch change @@ -429,7 +431,7 @@ describe("Toolbar Item Closing Events - closeOverflowSet functionality", () => { // Toggle the switch (fires 'change' event) cy.get("[ui5-toolbar-item]") .find("[ui5-switch]") - .click(); + .realClick(); // Verify popover is closed cy.get("#toolbar-multiple-components") From 712737232e364d0223f021d00f1908332ba7626f Mon Sep 17 00:00:00 2001 From: PetyaMarkovaBogdanova Date: Mon, 9 Feb 2026 15:03:55 +0200 Subject: [PATCH 43/43] feat(ui5-toolbar): failing tests fixed --- packages/main/cypress/specs/ToolbarItem.cy.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/main/cypress/specs/ToolbarItem.cy.tsx b/packages/main/cypress/specs/ToolbarItem.cy.tsx index 7d4465574660..595408c1381a 100644 --- a/packages/main/cypress/specs/ToolbarItem.cy.tsx +++ b/packages/main/cypress/specs/ToolbarItem.cy.tsx @@ -431,6 +431,8 @@ describe("Toolbar Item Closing Events - closeOverflowSet functionality", () => { // Toggle the switch (fires 'change' event) cy.get("[ui5-toolbar-item]") .find("[ui5-switch]") + .shadow() + .find(".ui5-switch-root") .realClick(); // Verify popover is closed