diff --git a/src/components/stepper/step-label/readme.md b/src/components/stepper/step-label/readme.md index 5d8ac005a9..e58b231f9b 100644 --- a/src/components/stepper/step-label/readme.md +++ b/src/components/stepper/step-label/readme.md @@ -10,10 +10,10 @@ It has an implicit slot name: `step-label`. ## States -It can ben selected or `disabled`. +It can be `disabled`. ```html -Step label Step label +Step label ``` ## Style @@ -38,7 +38,6 @@ The aria values `aria-controls`, `aria-setsize`, `aria-posinset` are set automat | Name | Attribute | Privacy | Type | Default | Description | | ---------- | ----------- | ------- | ------------------------ | ---------- | -------------------------------------------------------------------------------------------------------------------------------- | -| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | | `form` | `form` | public | `string \| undefined` | | The
element to associate the button with. | | `iconName` | `icon-name` | public | `string \| undefined` | | The icon name we want to use, choose from the small icon variants from the ui-icons category from here https://icons.app.sbb.ch. | | `name` | `name` | public | `string` | | The name of the button element. | diff --git a/src/components/stepper/step-label/step-label.ts b/src/components/stepper/step-label/step-label.ts index 56af1a6769..e6e51fc62a 100644 --- a/src/components/stepper/step-label/step-label.ts +++ b/src/components/stepper/step-label/step-label.ts @@ -4,7 +4,6 @@ import { customElement } from 'lit/decorators.js'; import { SbbButtonBaseElement } from '../../core/base-elements.js'; import { SbbConnectedAbortController } from '../../core/controllers.js'; import { hostAttributes } from '../../core/decorators.js'; -import { SbbDisabledTabIndexActionMixin } from '../../core/mixins.js'; import { SbbIconNameMixin } from '../../icon.js'; import type { SbbStepElement } from '../step.js'; import type { SbbStepperElement } from '../stepper.js'; @@ -22,11 +21,10 @@ let nextId = 0; @customElement('sbb-step-label') @hostAttributes({ slot: 'step-label', + tabindex: '-1', role: 'tab', }) -export class SbbStepLabelElement extends SbbIconNameMixin( - SbbDisabledTabIndexActionMixin(SbbButtonBaseElement), -) { +export class SbbStepLabelElement extends SbbIconNameMixin(SbbButtonBaseElement) { public static override styles: CSSResultGroup = style; /** @internal */ @@ -44,6 +42,7 @@ export class SbbStepLabelElement extends SbbIconNameMixin( * @internal */ public select(): void { + this.tabIndex = 0; this._internals.ariaSelected = 'true'; this.toggleAttribute('data-selected', true); } @@ -53,6 +52,7 @@ export class SbbStepLabelElement extends SbbIconNameMixin( * @internal */ public deselect(): void { + this.tabIndex = -1; this._internals.ariaSelected = 'false'; this.toggleAttribute('data-selected', false); } diff --git a/src/components/stepper/stepper/stepper.scss b/src/components/stepper/stepper/stepper.scss index 0a8ea7acc8..d46feaad31 100644 --- a/src/components/stepper/stepper/stepper.scss +++ b/src/components/stepper/stepper/stepper.scss @@ -6,6 +6,7 @@ :host { --sbb-stepper-orientation: row; --sbb-stepper-marker-size: 0; + --sbb-stepper-marker-width: var(--sbb-border-width-3x); --sbb-stepper-animation-duration: var( --sbb-disable-animation-zero-time, var(--sbb-animation-duration-6x) @@ -54,7 +55,7 @@ &::before { inset-block-end: calc(var(--sbb-border-width-1x) * -1); - height: #{sbb.px-to-rem-build(3)}; + height: var(--sbb-stepper-marker-width); width: var(--sbb-stepper-marker-size); transition: width var(--sbb-stepper-animation-duration) var(--sbb-animation-easing); } @@ -66,7 +67,7 @@ &::before { inset-block-start: 0; - width: #{sbb.px-to-rem-build(3)}; + width: var(--sbb-stepper-marker-width); height: var(--sbb-stepper-marker-size); transition: height var(--sbb-stepper-animation-duration) var(--sbb-animation-easing); } diff --git a/src/components/stepper/stepper/stepper.ts b/src/components/stepper/stepper/stepper.ts index b2d9e4f91e..eded3eac41 100644 --- a/src/components/stepper/stepper/stepper.ts +++ b/src/components/stepper/stepper/stepper.ts @@ -7,6 +7,7 @@ import { } from 'lit'; import { customElement, property } from 'lit/decorators.js'; +import { getNextElementIndex, isArrowKeyPressed } from '../../core/a11y.js'; import { SbbConnectedAbortController } from '../../core/controllers.js'; import { breakpoints, isBreakpoint } from '../../core/dom.js'; import type { SbbHorizontalFrom, SbbOrientation } from '../../core/interfaces.js'; @@ -85,6 +86,10 @@ export class SbbStepperElement extends LitElement { return Array.from(this.querySelectorAll('sbb-step')); } + private get _enabledSteps(): SbbStepElement[] { + return this.steps.filter((s) => !s.label?.hasAttribute('disabled')); + } + /** * Selects the next step. */ @@ -155,9 +160,6 @@ export class SbbStepperElement extends LitElement { step.select(); this._setMarkerSize(); this._configureLinearMode(); - if (this._loaded) { - step.label?.focus(); - } } private _setMarkerSize(): void { @@ -248,6 +250,7 @@ export class SbbStepperElement extends LitElement { public override connectedCallback(): void { super.connectedCallback(); const signal = this._abort.signal; + this.addEventListener('keydown', (e) => this._handleKeyDown(e), { signal }); window.addEventListener('resize', () => this._onStepperResize(), { signal, passive: true, @@ -276,6 +279,26 @@ export class SbbStepperElement extends LitElement { } } + private _handleKeyDown(evt: KeyboardEvent): void { + const enabledSteps: SbbStepElement[] = this._enabledSteps; + + if ( + !enabledSteps || + // don't trap nested handling + ((evt.target as HTMLElement) !== this && (evt.target as HTMLElement).parentElement !== this) + ) { + return; + } + + if (isArrowKeyPressed(evt)) { + const current: number = enabledSteps.indexOf(this.selected!); + const nextIndex: number = getNextElementIndex(evt, current, enabledSteps.length); + this._select(enabledSteps[nextIndex]); + enabledSteps[nextIndex]?.label?.focus(); + evt.preventDefault(); + } + } + protected override render(): TemplateResult { return html`