Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4000845
feat: implement content fit
GDamyanov Sep 25, 2025
f6d0127
feat: truncate long texts in contentFit mode
GDamyanov Sep 25, 2025
091083a
feat: dynamic text truncation
GDamyanov Sep 26, 2025
384debc
chore: add todo
GDamyanov Sep 26, 2025
23f7c47
refactor: make code productive
GDamyanov Oct 2, 2025
fb4ffb9
Merge branch 'main' into segmented-button-width-poc
GDamyanov Oct 2, 2025
cb46f22
Merge branch 'segmented-button-width-poc' of https://github.com/UI5/w…
GDamyanov Oct 2, 2025
f34a084
refactor: remove unused import
GDamyanov Oct 2, 2025
850ed64
refactor: add jsdoc
GDamyanov Oct 2, 2025
05d36c0
test: add cypress tests
GDamyanov Oct 2, 2025
0ced16b
Merge branch 'main' into segmented-button-width-poc
GDamyanov Oct 2, 2025
f7cd891
refactor: add jsdoc
GDamyanov Oct 2, 2025
0cd64c6
Merge branch 'main' into segmented-button-width-poc
GDamyanov Oct 2, 2025
9032538
Merge branch 'main' into segmented-button-width-poc
GDamyanov Oct 6, 2025
1a99f0d
Update packages/main/src/types/SegmentedButtonContentMode.ts
GDamyanov Oct 6, 2025
a6a3988
Merge branch 'main' into segmented-button-width-poc
GDamyanov Oct 6, 2025
5e8a555
Merge branch 'main' into segmented-button-width-poc
hinzzx Oct 6, 2025
81f7845
feat: add sample in playground
GDamyanov Oct 6, 2025
cfeaca9
Merge branch 'main' into segmented-button-width-poc
GDamyanov Oct 7, 2025
0b0e558
fix: import sample
GDamyanov Oct 7, 2025
0109951
Merge branch 'segmented-button-width-poc' of https://github.com/UI5/w…
GDamyanov Oct 7, 2025
f722aa0
Merge branch 'main' into segmented-button-width-poc
GDamyanov Oct 7, 2025
c638232
fix: refactor jsdocs
GDamyanov Oct 10, 2025
cc7f212
refactor: extract class in template
GDamyanov Oct 10, 2025
abab751
Merge branch 'main' into segmented-button-width-poc
GDamyanov Oct 10, 2025
152c7cd
Merge branch 'main' into segmented-button-width-poc
GDamyanov Oct 13, 2025
e9439c0
Merge branch 'main' into segmented-button-width-poc
GDamyanov Oct 13, 2025
d934e39
Merge branch 'main' into segmented-button-width-poc
GDamyanov Oct 13, 2025
d409c43
Merge branch 'main' into segmented-button-width-poc
GDamyanov Oct 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions packages/main/cypress/specs/SegmentedButton.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,50 @@ describe("SegmentedButtonItems appearance", () => {
});
});

describe("SegmentedButton: contentMode", () => {
it("should have items with width which fits item content in content mode: ContentFit", () => {
cy.mount(
<SegmentedButton contentMode="ContentFit">
<SegmentedButtonItem id="item1">Short</SegmentedButtonItem>
<SegmentedButtonItem id="item2">Much longer text</SegmentedButtonItem>
<SegmentedButtonItem id="item3">Medium</SegmentedButtonItem>
</SegmentedButton>
);

cy.get("#item1")
.invoke("outerWidth")
.then(shortWidth => {
cy.get("#item2")
.invoke("outerWidth")
.should("be.gt", shortWidth);
cy.get("#item3")
.invoke("outerWidth")
.should("be.gt", shortWidth);
});
});

it("should have items with equal width in content mode:EqualSized", () => {
cy.mount(
<SegmentedButton contentMode="EqualSized">
<SegmentedButtonItem id="item1">Short</SegmentedButtonItem>
<SegmentedButtonItem id="item2">Much longer text</SegmentedButtonItem>
<SegmentedButtonItem id="item3">Medium</SegmentedButtonItem>
</SegmentedButton>
);

cy.get("#item1")
.invoke("outerWidth")
.then(width1 => {
cy.get("#item2")
.invoke("outerWidth")
.should("eq", width1);
cy.get("#item3")
.invoke("outerWidth")
.should("eq", width1);
});
});
});

