diff --git a/.changeset/swift-lamps-itch.md b/.changeset/swift-lamps-itch.md
new file mode 100644
index 0000000000..2bf1faca00
--- /dev/null
+++ b/.changeset/swift-lamps-itch.md
@@ -0,0 +1,5 @@
+---
+'@baloise/ds-core': minor
+---
+
+**segment**: new component
diff --git a/docs/stories/components/bal-icon/theming.md b/docs/stories/components/bal-icon/theming.md
index 8a2c36c8ec..76cf09c368 100644
--- a/docs/stories/components/bal-icon/theming.md
+++ b/docs/stories/components/bal-icon/theming.md
@@ -16,6 +16,7 @@ The component can be customization by changing the CSS variables.
| --------------------------------- |
| `--bal-icon-color-grey` |
| `--bal-icon-color-grey-light` |
+| `--bal-icon-color-grey-dark` |
| `--bal-icon-color-success` |
| `--bal-icon-color-success-dark` |
| `--bal-icon-color-success-darker` |
diff --git a/docs/stories/components/bal-segment/bal-segment.mdx b/docs/stories/components/bal-segment/bal-segment.mdx
new file mode 100644
index 0000000000..2c1dd3fd7f
--- /dev/null
+++ b/docs/stories/components/bal-segment/bal-segment.mdx
@@ -0,0 +1,62 @@
+import { Canvas, Meta, Markdown } from '@storybook/blocks'
+import { Banner, Lead, PlaygroundBar, StoryHeading, Footer } from '../../../.storybook/blocks'
+import * as SegmentStories from './bal-segment.stories'
+
+
+
+
+
+
+
+
+ **Segment** offers a list of options. The user can only select one option from a number of choices. It automatically
+ detects if it has enough space and changes to Vertical.
+
+
+
+
+
+
+{/* STORIES */}
+{/* ------------------------------------------------------ */}
+
+
+
+The segment items can be displayed as a list just set `vertical`.
+
+
+
+{/* ------------------------------------------------------ */}
+
+
+
+Set `expanded` to stretch the component over the full width.
+
+
+
+{/* ------------------------------------------------------ */}
+
+## Component API
+
+import api from './api.md?raw'
+import apiItem from './bal-segment-item/api.md?raw'
+
+{api}
+
+{apiItem}
+
+## Integration
+
+import integration from '../../snippets/integration.md?raw'
+
+{integration}
+
+import theming from './theming.md?raw'
+
+{theming}
+
+import testing from './testing.md?raw'
+
+{testing}
+
+
diff --git a/docs/stories/components/bal-segment/bal-segment.stories.ts b/docs/stories/components/bal-segment/bal-segment.stories.ts
new file mode 100644
index 0000000000..5be8bc170c
--- /dev/null
+++ b/docs/stories/components/bal-segment/bal-segment.stories.ts
@@ -0,0 +1,53 @@
+import type { JSX } from '@baloise/ds-core'
+import type { Meta } from '@storybook/html'
+import { props, withRender, withComponentControls, StoryFactory } from '../../utils'
+
+type Args = JSX.BalSegment
+
+const meta: Meta = {
+ title: 'Components/Form/Segment',
+ args: {
+ value: 'yes',
+ },
+ argTypes: {
+ ...withComponentControls({ tag: 'bal-segment' }),
+ },
+ ...withRender(
+ ({ ...args }) => `
+
+
+`,
+ ),
+}
+
+export default meta
+
+/**
+ * STORIES
+ * ------------------------------------------------------
+ */
+
+const Story = StoryFactory(meta)
+
+export const Basic = Story()
+
+export const Vertical = Story({
+ ...withRender(
+ () => `
+
+ entspricht rund 186 km pro Woche
+ entspricht rund 186 km pro Woche
+ entspricht rund 186 km pro Woche
+ entspricht rund 186 km pro Woche
+ entspricht rund 186 km pro Woche
+`,
+ ),
+})
+
+export const Expanded = Story({
+ args: {
+ expanded: true,
+ },
+})
diff --git a/docs/stories/components/bal-segment/testing.md b/docs/stories/components/bal-segment/testing.md
new file mode 100644
index 0000000000..98d460be9e
--- /dev/null
+++ b/docs/stories/components/bal-segment/testing.md
@@ -0,0 +1,12 @@
+## Testing
+
+The Baloise Design System provides a collection of custom cypress commands for the components. Moreover, some basic cypress commands like `should` or `click` have been overridden to work with the components.
+
+Go to testing guide
+
+
+
+
+
+
+
diff --git a/docs/stories/components/bal-segment/theming.md b/docs/stories/components/bal-segment/theming.md
new file mode 100644
index 0000000000..4364169c36
--- /dev/null
+++ b/docs/stories/components/bal-segment/theming.md
@@ -0,0 +1,34 @@
+## Theming
+
+The component can be customization by changing the CSS variables.
+
+Go to theming guide
+
+
+
+
+
+
+
+### Variables
+
+| Variable |
+| -------------------------------------------------- |
+| `--bal-badge-background` |
+| `--bal-segment-background` |
+| `--bal-segment-background-invalid` |
+| `--bal-segment-item-focus-border` |
+| `--bal-segment-item-text-color` |
+| `--bal-segment-item-text-color-checked` |
+| `--bal-segment-item-text-color-checked-hovered` |
+| `--bal-segment-item-text-color-checked-pressed` |
+| `--bal-segment-item-text-color-invalid` |
+| `--bal-segment-item-text-color-invalid-hovered` |
+| `--bal-segment-item-text-color-invalid-pressed` |
+| `--bal-segment-item-text-color-disabled` |
+| `--bal-segment-item-text-color-disabled-checked` |
+| `--bal-segment-item-divider-background` |
+| `--bal-segment-item-divider-background-invalid` |
+| `--bal-segment-item-indicator-background` |
+| `--bal-segment-item-indicator-background-invalid` |
+| `--bal-segment-item-indicator-background-disabled` |
diff --git a/e2e/cypress/component/bal-segment.cy.ts b/e2e/cypress/component/bal-segment.cy.ts
new file mode 100644
index 0000000000..41d8f52e53
--- /dev/null
+++ b/e2e/cypress/component/bal-segment.cy.ts
@@ -0,0 +1,46 @@
+import { Components } from '../support/utils'
+
+describe('bal-segment', () => {
+ it('should fire change event', () => {
+ const onBalChangeSpy = cy.spy().as('balChange')
+
+ cy.mount(
+ `
+
+
+ `,
+ {
+ props: {},
+ events: {
+ balChange: onBalChangeSpy,
+ },
+ },
+ )
+
+ cy.get('bal-segment-item').contains('Yes').closest('bal-segment-item').click()
+ cy.get('@balChange').should('have.been.calledOnce')
+ cy.get('@balChange').shouldHaveEventDetail('yes')
+ })
+
+ it('should not fire change event', () => {
+ const onBalChangeSpy = cy.spy().as('balChange')
+
+ cy.mount(
+ `
+
+
+ `,
+ {
+ props: {
+ value: 'yes',
+ },
+ events: {
+ balChange: onBalChangeSpy,
+ },
+ },
+ )
+
+ cy.get('bal-segment-item').contains('Yes').closest('bal-segment-item').click()
+ cy.get('@balChange').should('not.have.been.called')
+ })
+})
diff --git a/e2e/cypress/e2e/a11y/bal-segment.a11y.cy.ts b/e2e/cypress/e2e/a11y/bal-segment.a11y.cy.ts
new file mode 100644
index 0000000000..8e1714f7a3
--- /dev/null
+++ b/e2e/cypress/e2e/a11y/bal-segment.a11y.cy.ts
@@ -0,0 +1,11 @@
+describe('bal-segment', () => {
+ context('a11y', () => {
+ beforeEach(() => cy.platform('desktop').pageA11y('/components/bal-segment/test/bal-segment.a11y.html'))
+
+ describe('have the AA standard', () => {
+ it('basic', () => {
+ cy.getByTestId('basic').testA11y()
+ })
+ })
+ })
+})
diff --git a/e2e/cypress/e2e/visual/bal-segment.visual.cy.ts b/e2e/cypress/e2e/visual/bal-segment.visual.cy.ts
new file mode 100644
index 0000000000..0df0bc5feb
--- /dev/null
+++ b/e2e/cypress/e2e/visual/bal-segment.visual.cy.ts
@@ -0,0 +1,27 @@
+describe('bal-segment', () => {
+ beforeEach(() => cy.visit('/components/bal-segment/test/bal-segment.visual.html').waitForDesignSystem())
+
+ it('basic component', () => {
+ cy.platform('desktop')
+ cy.getByTestId('basic').testVisual('icon-desktop')
+
+ cy.platform('mobile')
+ cy.getByTestId('basic').testVisual('icon-mobile')
+ })
+
+ it('component horizontal', () => {
+ cy.platform('desktop')
+ cy.getByTestId('horizontal').testVisual('horizontal-desktop')
+
+ cy.platform('mobile')
+ cy.getByTestId('horizontal').testVisual('horizontal-mobile')
+ })
+
+ it('component vertical', () => {
+ cy.platform('desktop')
+ cy.getByTestId('vertical').testVisual('vertical-desktop')
+
+ cy.platform('mobile')
+ cy.getByTestId('vertical').testVisual('vertical-mobile')
+ })
+})
diff --git a/e2e/cypress/snapshots/base/visual/bal-segment.visual.cy.ts/horizontal-desktop.png b/e2e/cypress/snapshots/base/visual/bal-segment.visual.cy.ts/horizontal-desktop.png
new file mode 100644
index 0000000000..21205c4fdf
Binary files /dev/null and b/e2e/cypress/snapshots/base/visual/bal-segment.visual.cy.ts/horizontal-desktop.png differ
diff --git a/e2e/cypress/snapshots/base/visual/bal-segment.visual.cy.ts/horizontal-mobile.png b/e2e/cypress/snapshots/base/visual/bal-segment.visual.cy.ts/horizontal-mobile.png
new file mode 100644
index 0000000000..ca2cc8bba9
Binary files /dev/null and b/e2e/cypress/snapshots/base/visual/bal-segment.visual.cy.ts/horizontal-mobile.png differ
diff --git a/e2e/cypress/snapshots/base/visual/bal-segment.visual.cy.ts/icon-desktop.png b/e2e/cypress/snapshots/base/visual/bal-segment.visual.cy.ts/icon-desktop.png
new file mode 100644
index 0000000000..0a06cb1d83
Binary files /dev/null and b/e2e/cypress/snapshots/base/visual/bal-segment.visual.cy.ts/icon-desktop.png differ
diff --git a/e2e/cypress/snapshots/base/visual/bal-segment.visual.cy.ts/icon-mobile.png b/e2e/cypress/snapshots/base/visual/bal-segment.visual.cy.ts/icon-mobile.png
new file mode 100644
index 0000000000..ac4b2b8372
Binary files /dev/null and b/e2e/cypress/snapshots/base/visual/bal-segment.visual.cy.ts/icon-mobile.png differ
diff --git a/e2e/cypress/snapshots/base/visual/bal-segment.visual.cy.ts/vertical-desktop.png b/e2e/cypress/snapshots/base/visual/bal-segment.visual.cy.ts/vertical-desktop.png
new file mode 100644
index 0000000000..66aa40a836
Binary files /dev/null and b/e2e/cypress/snapshots/base/visual/bal-segment.visual.cy.ts/vertical-desktop.png differ
diff --git a/e2e/cypress/snapshots/base/visual/bal-segment.visual.cy.ts/vertical-mobile.png b/e2e/cypress/snapshots/base/visual/bal-segment.visual.cy.ts/vertical-mobile.png
new file mode 100644
index 0000000000..ebc3e65355
Binary files /dev/null and b/e2e/cypress/snapshots/base/visual/bal-segment.visual.cy.ts/vertical-mobile.png differ
diff --git a/e2e/package.json b/e2e/package.json
index f457594471..795444ca74 100644
--- a/e2e/package.json
+++ b/e2e/package.json
@@ -9,7 +9,7 @@
"dependencies": {
"@baloise/ds-core": "16.4.0",
"@baloise/ds-testing": "16.4.0",
- "@baloise/web-app-utils": "~3.11.2",
+ "@baloise/web-app-utils": "3.15.0",
"@cypress/mount-utils": "^4.1.0",
"axe-core": "~4.8.4",
"compression": "~1.7.4",
diff --git a/package-lock.json b/package-lock.json
index d975ea42e5..7bc8091d34 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -177,7 +177,7 @@
"dependencies": {
"@baloise/ds-core": "16.4.0",
"@baloise/ds-testing": "16.4.0",
- "@baloise/web-app-utils": "~3.11.2",
+ "@baloise/web-app-utils": "3.15.0",
"@cypress/mount-utils": "^4.1.0",
"axe-core": "~4.8.4",
"compression": "~1.7.4",
@@ -4108,9 +4108,9 @@
"link": true
},
"node_modules/@baloise/web-app-utils": {
- "version": "3.11.2",
- "resolved": "https://registry.npmjs.org/@baloise/web-app-utils/-/web-app-utils-3.11.2.tgz",
- "integrity": "sha512-fgHv6p3EPbuYYanIvwTHAhx3Rj8mNYsIMKH5KRvbziYPauVrv+gbd9SWyXsxKO9Po13lMRcwExAh7rs+fNH2nA==",
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/@baloise/web-app-utils/-/web-app-utils-3.15.0.tgz",
+ "integrity": "sha512-MjXNy3HC0y2PLer3t/OPt6KBetevIXhWgjYUvpUd9TBwZSaFv3rW75qqSOfPjRUBXUtBVpILj52l3llcivzYcw==",
"dependencies": {
"date-fns": "^2.28.0",
"lodash.camelcase": "^4.3.0",
@@ -48704,7 +48704,7 @@
"dependencies": {
"@baloise/ds-icons": "16.4.0",
"@baloise/ds-styles": "16.4.0",
- "@baloise/web-app-utils": "3.11.2",
+ "@baloise/web-app-utils": "3.15.0",
"@floating-ui/dom": "~1.6.3",
"@popperjs/core": "~2.11.4",
"@stencil/core": "4.11.0",
diff --git a/package.json b/package.json
index 3656add626..6b7de729fb 100644
--- a/package.json
+++ b/package.json
@@ -140,5 +140,6 @@
"vue": "~3.4.15",
"vue-tsc": "~1.8.8",
"zone.js": "~0.11.4"
- }
+ },
+ "packageManager": "pnpm@8.5.0+sha1.83c41fe4ebdb61e08446f8bb68a75fb48756b330"
}
diff --git a/packages/angular/src/bundles.ts b/packages/angular/src/bundles.ts
index 2772da378a..cd4db3b932 100644
--- a/packages/angular/src/bundles.ts
+++ b/packages/angular/src/bundles.ts
@@ -13,6 +13,8 @@ import {
BalSelect,
BalTextarea,
BalTimeInput,
+ BalSegment,
+ BalFileUpload,
} from './components'
import { BalNgErrorComponent } from './directives/error.component'
import {
@@ -72,6 +74,7 @@ import {
BalNavbarMenuStart,
BalRadio,
BalRadioButton,
+ BalSegmentItem,
BalSelectOption,
BalShape,
BalStack,
@@ -160,6 +163,8 @@ export const BalFieldBundle = [
export const BalDropdownBundle = [BalDropdown, BalOptionList, BalOption] as const
+export const BalSegmentBundle = [BalSegment, BalSegmentItem] as const
+
/* Component Sections */
export const BalFormBundle = [
@@ -184,6 +189,8 @@ export const BalFormBundle = [
BalNumberInput,
...BalRadioBundle,
...BalSelectBundle,
+ ...BalSegmentBundle,
+ BalFileUpload,
BalTextarea,
BalTimeInput,
] as const
@@ -263,6 +270,9 @@ export const BalComponentBundle = [
BalStageImage,
BalStepItem,
BalSteps,
+ BalSegment,
+ BalSegmentItem,
+ BalFileUpload,
BalTabItem,
BalTabs,
BalTag,
diff --git a/packages/angular/src/components/bal-segment.ts b/packages/angular/src/components/bal-segment.ts
new file mode 100644
index 0000000000..cc5b6a1099
--- /dev/null
+++ b/packages/angular/src/components/bal-segment.ts
@@ -0,0 +1,67 @@
+import {
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Component,
+ ElementRef,
+ EventEmitter,
+ HostListener,
+ Injector,
+ NgZone,
+ forwardRef,
+} from '@angular/core'
+import { NG_VALUE_ACCESSOR } from '@angular/forms'
+
+import type { Components } from '@baloise/ds-core'
+import { defineCustomElement } from '@baloise/ds-core/components/bal-segment'
+
+import { ProxyCmp, proxyOutputs } from '../generated/angular-component-lib/utils'
+import { ValueAccessor } from '../generated/value-accessor'
+import { BalSegmentInputs, BalSegmentMethods, BalSegmentOutputs } from '../generated/meta'
+
+const accessorProvider = {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: /*@__PURE__*/ forwardRef(() => BalSegment),
+ multi: true,
+}
+
+@ProxyCmp({
+ defineCustomElementFn: defineCustomElement,
+ inputs: BalSegmentInputs,
+ methods: BalSegmentMethods,
+})
+@Component({
+ selector: 'bal-segment',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ template: '',
+ providers: [accessorProvider],
+ standalone: true,
+ inputs: BalSegmentInputs,
+ outputs: BalSegmentOutputs,
+})
+export class BalSegment extends ValueAccessor {
+ protected el: HTMLElement
+
+ constructor(
+ c: ChangeDetectorRef,
+ r: ElementRef,
+ protected z: NgZone,
+ injector: Injector,
+ ) {
+ super(injector, r)
+ c.detach()
+ this.el = r.nativeElement
+ proxyOutputs(this, this.el, BalSegmentOutputs)
+ }
+
+ @HostListener('balChange', ['$event'])
+ handleBalChange(event: CustomEvent): void {
+ this.handleValueChange(event)
+ }
+}
+
+export declare interface BalSegment extends Components.BalSegment {
+ /** Emitted when the checked property has changed. */
+ balChange: EventEmitter>
+ /** Emitted when the toggle loses focus. */
+ balBlur: EventEmitter>
+}
diff --git a/packages/angular/src/components/index.ts b/packages/angular/src/components/index.ts
index fe1a2a9b97..1be133d4af 100644
--- a/packages/angular/src/components/index.ts
+++ b/packages/angular/src/components/index.ts
@@ -10,6 +10,7 @@ export * from './bal-input'
export * from './bal-number-input'
export * from './bal-radio-group'
export * from './bal-select'
+export * from './bal-segment'
export * from './bal-dropdown'
export * from './bal-textarea'
export * from './bal-time-input'
diff --git a/packages/core/config/stencil.bindings.angular.ts b/packages/core/config/stencil.bindings.angular.ts
index 87979219f6..a36ba32148 100644
--- a/packages/core/config/stencil.bindings.angular.ts
+++ b/packages/core/config/stencil.bindings.angular.ts
@@ -5,6 +5,7 @@ export const angularValueAccessorBindings: ValueAccessorConfig[] = [
{
elementSelectors: [
'bal-radio-group',
+ 'bal-segment',
'bal-checkbox-group',
'bal-select',
'bal-dropdown',
@@ -46,6 +47,7 @@ export const AngularGenerator = () =>
valueAccessorConfigs: angularValueAccessorBindings,
excludeComponents: [
...docComponents,
+ 'bal-segment',
'bal-checkbox-group',
'bal-checkbox',
'bal-date',
diff --git a/packages/core/config/stencil.bindings.vue.ts b/packages/core/config/stencil.bindings.vue.ts
index 5419b5d7dc..88e81726a5 100644
--- a/packages/core/config/stencil.bindings.vue.ts
+++ b/packages/core/config/stencil.bindings.vue.ts
@@ -3,6 +3,7 @@ import { vueOutputTarget } from '@baloise/output-target-vue'
export const vueComponentModels: any[] = [
{
elements: [
+ 'bal-segment',
'bal-radio-group',
'bal-datepicker',
'bal-select',
diff --git a/packages/core/package.json b/packages/core/package.json
index 02d3830894..9d1c98a4d9 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -30,7 +30,7 @@
"dependencies": {
"@baloise/ds-styles": "16.4.0",
"@baloise/ds-icons": "16.4.0",
- "@baloise/web-app-utils": "3.11.2",
+ "@baloise/web-app-utils": "3.15.0",
"@floating-ui/dom": "~1.6.3",
"@popperjs/core": "~2.11.4",
"@stencil/core": "4.11.0",
diff --git a/packages/core/src/components.d.ts b/packages/core/src/components.d.ts
index 9dad3901aa..258b208f71 100644
--- a/packages/core/src/components.d.ts
+++ b/packages/core/src/components.d.ts
@@ -14,6 +14,7 @@ import { BalOption } from "./utils/dropdown";
import { OverlayEventDetail } from "./components/bal-modal/bal-modal.type";
import { PopoverPresentOptions } from "./components/bal-popover/bal-popover";
import { BalRadioOption } from "./components/bal-radio/bal-radio.type";
+import { SegmentValue } from "./components/bal-segment/bal-segment.types";
import { BalStepOption } from "./components/bal-steps/bal-step.type";
import { BalTabOption } from "./components/bal-tabs/bal-tab.type";
export { BalConfigState } from "./utils/config";
@@ -25,6 +26,7 @@ export { BalOption } from "./utils/dropdown";
export { OverlayEventDetail } from "./components/bal-modal/bal-modal.type";
export { PopoverPresentOptions } from "./components/bal-popover/bal-popover";
export { BalRadioOption } from "./components/bal-radio/bal-radio.type";
+export { SegmentValue } from "./components/bal-segment/bal-segment.types";
export { BalStepOption } from "./components/bal-steps/bal-step.type";
export { BalTabOption } from "./components/bal-tabs/bal-tab.type";
export namespace Components {
@@ -1752,6 +1754,10 @@ export namespace Components {
* The value of the for attribute must be a single id for a labeled form-related element in the same document as the