diff --git a/.changeset/content-header-responsive-text.md b/.changeset/content-header-responsive-text.md new file mode 100644 index 00000000000..377d6789638 --- /dev/null +++ b/.changeset/content-header-responsive-text.md @@ -0,0 +1,8 @@ +--- +'@siemens/ix': minor +'@siemens/ix-angular': minor +'@siemens/ix-react': minor +'@siemens/ix-vue': minor +--- + +Add responsive wrapping for `ix-content-header` titles and subtitles and a `textOverflow="truncate"` option for compact single-line ellipsis behavior with native tooltips for both values. diff --git a/packages/angular-standalone-test-app/src/app/app.routes.ts b/packages/angular-standalone-test-app/src/app/app.routes.ts index 8e4893a92a7..df895ef48f9 100644 --- a/packages/angular-standalone-test-app/src/app/app.routes.ts +++ b/packages/angular-standalone-test-app/src/app/app.routes.ts @@ -128,6 +128,10 @@ export const routePaths: IxPreviewRoutes = { import('../preview-examples/content-header').then((m) => m.default), 'preview/content-header-no-back': () => import('../preview-examples/content-header-no-back').then((m) => m.default), + 'preview/content-header-truncate': () => + import('../preview-examples/content-header-truncate').then( + (m) => m.default + ), 'preview/content-header-with-slot': () => import('../preview-examples/content-header-with-slot').then( (m) => m.default diff --git a/packages/angular-standalone-test-app/src/preview-examples/content-header-truncate.html b/packages/angular-standalone-test-app/src/preview-examples/content-header-truncate.html new file mode 100644 index 00000000000..69744c3863e --- /dev/null +++ b/packages/angular-standalone-test-app/src/preview-examples/content-header-truncate.html @@ -0,0 +1,19 @@ + + + + Button1 + Button2 + Button3 + diff --git a/packages/angular-standalone-test-app/src/preview-examples/content-header-truncate.ts b/packages/angular-standalone-test-app/src/preview-examples/content-header-truncate.ts new file mode 100644 index 00000000000..f620d7861e0 --- /dev/null +++ b/packages/angular-standalone-test-app/src/preview-examples/content-header-truncate.ts @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2026 Siemens AG + * + * SPDX-License-Identifier: MIT + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { Component } from '@angular/core'; +import { IxButton, IxContentHeader } from '@siemens/ix-angular/standalone'; + +@Component({ + selector: 'app-example', + imports: [IxContentHeader, IxButton], + templateUrl: './content-header-truncate.html', +}) +export default class ContentHeaderTruncate {} diff --git a/packages/angular-standalone-test-app/src/preview-examples/content-header.html b/packages/angular-standalone-test-app/src/preview-examples/content-header.html index 9dc2c413a9f..8d026c8db04 100644 --- a/packages/angular-standalone-test-app/src/preview-examples/content-header.html +++ b/packages/angular-standalone-test-app/src/preview-examples/content-header.html @@ -9,8 +9,8 @@ Button1 Button2 diff --git a/packages/angular-test-app/src/app/app-routing.module.ts b/packages/angular-test-app/src/app/app-routing.module.ts index b3de23a66cd..b19a3f17372 100644 --- a/packages/angular-test-app/src/app/app-routing.module.ts +++ b/packages/angular-test-app/src/app/app-routing.module.ts @@ -62,6 +62,7 @@ import Chip from '../preview-examples/chip'; import ContentExample from '../preview-examples/content'; import ContentHeader from '../preview-examples/content-header'; import ContentHeaderNoBack from '../preview-examples/content-header-no-back'; +import ContentHeaderTruncate from '../preview-examples/content-header-truncate'; import ContentHeaderWithSlot from '../preview-examples/content-header-with-slot'; import CustomField from '../preview-examples/custom-field'; import CustomFieldValidation from '../preview-examples/custom-field-validation'; @@ -547,6 +548,10 @@ const routes: Routes = [ path: 'content-header', component: ContentHeader, }, + { + path: 'content-header-truncate', + component: ContentHeaderTruncate, + }, { path: 'content-header-with-slot', component: ContentHeaderWithSlot, diff --git a/packages/angular-test-app/src/app/app.module.ts b/packages/angular-test-app/src/app/app.module.ts index 600cb855076..f68db33af8b 100644 --- a/packages/angular-test-app/src/app/app.module.ts +++ b/packages/angular-test-app/src/app/app.module.ts @@ -71,6 +71,7 @@ import Chip from '../preview-examples/chip'; import ContentExample from '../preview-examples/content'; import ContentHeader from '../preview-examples/content-header'; import ContentHeaderNoBack from '../preview-examples/content-header-no-back'; +import ContentHeaderTruncate from '../preview-examples/content-header-truncate'; import ContentHeaderWithSlot from '../preview-examples/content-header-with-slot'; import CustomField from '../preview-examples/custom-field'; import CustomFieldValidation from '../preview-examples/custom-field-validation'; @@ -348,6 +349,7 @@ import WorkflowVertical from '../preview-examples/workflow-vertical'; ContentExample, ContentHeader, ContentHeaderNoBack, + ContentHeaderTruncate, ContentHeaderWithSlot, DatepickerRange, Datepicker, diff --git a/packages/angular-test-app/src/preview-examples/content-header-truncate.html b/packages/angular-test-app/src/preview-examples/content-header-truncate.html new file mode 100644 index 00000000000..69744c3863e --- /dev/null +++ b/packages/angular-test-app/src/preview-examples/content-header-truncate.html @@ -0,0 +1,19 @@ + + + + Button1 + Button2 + Button3 + diff --git a/packages/angular-test-app/src/preview-examples/content-header-truncate.ts b/packages/angular-test-app/src/preview-examples/content-header-truncate.ts new file mode 100644 index 00000000000..f513eb77e95 --- /dev/null +++ b/packages/angular-test-app/src/preview-examples/content-header-truncate.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: 2026 Siemens AG + * + * SPDX-License-Identifier: MIT + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { Component } from '@angular/core'; + +@Component({ + standalone: false, + selector: 'app-example', + templateUrl: './content-header-truncate.html', +}) +export default class ContentHeaderTruncate {} diff --git a/packages/angular-test-app/src/preview-examples/content-header.html b/packages/angular-test-app/src/preview-examples/content-header.html index 99504615115..17ee9f49fe8 100644 --- a/packages/angular-test-app/src/preview-examples/content-header.html +++ b/packages/angular-test-app/src/preview-examples/content-header.html @@ -9,8 +9,8 @@ Button1 Button2 diff --git a/packages/angular/src/components.ts b/packages/angular/src/components.ts index 07d38cb729d..13cb925740f 100644 --- a/packages/angular/src/components.ts +++ b/packages/angular/src/components.ts @@ -712,14 +712,14 @@ export declare interface IxContent extends Components.IxContent {} @ProxyCmp({ - inputs: ['hasBackButton', 'headerSubtitle', 'headerTitle', 'variant'] + inputs: ['hasBackButton', 'headerSubtitle', 'headerTitle', 'textOverflow', 'variant'] }) @Component({ selector: 'ix-content-header', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: ['hasBackButton', 'headerSubtitle', 'headerTitle', 'variant'], + inputs: ['hasBackButton', 'headerSubtitle', 'headerTitle', 'textOverflow', 'variant'], outputs: ['backButtonClick'], standalone: false }) diff --git a/packages/angular/standalone/src/components.ts b/packages/angular/standalone/src/components.ts index 6c406a74416..023358b0b60 100644 --- a/packages/angular/standalone/src/components.ts +++ b/packages/angular/standalone/src/components.ts @@ -823,14 +823,14 @@ export declare interface IxContent extends Components.IxContent {} @ProxyCmp({ defineCustomElementFn: defineIxContentHeader, - inputs: ['hasBackButton', 'headerSubtitle', 'headerTitle', 'variant'] + inputs: ['hasBackButton', 'headerSubtitle', 'headerTitle', 'textOverflow', 'variant'] }) @Component({ selector: 'ix-content-header', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: ['hasBackButton', 'headerSubtitle', 'headerTitle', 'variant'], + inputs: ['hasBackButton', 'headerSubtitle', 'headerTitle', 'textOverflow', 'variant'], outputs: ['backButtonClick'], }) export class IxContentHeader { diff --git a/packages/core/src/components.d.ts b/packages/core/src/components.d.ts index c555e9915ab..55e693e61d7 100644 --- a/packages/core/src/components.d.ts +++ b/packages/core/src/components.d.ts @@ -22,7 +22,7 @@ import { InputState } from "./components/category-filter/input-state"; import { ChatAttachmentStatus } from "./components/chat-attachment/chat-attachment.types"; import { ChipVariant } from "./components/chip/chip.types"; import { ColumnSize } from "./components/col/col.types"; -import { ContentHeaderVariant } from "./components/content-header/content-header.types"; +import { ContentHeaderTextOverflow, ContentHeaderVariant } from "./components/content-header/content-header.types"; import { CssGridTemplateType } from "./components/css-grid/css-grid.types"; import { ButtonVariant as ButtonVariant1 } from "./components/button/button"; import { DateDropdownOption, DateRangeChangeEvent } from "./components/date-dropdown/date-dropdown.types"; @@ -79,7 +79,7 @@ export { InputState } from "./components/category-filter/input-state"; export { ChatAttachmentStatus } from "./components/chat-attachment/chat-attachment.types"; export { ChipVariant } from "./components/chip/chip.types"; export { ColumnSize } from "./components/col/col.types"; -export { ContentHeaderVariant } from "./components/content-header/content-header.types"; +export { ContentHeaderTextOverflow, ContentHeaderVariant } from "./components/content-header/content-header.types"; export { CssGridTemplateType } from "./components/css-grid/css-grid.types"; export { ButtonVariant as ButtonVariant1 } from "./components/button/button"; export { DateDropdownOption, DateRangeChangeEvent } from "./components/date-dropdown/date-dropdown.types"; @@ -1051,6 +1051,12 @@ export namespace Components { * Title of Header */ "headerTitle"?: string; + /** + * Controls how the title and subtitle handle limited horizontal space. + * @since 5.2.0 + * @default 'wrap' + */ + "textOverflow": ContentHeaderTextOverflow; /** * Variant of content header * @default 'primary' @@ -8020,6 +8026,12 @@ declare namespace LocalJSX { * Triggered when back button is clicked */ "onBackButtonClick"?: (event: IxContentHeaderCustomEvent) => void; + /** + * Controls how the title and subtitle handle limited horizontal space. + * @since 5.2.0 + * @default 'wrap' + */ + "textOverflow"?: ContentHeaderTextOverflow; /** * Variant of content header * @default 'primary' @@ -12383,6 +12395,7 @@ declare namespace LocalJSX { "variant": ContentHeaderVariant; "headerTitle": string; "headerSubtitle": string | undefined; + "textOverflow": ContentHeaderTextOverflow; "hasBackButton": boolean; } interface IxCssGridItemAttributes { diff --git a/packages/core/src/components/content-header/content-header.scss b/packages/core/src/components/content-header/content-header.scss index b973297e24f..ab93d598df5 100644 --- a/packages/core/src/components/content-header/content-header.scss +++ b/packages/core/src/components/content-header/content-header.scss @@ -18,19 +18,30 @@ flex-direction: column; flex: 1 1 0; min-width: 0; - white-space: nowrap; margin-right: 0.5rem; - .titleOverflow { - overflow: hidden; - text-overflow: ellipsis; + .headerText { + min-width: 0; + overflow-wrap: anywhere; + + &.truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } } .headerTitleRow { display: flex; + .headerText { + flex: 0 1 auto; + } + .headerSlot { display: inline-flex; + flex: 0 0 auto; + align-self: flex-start; margin-left: 0.5rem; } } diff --git a/packages/core/src/components/content-header/content-header.tsx b/packages/core/src/components/content-header/content-header.tsx index ef271ac3f9e..d27c31f0a71 100644 --- a/packages/core/src/components/content-header/content-header.tsx +++ b/packages/core/src/components/content-header/content-header.tsx @@ -9,7 +9,10 @@ import { iconArrowLeft } from '@siemens/ix-icons/icons'; import { Component, Event, EventEmitter, h, Host, Prop } from '@stencil/core'; -import type { ContentHeaderVariant } from './content-header.types'; +import type { + ContentHeaderTextOverflow, + ContentHeaderVariant, +} from './content-header.types'; /** * @slot header - Content to be placed in the header area next to the title @@ -36,6 +39,13 @@ export class ContentHeader { */ @Prop() headerSubtitle: string | undefined = undefined; + /** + * Controls how the title and subtitle handle limited horizontal space. + * + * @since 5.2.0 + */ + @Prop({ reflect: true }) textOverflow: ContentHeaderTextOverflow = 'wrap'; + /** * Display a back button */ @@ -64,8 +74,12 @@ export class ContentHeader { format={this.variant === 'secondary' ? 'h4' : 'h3'} class={{ secondary: this.variant === 'secondary', - titleOverflow: true, + headerText: true, + truncate: this.textOverflow === 'truncate', }} + title={ + this.textOverflow === 'truncate' ? this.headerTitle : undefined + } > {this.headerTitle} @@ -79,9 +93,14 @@ export class ContentHeader { text-color={'soft'} class={{ subtitle: this.variant === 'secondary', - titleOverflow: true, + headerText: true, + truncate: this.textOverflow === 'truncate', }} - title={this.headerSubtitle} + title={ + this.textOverflow === 'truncate' + ? this.headerSubtitle + : undefined + } > {this.headerSubtitle} diff --git a/packages/core/src/components/content-header/content-header.types.ts b/packages/core/src/components/content-header/content-header.types.ts index 942be25721c..fb4d835f700 100644 --- a/packages/core/src/components/content-header/content-header.types.ts +++ b/packages/core/src/components/content-header/content-header.types.ts @@ -7,3 +7,5 @@ * LICENSE file in the root directory of this source tree. */ export type ContentHeaderVariant = 'primary' | 'secondary'; + +export type ContentHeaderTextOverflow = 'wrap' | 'truncate'; diff --git a/packages/core/src/components/content-header/test/content-header.ct.ts b/packages/core/src/components/content-header/test/content-header.ct.ts new file mode 100644 index 00000000000..ec907d35340 --- /dev/null +++ b/packages/core/src/components/content-header/test/content-header.ct.ts @@ -0,0 +1,281 @@ +/* + * SPDX-FileCopyrightText: 2026 Siemens AG + * + * SPDX-License-Identifier: MIT + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +import { expect, type Locator } from '@playwright/test'; +import { regressionTest, viewPorts } from '@utils/test'; + +const getTextLayout = (text: Locator) => + text.evaluate((element) => { + const style = getComputedStyle(element); + + return { + clientWidth: element.clientWidth, + height: element.getBoundingClientRect().height, + lineHeight: Number.parseFloat(style.lineHeight), + overflow: style.overflow, + scrollWidth: element.scrollWidth, + textOverflow: style.textOverflow, + whiteSpace: style.whiteSpace, + }; + }); + +regressionTest('accessibility', async ({ mount, makeAxeBuilder }) => { + await mount(` + + Online + Edit + + `); + + const results = await makeAxeBuilder().analyze(); + expect(results.violations).toEqual([]); +}); + +regressionTest('renders', async ({ mount, page }) => { + await mount(` + + `); + + const header = page.locator('ix-content-header'); + await expect(header).toHaveClass(/\bhydrated\b/); + await expect(header).toHaveAttribute('text-overflow', 'wrap'); + await expect(header).toHaveJSProperty('textOverflow', 'wrap'); + await expect(page.getByText('Content title', { exact: true })).toBeVisible(); + await expect(page.getByText('Subtitle', { exact: true })).toBeVisible(); +}); + +regressionTest( + 'wraps title and subtitle within the available component width by default', + async ({ mount, page }) => { + const title = + 'A responsive content header title that remains completely visible'; + const subtitle = + 'Supporting context also wraps naturally in constrained containers'; + + await mount(` + + Header action + + `); + + const header = page.locator('ix-content-header'); + await expect(header).toHaveClass(/\bhydrated\b/); + const titleText = header.getByText(title, { exact: true }); + const subtitleText = header.getByText(subtitle, { exact: true }); + + for (const text of [titleText, subtitleText]) { + const layout = await getTextLayout(text); + + expect(layout.height).toBeGreaterThan(layout.lineHeight); + expect(layout.scrollWidth).toBeLessThanOrEqual(layout.clientWidth); + await expect(text).not.toHaveAttribute('title'); + } + } +); + +regressionTest( + 'wraps at the small viewport and remains on one line at larger viewports', + async ({ mount, page }) => { + const title = + 'Responsive content header text uses the space available to the component'; + + await page.setViewportSize(viewPorts.sm); + await mount( + `` + ); + + const header = page.locator('ix-content-header'); + await expect(header).toHaveClass(/\bhydrated\b/); + const titleText = header.getByText(title, { exact: true }); + const getLineCount = async () => { + const layout = await getTextLayout(titleText); + return layout.height / layout.lineHeight; + }; + + expect(await getLineCount()).toBeGreaterThan(1); + + await page.setViewportSize(viewPorts.md); + expect(await getLineCount()).toBe(1); + + await page.setViewportSize(viewPorts.lg); + expect(await getLineCount()).toBe(1); + } +); + +regressionTest( + 'breaks long unbroken text without horizontal overflow', + async ({ mount, page }) => { + const title = + 'production-line-identifier-without-natural-break-opportunities-123456789'; + + await mount(` + + `); + + const header = page.locator('ix-content-header'); + await expect(header).toHaveClass(/\bhydrated\b/); + const titleText = header.getByText(title, { exact: true }); + const layout = await getTextLayout(titleText); + + expect(layout.height).toBeGreaterThan(layout.lineHeight); + expect(layout.scrollWidth).toBeLessThanOrEqual(layout.clientWidth); + } +); + +regressionTest( + 'keeps header content and actions aligned with the first title line', + async ({ mount, page }) => { + const title = + 'A constrained title that wraps while adjacent content keeps its place'; + const subtitle = 'Supporting context stays clear of the action area'; + + await mount(` + + Online + Non-wrapping action + + `); + + const header = page.locator('ix-content-header'); + await expect(header).toHaveClass(/\bhydrated\b/); + const titleText = header.getByText(title, { exact: true }); + const subtitleText = header.getByText(subtitle, { exact: true }); + const pill = header.getByText('Online', { exact: true }); + const action = page.getByRole('button', { name: 'Non-wrapping action' }); + const backButton = page.getByRole('button', { name: 'Arrow Left' }); + + const titleBox = await titleText.boundingBox(); + const subtitleBox = await subtitleText.boundingBox(); + const pillBox = await pill.boundingBox(); + const actionBox = await action.boundingBox(); + const backButtonBox = await backButton.boundingBox(); + const titleLineHeight = await titleText.evaluate((element) => + Number.parseFloat(getComputedStyle(element).lineHeight) + ); + + expect(titleBox).not.toBeNull(); + expect(subtitleBox).not.toBeNull(); + expect(pillBox).not.toBeNull(); + expect(actionBox).not.toBeNull(); + expect(backButtonBox).not.toBeNull(); + expect(titleBox!.height).toBeGreaterThan(titleLineHeight); + expect(pillBox!.y).toBeLessThan(titleBox!.y + titleLineHeight); + expect(actionBox!.y).toBeLessThan(titleBox!.y + titleLineHeight); + expect(backButtonBox!.y).toBeLessThan(titleBox!.y + titleLineHeight); + expect(titleBox!.x + titleBox!.width).toBeLessThanOrEqual(pillBox!.x); + expect(subtitleBox!.x + subtitleBox!.width).toBeLessThanOrEqual( + actionBox!.x + ); + + await header.evaluate((element) => { + element.style.width = '80rem'; + }); + const wideActionBox = await action.boundingBox(); + expect(wideActionBox).not.toBeNull(); + expect(actionBox!.height).toBe(wideActionBox!.height); + expect(actionBox!.width).toBe(wideActionBox!.width); + } +); + +regressionTest( + 'truncates title and subtitle on separate lines with complete native tooltips', + async ({ mount, page }) => { + const title = 'A long title that is truncated in compact layouts'; + const subtitle = 'A long subtitle that is truncated independently'; + + await mount(` + + `); + + const header = page.locator('ix-content-header'); + await expect(header).toHaveClass(/\bhydrated\b/); + const titleText = header.getByText(title, { exact: true }); + const subtitleText = header.getByText(subtitle, { exact: true }); + + for (const [text, completeValue] of [ + [titleText, title], + [subtitleText, subtitle], + ] as const) { + const overflow = await getTextLayout(text); + + expect(overflow.height).toBe(overflow.lineHeight); + expect(overflow.scrollWidth).toBeGreaterThan(overflow.clientWidth); + expect(overflow.overflow).toBe('hidden'); + expect(overflow.textOverflow).toBe('ellipsis'); + expect(overflow.whiteSpace).toBe('nowrap'); + await expect(text).toHaveAttribute('title', completeValue); + } + + const titleBox = await titleText.boundingBox(); + const subtitleBox = await subtitleText.boundingBox(); + expect(titleBox).not.toBeNull(); + expect(subtitleBox).not.toBeNull(); + expect(titleBox!.y + titleBox!.height).toBeLessThanOrEqual(subtitleBox!.y); + } +); + +regressionTest( + 'updates overflow behavior when textOverflow changes at runtime', + async ({ mount, page }) => { + const title = 'A title that switches between wrapping and truncation'; + + await mount(` + + `); + + const header = page.locator('ix-content-header'); + await expect(header).toHaveClass(/\bhydrated\b/); + const titleText = header.getByText(title, { exact: true }); + const getHeight = () => + titleText.evaluate((element) => element.getBoundingClientRect().height); + + const wrappedHeight = await getHeight(); + + await header.evaluate((element: HTMLIxContentHeaderElement) => { + element.textOverflow = 'truncate'; + }); + + await expect(header).toHaveAttribute('text-overflow', 'truncate'); + await expect(titleText).toHaveAttribute('title', title); + await expect.poll(getHeight).toBeLessThan(wrappedHeight); + + await header.evaluate((element: HTMLIxContentHeaderElement) => { + element.textOverflow = 'wrap'; + }); + + await expect(header).toHaveAttribute('text-overflow', 'wrap'); + await expect(titleText).not.toHaveAttribute('title'); + await expect.poll(getHeight).toBe(wrappedHeight); + } +); diff --git a/packages/html-test-app/src/preview-examples/content-header-truncate.html b/packages/html-test-app/src/preview-examples/content-header-truncate.html new file mode 100644 index 00000000000..c3b0500d288 --- /dev/null +++ b/packages/html-test-app/src/preview-examples/content-header-truncate.html @@ -0,0 +1,30 @@ + + + + + + + + Content header truncation example + + + + Button1 + Button2 + Button3 + + + + diff --git a/packages/html-test-app/src/preview-examples/content-header.html b/packages/html-test-app/src/preview-examples/content-header.html index 5eecdaf7146..85da75f6410 100644 --- a/packages/html-test-app/src/preview-examples/content-header.html +++ b/packages/html-test-app/src/preview-examples/content-header.html @@ -17,8 +17,8 @@ Button1 Button2 diff --git a/packages/react-test-app/src/main.tsx b/packages/react-test-app/src/main.tsx index e1da8f0e39d..5aa0f686812 100644 --- a/packages/react-test-app/src/main.tsx +++ b/packages/react-test-app/src/main.tsx @@ -67,6 +67,7 @@ import Chip from './preview-examples/chip'; import Content from './preview-examples/content'; import ContentHeader from './preview-examples/content-header'; import ContentHeaderNoBack from './preview-examples/content-header-no-back'; +import ContentHeaderTruncate from './preview-examples/content-header-truncate'; import ContentHeaderWithSlot from './preview-examples/content-header-with-slot'; import CustomField from './preview-examples/custom-field'; import CustomFieldValidation from './preview-examples/custom-field-validation'; @@ -341,6 +342,7 @@ const routes: IxPreviewRoutes = { '/preview/datetime-range': DatetimeRange, '/preview/content-header-no-back': ContentHeaderNoBack, '/preview/content-header': ContentHeader, + '/preview/content-header-truncate': ContentHeaderTruncate, '/preview/content-header-with-slot': ContentHeaderWithSlot, '/preview/datepicker-range': DatepickerRange, '/preview/datepicker': Datepicker, diff --git a/packages/react-test-app/src/preview-examples/content-header-truncate.tsx b/packages/react-test-app/src/preview-examples/content-header-truncate.tsx new file mode 100644 index 00000000000..bfce96b05d6 --- /dev/null +++ b/packages/react-test-app/src/preview-examples/content-header-truncate.tsx @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2026 Siemens AG + * + * SPDX-License-Identifier: MIT + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { IxButton, IxContentHeader } from '@siemens/ix-react'; + +export default () => { + return ( + + Button1 + Button2 + Button3 + + ); +}; diff --git a/packages/react-test-app/src/preview-examples/content-header.tsx b/packages/react-test-app/src/preview-examples/content-header.tsx index 7da1b544556..4a51bfb16b5 100644 --- a/packages/react-test-app/src/preview-examples/content-header.tsx +++ b/packages/react-test-app/src/preview-examples/content-header.tsx @@ -12,9 +12,9 @@ import { IxButton, IxContentHeader } from '@siemens/ix-react'; export default () => { return ( Button1 Button2 diff --git a/packages/react/src/components/components.server.ts b/packages/react/src/components/components.server.ts index f348760a3fa..cabd8f1c721 100644 --- a/packages/react/src/components/components.server.ts +++ b/packages/react/src/components/components.server.ts @@ -590,6 +590,7 @@ export const IxContentHeader: StencilReactComponent) : undefined, diff --git a/packages/vue-test-app/src/Root.vue b/packages/vue-test-app/src/Root.vue index a6350968be0..5a12c179489 100644 --- a/packages/vue-test-app/src/Root.vue +++ b/packages/vue-test-app/src/Root.vue @@ -64,6 +64,7 @@ import Checkbox from './preview-examples/checkbox.vue'; import Chip from './preview-examples/chip.vue'; import ContentHeaderNoBack from './preview-examples/content-header-no-back.vue'; import ContentHeader from './preview-examples/content-header.vue'; +import ContentHeaderTruncate from './preview-examples/content-header-truncate.vue'; import ContentHeaderWithSlot from './preview-examples/content-header-with-slot.vue'; import Content from './preview-examples/content.vue'; import CustomFieldValidation from './preview-examples/custom-field-validation.vue'; @@ -323,6 +324,7 @@ const routes: IxPreviewRoutes = { '/preview/datetime-range': DatetimeRange, '/preview/content-header-no-back': ContentHeaderNoBack, '/preview/content-header': ContentHeader, + '/preview/content-header-truncate': ContentHeaderTruncate, '/preview/content-header-with-slot': ContentHeaderWithSlot, '/preview/datepicker-range': DatepickerRange, '/preview/datepicker': Datepicker, diff --git a/packages/vue-test-app/src/preview-examples/content-header-truncate.vue b/packages/vue-test-app/src/preview-examples/content-header-truncate.vue new file mode 100644 index 00000000000..341ab36daa8 --- /dev/null +++ b/packages/vue-test-app/src/preview-examples/content-header-truncate.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/packages/vue-test-app/src/preview-examples/content-header.vue b/packages/vue-test-app/src/preview-examples/content-header.vue index cc97abfafe7..e70683a98d2 100644 --- a/packages/vue-test-app/src/preview-examples/content-header.vue +++ b/packages/vue-test-app/src/preview-examples/content-header.vue @@ -14,8 +14,8 @@ import { IxButton, IxContentHeader } from '@siemens/ix-vue';