describe("SegmentedButton Accessibility", () => {
it("segmented button should have correct aria label when accessibleName is set", () => {
const LABEL = "Label";
Expand Down Expand Up @@ -485,4 +529,17 @@ describe("SebmentedButtonItem Accessibility", () => {
.find("li")
.should("have.attr", "aria-description", REF_DESCRIPTION);
});

it("should set title attribute to slot text when tooltip is not provided", () => {
cy.mount(
<SegmentedButton>
<SegmentedButtonItem id="item1">Segmented Item Text</SegmentedButtonItem>
</SegmentedButton>
);

cy.get("#item1")
.shadow()
.find("li")
.should("have.attr", "title", "Segmented Item Text");
});
});
17 changes: 16 additions & 1 deletion packages/main/src/SegmentedButton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import SegmentedButtonTemplate from "./SegmentedButtonTemplate.js";

// Styles
import SegmentedButtonCss from "./generated/themes/SegmentedButton.css.js";
import SegmentedButtonContentMode from "./types/SegmentedButtonContentMode.js";

/**
* Interface for components that may be slotted inside `ui5-segmented-button` as items
Expand Down Expand Up @@ -128,6 +129,18 @@ class SegmentedButton extends UI5Element {
selectionMode: `${SegmentedButtonSelectionMode}` = "Single";

/**
* Defines the content mode of the segmented button items.
*
* - "EqualSized": All items are sized equally to fill the available space.
* - "ContentFit": Each item is sized to fit its content, with any extra space placed after the last item.
*
* @default "EqualSized"
* @public
* @since 2.16.0
*/
@property()
contentMode: `${SegmentedButtonContentMode}` = "EqualSized";
/**
* Defines the items of `ui5-segmented-button`.
*
* **Note:** Multiple items are allowed.
Expand Down Expand Up @@ -171,7 +184,9 @@ class SegmentedButton extends UI5Element {

this.normalizeSelection();

this.style.setProperty(getScopedVarName("--_ui5_segmented_btn_items_count"), `${visibleItems.length}`);
if (this.contentMode === SegmentedButtonContentMode.EqualSized) {
this.style.setProperty(getScopedVarName("--_ui5_segmented_btn_items_count"), `${visibleItems.length}`);
}
}
Comment on lines +187 to 190
Copy link

Copilot AI Oct 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing closing brace for the if statement. The code block should be properly closed.

Copilot uses AI. Check for mistakes.


normalizeSelection() {
Expand Down
8 changes: 8 additions & 0 deletions packages/main/src/SegmentedButtonItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,14 @@ class SegmentedButtonItem extends UI5Element implements IButton, ISegmentedButto
get showIconTooltip() {
return getEnableDefaultTooltips() && this.iconOnly && !this.tooltip;
}

get slotTextContent(): string {
return this.text
.filter(node => node.nodeType === Node.TEXT_NODE)
.map(node => node.textContent?.trim() || "")
.filter(Boolean)
.join(" ");
}
}

SegmentedButtonItem.define();
Expand Down
2 changes: 1 addition & 1 deletion packages/main/src/SegmentedButtonItemTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default function SegmentedButtonItemTemplate(this: SegmentedButtonItem) {
tabindex={this.tabIndexValue ? parseInt(this.tabIndexValue) : undefined }
aria-label={this.ariaLabelText}
aria-description={this.ariaDescriptionText}
title={this.tooltip}
title={this.tooltip || this.slotTextContent}
>
{this.icon &&
<Icon
Expand Down
7 changes: 6 additions & 1 deletion packages/main/src/SegmentedButtonTemplate.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import type SegmentedButton from "./SegmentedButton.js";
import SegmentedButtonContentMode from "./types/SegmentedButtonContentMode.js";

export default function SegmentedButtonTemplate(this: SegmentedButton) {
return (
<ul
role="listbox"
class="ui5-segmented-button-root"
class={{
"ui5-segmented-button-root": true,
"ui5-segmented-button-root-equal-sized-items": this.contentMode === SegmentedButtonContentMode.EqualSized,
"ui5-segmented-button-root-content-fit-items": this.contentMode === SegmentedButtonContentMode.ContentFit,
}}
onClick={this._onclick}
onMouseDown={this._onmousedown}
onKeyDown={this._onkeydown}
Expand Down
12 changes: 10 additions & 2 deletions packages/main/src/themes/SegmentedButton.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,23 @@

.ui5-segmented-button-root {
width: inherit;
display: grid;
grid-template-columns: repeat(var(--_ui5_segmented_btn_items_count), minmax(var(--_ui5_button_base_min_width), 1fr));
margin: 0;
padding: 0;
background-color: var(--sapButton_Background);
border-radius: var(--sapButton_BorderCornerRadius);
box-shadow: inset 0 0 0 var(--sapButton_BorderWidth) var(--sapButton_BorderColor);
}

.ui5-segmented-button-root-equal-sized-items {
display: grid;
grid-template-columns: repeat(var(--_ui5_segmented_btn_items_count), minmax(var(--_ui5_button_base_min_width), 1fr));
}

.ui5-segmented-button-root-content-fit-items {
display: inline-flex;
gap: 0;
}

::slotted([ui5-segmented-button-item]) {
border-radius: var(--sapButton_Segment_BorderCornerRadius);
border-color: var(--_ui5_segmented_btn_border_color);
Expand Down
18 changes: 18 additions & 0 deletions packages/main/src/types/SegmentedButtonContentMode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Different SegmentedButton items sizing modes.
* @public
*/
enum SegmentedButtonContentMode {
/**
* Each item is sized to fit its content, with any extra space placed after the last item.
* @public
*/
ContentFit = "ContentFit",
/**
* All items are sized equally to fill the available space.
* @public
*/
EqualSized = "EqualSized",
}

export default SegmentedButtonContentMode;
53 changes: 53 additions & 0 deletions packages/main/test/pages/SegmentedButton.html
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,59 @@ <h1>SegmentedButton wrapped in a container with set width</h1>
</ui5-segmented-button>
</div>
</section>

<section>
<h1>SegmentedButton with Content Mode: Content Fit</h1>

<ui5-segmented-button content-mode="ContentFit" selection-mode="Multiple">
<ui5-segmented-button-item icon="employee" selected></ui5-segmented-button-item>
<ui5-segmented-button-item selected>Word</ui5-segmented-button-item>
<ui5-segmented-button-item selected>Word Word</ui5-segmented-button-item>
<ui5-segmented-button-item selected>selected SegmentedButtonItem With Bigger Text</ui5-segmented-button-item>
<ui5-segmented-button-item selected>Item</ui5-segmented-button-item>
<ui5-segmented-button-item selected>selected SegmentedButtonItem</ui5-segmented-button-item>
<ui5-segmented-button-item selected>A</ui5-segmented-button-item>
</ui5-segmented-button>
</section>

<section>
<h1>SegmentedButton with Content Mode: Content Fit - LONG TEXT</h1>

<ui5-segmented-button content-mode="ContentFit" selection-mode="Multiple">
<ui5-segmented-button-item selected>Word</ui5-segmented-button-item>
<ui5-segmented-button-item selected>selected SegmentedButtonItem With Bigger Text selected SegmentedButtonItem With Bigger Text selected SegmentedButtonItem With Bigger Text</ui5-segmented-button-item>
<ui5-segmented-button-item selected>Item</ui5-segmented-button-item>
<ui5-segmented-button-item selected>selected SegmentedButtonItem</ui5-segmented-button-item>
<ui5-segmented-button-item selected>A</ui5-segmented-button-item>
</ui5-segmented-button>
</section>

<section>
<h1>SegmentedButton with Content Mode: Content Fit with set width</h1>

<ui5-segmented-button content-mode="ContentFit" selection-mode="Multiple" class="segmentedbutton2auto">
<ui5-segmented-button-item icon="employee" selected></ui5-segmented-button-item>
<ui5-segmented-button-item selected>Word</ui5-segmented-button-item>
<ui5-segmented-button-item selected>selected SegmentedButtonItem With Bigger Text</ui5-segmented-button-item>
<ui5-segmented-button-item selected>Item</ui5-segmented-button-item>
<ui5-segmented-button-item selected>selected SegmentedButtonItem</ui5-segmented-button-item>
<ui5-segmented-button-item selected>A</ui5-segmented-button-item>
</ui5-segmented-button>
</section>

<section>
<h1>SegmentedButton with Content Mode: Content Fit with set width (truncation)</h1>

<ui5-segmented-button content-mode="ContentFit" selection-mode="Multiple" class="segmentedbutton1auto">
<ui5-segmented-button-item selected>Word</ui5-segmented-button-item>
<ui5-segmented-button-item selected>selected SegmentedButtonItem With Bigger Text</ui5-segmented-button-item>
<ui5-segmented-button-item selected>Item</ui5-segmented-button-item>
<ui5-segmented-button-item selected>selected SegmentedButtonItem</ui5-segmented-button-item>
<ui5-segmented-button-item selected>A</ui5-segmented-button-item>
</ui5-segmented-button>
</section>


</div>
</div>
</section>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ slug: ../../SegmentedButton

import Basic from "../../../_samples/main/SegmentedButton/Basic/Basic.md";
import SelectionModes from "../../../_samples/main/SegmentedButton/SelectionModes/SelectionModes.md";
import ContentModes from "../../../_samples/main/SegmentedButton/ContentModes/ContentModes.md";

<%COMPONENT_OVERVIEW%>

Expand All @@ -17,4 +18,8 @@ import SelectionModes from "../../../_samples/main/SegmentedButton/SelectionMode
### Selection Modes
You can define if single or multiple buttons can be pressed.

<SelectionModes />
<SelectionModes />

### Content Modes
You can define the content behaviour of SegmentedButton items.
<ContentModes/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import html from '!!raw-loader!./sample.html';
import js from '!!raw-loader!./main.js';

<Editor html={html} js={js} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import "@ui5/webcomponents/dist/SegmentedButton.js";
import "@ui5/webcomponents/dist/SegmentedButtonItem.js";
import "@ui5/webcomponents-icons/dist/bold-text.js";
import "@ui5/webcomponents-icons/dist/underline-text.js";
import "@ui5/webcomponents-icons/dist/italic-text.js";
import "@ui5/webcomponents/dist/Title.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!-- playground-fold -->
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sample</title>
</head>

<body style="background-color: var(--sapBackgroundColor)">
<!-- playground-fold-end -->
<ui5-title>Content Mode: ContentFit</ui5-title>
<br>
<ui5-segmented-button accessible-name="Font style" content-mode="ContentFit">
<ui5-segmented-button-item selected>Map</ui5-segmented-button-item>
<ui5-segmented-button-item>Some Big Satellite</ui5-segmented-button-item>
<ui5-segmented-button-item>Terrain</ui5-segmented-button-item>
<ui5-segmented-button-item tooltip="Italic" icon="italic-text"></ui5-segmented-button-item>
</ui5-segmented-button>

<br><br>

<ui5-title>Content Mode: EqualSized</ui5-title>
<br>
<ui5-segmented-button accessible-name="Font style" content-mode="EqualSized">
<ui5-segmented-button-item selected>Map</ui5-segmented-button-item>
<ui5-segmented-button-item>Some Big Satellite</ui5-segmented-button-item>
<ui5-segmented-button-item>Terrain</ui5-segmented-button-item>
<ui5-segmented-button-item tooltip="Italic" icon="italic-text"></ui5-segmented-button-item>
</ui5-segmented-button>

<!-- playground-fold -->
<script type="module" src="main.js"></script>
</body>

</html>
<!-- playground-fold-end -->
Loading