diff --git a/packages/main/cypress/specs/ToolbarItem.cy.tsx b/packages/main/cypress/specs/ToolbarItem.cy.tsx
index dc579edc71858..f6337db58ca97 100644
--- a/packages/main/cypress/specs/ToolbarItem.cy.tsx
+++ b/packages/main/cypress/specs/ToolbarItem.cy.tsx
@@ -11,6 +11,7 @@ import DatePicker from "../../src/DatePicker.js";
import Select from "../../src/Select.js";
import Option from "../../src/Option.js";
import CheckBox from "../../src/CheckBox.js";
+import Title from "../../src/Title.js";
import add from "@ui5/webcomponents-icons/dist/add.js";
describe("Toolbar Item Properties", () => {
@@ -790,4 +791,106 @@ describe("ToolbarItem CSS Custom State", () => {
expect(item._internals.states.has("overflowed")).to.be.true;
});
});
-});
\ No newline at end of file
+});
+
+describe("ToolbarItem shrink-content", () => {
+ it("Should keep the toolbar button visible when shrink-content is set on a wide NeverOverflow item", () => {
+ cy.mount(
+
+
+
+ Super Super SuperSuperSuperSuper SuperSuperSuperSuper SuperSuperSuperSuper Long Text
+
+
+
+
+ );
+
+ // The button must be visible — the title shrinks to yield space
+ cy.get("[ui5-toolbar-button][text='Button 1']")
+ .should("be.visible");
+
+ // No overflow button needed — the title shrinks instead of pushing items out
+ cy.get("[ui5-toolbar]")
+ .shadow()
+ .find(".ui5-tb-overflow-btn")
+ .should("have.class", "ui5-tb-overflow-btn-hidden");
+ });
+
+ it("Should apply ui5-tb-self-overflow class to the item wrapper when shrink-content is set", () => {
+ cy.mount(
+
+
+
+ Super Super SuperSuperSuperSuper SuperSuperSuperSuper Long Text
+
+
+
+
+ );
+
+ // hasOverflow must be true when shrink-content is set
+ cy.get("[ui5-toolbar-item]").then($item => {
+ const item = $item[0] as ToolbarItem;
+ expect(item.hasOverflow).to.be.true;
+ });
+
+ // The toolbar wrapper div must carry the ui5-tb-self-overflow CSS class
+ cy.get("[ui5-toolbar]").then($toolbar => {
+ const toolbar = $toolbar[0] as Toolbar;
+ const item = document.querySelector("[ui5-toolbar-item]") as ToolbarItem;
+ const wrapper = toolbar.shadowRoot!.querySelector(`#${item._individualSlot}`) as HTMLElement;
+ expect(wrapper.classList.contains("ui5-tb-self-overflow")).to.be.true;
+ });
+ });
+
+ it("Should NOT apply shrink behavior when shrink-content is not set", () => {
+ cy.mount(
+
+
+
+ Super Super SuperSuperSuperSuper SuperSuperSuperSuper Long Text
+
+
+
+
+ );
+
+ // hasOverflow must be false when shrink-content is not set
+ cy.get("[ui5-toolbar-item]").then($item => {
+ const item = $item[0] as ToolbarItem;
+ expect(item.hasOverflow).to.be.false;
+ });
+ });
+
+ it("Should keep the button visible after the toolbar is resized", () => {
+ cy.mount(
+
+
+
+ Super Super SuperSuperSuperSuper SuperSuperSuperSuper SuperSuperSuperSuper Long Text
+
+
+
+
+ );
+
+ // Button is visible at narrow width
+ cy.get("[ui5-toolbar-button][text='Button 1']")
+ .should("be.visible");
+
+ // Widen the container
+ cy.get("#shrink-resize-container")
+ .invoke("css", "width", "800px");
+
+ // Button must still be visible after resize
+ cy.get("[ui5-toolbar-button][text='Button 1']")
+ .should("be.visible");
+
+ // Overflow button must remain hidden throughout
+ cy.get("[ui5-toolbar]")
+ .shadow()
+ .find(".ui5-tb-overflow-btn")
+ .should("have.class", "ui5-tb-overflow-btn-hidden");
+ });
+});
diff --git a/packages/main/src/Toolbar.ts b/packages/main/src/Toolbar.ts
index b8b12089dc9ba..8b6761532d7de 100644
--- a/packages/main/src/Toolbar.ts
+++ b/packages/main/src/Toolbar.ts
@@ -354,7 +354,7 @@ class Toolbar extends UI5Element {
addItemsAdditionalProperties(item: ToolbarItemBase) {
item.isOverflowed = this.overflowItems.indexOf(item) !== -1;
const itemWrapper = this.shadowRoot!.querySelector(`#${item._individualSlot}`) as HTMLElement;
- if (item.hasOverflow && !item.isOverflowed && itemWrapper) {
+ if (item.hasOverflow && item.clampMaxWidth && !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,
@@ -433,8 +433,13 @@ class Toolbar extends UI5Element {
this.items.forEach(item => {
const itemWidth = this.getItemWidth(item);
+ // shrinkContent items (clampMaxWidth=false) are intentionally included in totalWidth:
+ // getItemWidth returns 0 on first render (_isRendering=true) and the cached flex-distributed
+ // width on subsequent renders, both of which keep overflowSpace non-positive so distributeItems
+ // is not incorrectly triggered. They are excluded from minWidth only, as their rendered
+ // width is not a stable natural-width value.
totalWidth += itemWidth;
- if (item.overflowPriority === ToolbarItemOverflowBehavior.NeverOverflow) {
+ if (item.overflowPriority === ToolbarItemOverflowBehavior.NeverOverflow && item.clampMaxWidth) {
minWidth += itemWidth;
}
this.ITEMS_WIDTH_MAP.set(item._id, itemWidth);
diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts
index 2a0458cc0dc66..707ef495bbf3b 100644
--- a/packages/main/src/ToolbarItem.ts
+++ b/packages/main/src/ToolbarItem.ts
@@ -1,4 +1,5 @@
import slot from "@ui5/webcomponents-base/dist/decorators/slot-strict.js";
+import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import getActiveElement from "@ui5/webcomponents-base/dist/util/getActiveElement.js";
@@ -115,6 +116,18 @@ class ToolbarItem extends ToolbarItemBase {
})
item!: DefaultSlot;
+ /**
+ * Defines whether the item should shrink to fit the available toolbar space
+ * instead of pushing other items out of view.
+ * Use this when the slotted content (e.g. a long title) should yield space
+ * to other toolbar items rather than overflow them.
+ * @default false
+ * @public
+ * @since 2.25.0
+ */
+ @property({ type: Boolean })
+ shrinkContent = false;
+
// Method called by ui5-toolbar to inform about the existing toolbar wrapper
checkForWrapper() {
if (this._wrapperChecked) {
@@ -209,7 +222,11 @@ class ToolbarItem extends ToolbarItemBase {
}
get hasOverflow(): boolean {
- return this.item[0]?.hasOverflow ?? false;
+ return this.item[0]?.hasOverflow || this.shrinkContent;
+ }
+
+ get clampMaxWidth(): boolean {
+ return !this.shrinkContent;
}
getFocusDomRef(): HTMLElement | undefined {
diff --git a/packages/main/src/ToolbarItemBase.ts b/packages/main/src/ToolbarItemBase.ts
index 989819ad242eb..0037154491ac5 100644
--- a/packages/main/src/ToolbarItemBase.ts
+++ b/packages/main/src/ToolbarItemBase.ts
@@ -132,6 +132,17 @@ class ToolbarItemBase extends UI5Element {
return false;
}
+ /**
+ * Returns whether the toolbar should apply a max-width cap to this item
+ * during the self-overflow measurement cycle.
+ * Override to return false for items that must remain free to grow and shrink
+ * (i.e. items that use flex-shrink to yield space rather than overflowing).
+ * @protected
+ */
+ get clampMaxWidth(): boolean {
+ return true;
+ }
+
/**
* Returns if the item is separator.
* @protected
diff --git a/packages/website/docs/_samples/main/Toolbar/ShrinkContent/ShrinkContent.md b/packages/website/docs/_samples/main/Toolbar/ShrinkContent/ShrinkContent.md
new file mode 100644
index 0000000000000..0c062a836e844
--- /dev/null
+++ b/packages/website/docs/_samples/main/Toolbar/ShrinkContent/ShrinkContent.md
@@ -0,0 +1,5 @@
+import html from '!!raw-loader!./sample.html';
+import js from '!!raw-loader!./main.js';
+import react from '!!raw-loader!./sample.tsx';
+
+
diff --git a/packages/website/docs/_samples/main/Toolbar/ShrinkContent/main.js b/packages/website/docs/_samples/main/Toolbar/ShrinkContent/main.js
new file mode 100644
index 0000000000000..c003cbcdaa967
--- /dev/null
+++ b/packages/website/docs/_samples/main/Toolbar/ShrinkContent/main.js
@@ -0,0 +1,4 @@
+import "@ui5/webcomponents/dist/Toolbar.js";
+import "@ui5/webcomponents/dist/ToolbarItem.js";
+import "@ui5/webcomponents/dist/ToolbarButton.js";
+import "@ui5/webcomponents/dist/Title.js";
diff --git a/packages/website/docs/_samples/main/Toolbar/ShrinkContent/sample.html b/packages/website/docs/_samples/main/Toolbar/ShrinkContent/sample.html
new file mode 100644
index 0000000000000..450b3ea6181db
--- /dev/null
+++ b/packages/website/docs/_samples/main/Toolbar/ShrinkContent/sample.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+ Sample
+
+
+
+
+
+
+ Super Long Title That Should Shrink To Fit Available Space
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/website/docs/_samples/main/Toolbar/ShrinkContent/sample.tsx b/packages/website/docs/_samples/main/Toolbar/ShrinkContent/sample.tsx
new file mode 100644
index 0000000000000..2dcb10e174dea
--- /dev/null
+++ b/packages/website/docs/_samples/main/Toolbar/ShrinkContent/sample.tsx
@@ -0,0 +1,28 @@
+import createReactComponent from "@ui5/webcomponents-base/dist/createReactComponent.js";
+import ToolbarClass from "@ui5/webcomponents/dist/Toolbar.js";
+import ToolbarItemClass from "@ui5/webcomponents/dist/ToolbarItem.js";
+import ToolbarButtonClass from "@ui5/webcomponents/dist/ToolbarButton.js";
+import TitleClass from "@ui5/webcomponents/dist/Title.js";
+
+const Toolbar = createReactComponent(ToolbarClass);
+const ToolbarItem = createReactComponent(ToolbarItemClass);
+const ToolbarButton = createReactComponent(ToolbarButtonClass);
+const Title = createReactComponent(TitleClass);
+
+function App() {
+ return (
+ <>
+
+
+
+ Super Long Title That Should Shrink To Fit Available Space
+
+
+
+
+
+ >
+ );
+}
+
+export default App;