From 3b639e70b9f229094d233f3465ff4907d1afcf2b Mon Sep 17 00:00:00 2001 From: Jeremias Peier Date: Mon, 9 Sep 2024 11:59:42 +0200 Subject: [PATCH 1/3] build: enable eslint member ordering rule --- eslint.config.js | 24 +++++++ src/elements/accordion/accordion.ts | 48 +++++++------- src/elements/action-group/action-group.ts | 12 ++-- src/elements/alert/alert/alert.ts | 12 ++-- .../autocomplete-grid-button.ts | 8 +-- .../autocomplete/autocomplete-base-element.ts | 9 ++- src/elements/calendar/calendar.ts | 10 +-- src/elements/clock/clock.ts | 6 +- .../a11y/focus-visible-within-controller.ts | 8 +-- .../core/a11y/input-modality-detector.ts | 28 ++++---- .../core/base-elements/action-base-element.ts | 1 + .../core/base-elements/button-base-element.ts | 44 ++++++------- .../core/controllers/slot-state-controller.ts | 12 ++-- .../datepicker/datepicker/datepicker.ts | 5 ++ src/elements/file-selector/file-selector.ts | 14 ++-- .../option/optgroup/optgroup-base-element.ts | 5 +- .../option/option/option-base-element.ts | 15 +++-- src/elements/overlay/overlay-base-element.ts | 2 +- .../radio-button-group/radio-button-group.ts | 16 ++--- src/elements/stepper/step-label/step-label.ts | 64 +++++++++---------- src/elements/stepper/stepper/stepper.ts | 8 +-- src/elements/tabs/tab-group/tab-group.ts | 50 +++++++-------- src/elements/toggle/toggle/toggle.ts | 58 ++++++++--------- src/visual-regression-app/src/screenshots.ts | 12 ++-- 24 files changed, 254 insertions(+), 217 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 26c1c4ef93..9ed607a528 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -146,6 +146,30 @@ export default [ // TODO Discuss this with the team 'lit/no-invalid-html': 'off', camelcase: 'off', + '@typescript-eslint/member-ordering': [ + 'error', + { + default: { + memberTypes: [ + // Index signature + 'signature', + 'call-signature', + + 'static-field', + + // Static initialization + 'static-initialization', + + ['set', 'get', 'field', 'accessor'], + + 'constructor', + + 'static-method', + ['method', 'decorated-method'], + ], + }, + }, + ], }, }, { diff --git a/src/elements/accordion/accordion.ts b/src/elements/accordion/accordion.ts index 8c8e78fe8a..edda6ee93e 100644 --- a/src/elements/accordion/accordion.ts +++ b/src/elements/accordion/accordion.ts @@ -18,6 +18,9 @@ import style from './accordion.scss?lit&inline'; export class SbbAccordionElement extends SbbHydrationMixin(LitElement) { public static override styles: CSSResultGroup = style; + /** Size variant, either l or s; overrides the size on any projected `sbb-expansion-panel`. */ + @property({ reflect: true }) public size: 's' | 'l' = 'l'; + /** * The heading level for the sbb-expansion-panel-headers within the component. * @controls SbbExpansionPanelElement.titleLevel @@ -44,11 +47,22 @@ export class SbbAccordionElement extends SbbHydrationMixin(LitElement) { } private _multi: boolean = false; - /** Size variant, either l or s; overrides the size on any projected `sbb-expansion-panel`. */ - @property({ reflect: true }) public size: 's' | 'l' = 'l'; + private get _expansionPanels(): SbbExpansionPanelElement[] { + return Array.from(this.querySelectorAll?.('sbb-expansion-panel') ?? []); + } private _abort = new SbbConnectedAbortController(this); + public override connectedCallback(): void { + super.connectedCallback(); + const signal = this._abort.signal; + this.addEventListener( + SbbExpansionPanelElement.events.willOpen, + (e: CustomEvent) => this._closePanels(e), + { signal }, + ); + } + private _closePanels(e: CustomEvent): void { if ((e.target as HTMLElement)?.localName !== 'sbb-expansion-panel' || this.multi) { return; @@ -59,6 +73,14 @@ export class SbbAccordionElement extends SbbHydrationMixin(LitElement) { .forEach((panel) => (panel.expanded = false)); } + protected override willUpdate(changedProperties: PropertyValues): void { + super.willUpdate(changedProperties); + + if (changedProperties.has('size')) { + this._expansionPanels.forEach((panel: SbbExpansionPanelElement) => (panel.size = this.size)); + } + } + private _resetExpansionPanels(newValue: boolean, oldValue: boolean): void { // If it's changing from "multi = true" to "multi = false", open the first panel and close all the others. const expansionPanels = this._expansionPanels; @@ -76,28 +98,6 @@ export class SbbAccordionElement extends SbbHydrationMixin(LitElement) { ); } - private get _expansionPanels(): SbbExpansionPanelElement[] { - return Array.from(this.querySelectorAll?.('sbb-expansion-panel') ?? []); - } - - public override connectedCallback(): void { - super.connectedCallback(); - const signal = this._abort.signal; - this.addEventListener( - SbbExpansionPanelElement.events.willOpen, - (e: CustomEvent) => this._closePanels(e), - { signal }, - ); - } - - protected override willUpdate(changedProperties: PropertyValues): void { - super.willUpdate(changedProperties); - - if (changedProperties.has('size')) { - this._expansionPanels.forEach((panel: SbbExpansionPanelElement) => (panel.size = this.size)); - } - } - private _handleSlotchange(): void { this._expansionPanels.forEach( (panel: SbbExpansionPanelElement, index: number, array: SbbExpansionPanelElement[]) => { diff --git a/src/elements/action-group/action-group.ts b/src/elements/action-group/action-group.ts index 891242d5fb..5f770c85e4 100644 --- a/src/elements/action-group/action-group.ts +++ b/src/elements/action-group/action-group.ts @@ -54,12 +54,6 @@ export class SbbActionGroupElement extends LitElement { @property({ attribute: 'link-size', reflect: true }) public linkSize: SbbLinkSize = 'm'; - private _syncButtons(): void { - this.querySelectorAll?.('[data-sbb-button]').forEach( - (b) => (b.size = this.buttonSize), - ); - } - protected override willUpdate(changedProperties: PropertyValues): void { super.willUpdate(changedProperties); @@ -71,6 +65,12 @@ export class SbbActionGroupElement extends LitElement { } } + private _syncButtons(): void { + this.querySelectorAll?.('[data-sbb-button]').forEach( + (b) => (b.size = this.buttonSize), + ); + } + private _syncLinks(): void { this.querySelectorAll?.< SbbBlockLinkElement | SbbBlockLinkButtonElement | SbbBlockLinkStaticElement diff --git a/src/elements/alert/alert/alert.ts b/src/elements/alert/alert/alert.ts index 5b3782270f..5f5349fa2b 100644 --- a/src/elements/alert/alert/alert.ts +++ b/src/elements/alert/alert/alert.ts @@ -89,12 +89,6 @@ export class SbbAlertElement extends SbbIconNameMixin(SbbOpenCloseBaseElement) { private _language = new SbbLanguageController(this); - protected override async firstUpdated(changedProperties: PropertyValues): Promise { - super.firstUpdated(changedProperties); - - this.open(); - } - /** Requests dismissal of the alert. * @deprecated in favour of 'willClose' and 'didClose' events */ @@ -115,6 +109,12 @@ export class SbbAlertElement extends SbbIconNameMixin(SbbOpenCloseBaseElement) { } } + protected override async firstUpdated(changedProperties: PropertyValues): Promise { + super.firstUpdated(changedProperties); + + this.open(); + } + private _onAnimationEnd(event: AnimationEvent): void { if (this.state === 'opening' && event.animationName === 'open-opacity') { this._handleOpening(); diff --git a/src/elements/autocomplete-grid/autocomplete-grid-button/autocomplete-grid-button.ts b/src/elements/autocomplete-grid/autocomplete-grid-button/autocomplete-grid-button.ts index 128bc0e45e..8438b74f88 100644 --- a/src/elements/autocomplete-grid/autocomplete-grid-button/autocomplete-grid-button.ts +++ b/src/elements/autocomplete-grid/autocomplete-grid-button/autocomplete-grid-button.ts @@ -47,10 +47,6 @@ export class SbbAutocompleteGridButtonElement extends SbbDisabledMixin( /** Whether the component must be set disabled due disabled attribute on sbb-optgroup. */ private _disabledFromGroup = false; - protected override isDisabledExternally(): boolean { - return this._disabledFromGroup ?? false; - } - /** MutationObserver on data attributes. */ private _optionAttributeObserver = new AgnosticMutationObserver((mutationsList) => { for (const mutation of mutationsList) { @@ -69,6 +65,10 @@ export class SbbAutocompleteGridButtonElement extends SbbDisabledMixin( } } + protected override isDisabledExternally(): boolean { + return this._disabledFromGroup ?? false; + } + protected override renderTemplate(): TemplateResult { return super.renderIconSlot(); } diff --git a/src/elements/autocomplete/autocomplete-base-element.ts b/src/elements/autocomplete/autocomplete-base-element.ts index b06aaaca81..4c5df0c2e8 100644 --- a/src/elements/autocomplete/autocomplete-base-element.ts +++ b/src/elements/autocomplete/autocomplete-base-element.ts @@ -87,7 +87,12 @@ export abstract class SbbAutocompleteBaseElement extends SbbNegativeMixin( /** Opens the autocomplete. */ public open(): void { - if (this.state !== 'closed' || !this._overlay || this.options.length === 0 || this._readonly) { + if ( + this.state !== 'closed' || + !this._overlay || + this.options.length === 0 || + this._readonly() + ) { return; } if (!this.willOpen.emit()) { @@ -187,7 +192,7 @@ export abstract class SbbAutocompleteBaseElement extends SbbNegativeMixin( } /** The autocomplete should inherit 'readonly' state from the trigger. */ - private get _readonly(): boolean { + private _readonly(): boolean { return this.triggerElement?.hasAttribute('readonly') ?? false; } diff --git a/src/elements/calendar/calendar.ts b/src/elements/calendar/calendar.ts index 9ed1423f8d..e98d2eab35 100644 --- a/src/elements/calendar/calendar.ts +++ b/src/elements/calendar/calendar.ts @@ -141,7 +141,7 @@ export class SbbCalendarElement extends SbbHydrationMixin(LitElement) if ( !!this._selectedDate && (!this._isDayInRange(this._dateAdapter.toIso8601(this._selectedDate)) || - this._dateFilter(this._selectedDate)) + this._dateFilter()(this._selectedDate)) ) { this._selected = this._dateAdapter.toIso8601(this._selectedDate); } else { @@ -235,7 +235,7 @@ export class SbbCalendarElement extends SbbHydrationMixin(LitElement) this._setWeekdays(); } - private get _dateFilter(): (date: T) => boolean { + private _dateFilter(): (date: T) => boolean { return this.dateFilter ?? (() => true); } @@ -966,7 +966,7 @@ export class SbbCalendarElement extends SbbHydrationMixin(LitElement) private _createDayCells(week: Day[], today: string): TemplateResult[] { return week.map((day: Day) => { const isOutOfRange = !this._isDayInRange(day.value); - const isFilteredOut = !this._dateFilter(this._dateAdapter.deserialize(day.value)!); + const isFilteredOut = !this._dateFilter()(this._dateAdapter.deserialize(day.value)!); const selected: boolean = !!this._selected && day.value === this._selected; const dayValue = `${day.dayValue} ${day.monthValue} ${day.yearValue}`; const isToday = day.value === today; @@ -1252,7 +1252,7 @@ export class SbbCalendarElement extends SbbHydrationMixin(LitElement) this._startTableTransition(); } - private get _getView(): TemplateResult { + private _getView(): TemplateResult { if (isServer || this.hydrationRequired) { // TODO: We disable SSR for calendar for now. Figure our, if there is a way // to enable it, while considering i18n and date information. @@ -1288,7 +1288,7 @@ export class SbbCalendarElement extends SbbHydrationMixin(LitElement) } protected override render(): TemplateResult { - return html`
${this._getView}
`; + return html`
${this._getView()}
`; } } diff --git a/src/elements/clock/clock.ts b/src/elements/clock/clock.ts index 6ff652f1c8..c33e9a3b16 100644 --- a/src/elements/clock/clock.ts +++ b/src/elements/clock/clock.ts @@ -77,15 +77,15 @@ export class SbbClockElement extends LitElement { /** Seconds value for the current date. */ private _seconds!: number; + /** Move the minutes hand every minute. */ + private _handMovement?: ReturnType; + /** Callback function for hours hand. */ private _moveHoursHandFn = (): void => this._moveHoursHand(); /** Callback function for minutes hand. */ private _moveMinutesHandFn = (): void => this._moveMinutesHand(); - /** Move the minutes hand every minute. */ - private _handMovement?: ReturnType; - protected override async willUpdate(changedProperties: PropertyValues): Promise { super.willUpdate(changedProperties); diff --git a/src/elements/core/a11y/focus-visible-within-controller.ts b/src/elements/core/a11y/focus-visible-within-controller.ts index 4561bab7de..3d8016c447 100644 --- a/src/elements/core/a11y/focus-visible-within-controller.ts +++ b/src/elements/core/a11y/focus-visible-within-controller.ts @@ -4,6 +4,10 @@ import { sbbInputModalityDetector } from './input-modality-detector.js'; // Determine whether the element has a visible focus within. export class SbbFocusVisibleWithinController implements ReactiveController { + public constructor(private _host: ReactiveControllerHost & HTMLElement) { + this._host.addController(this); + } + private _focusinHandler = (): void => { this._host.toggleAttribute( 'data-has-visible-focus-within', @@ -15,10 +19,6 @@ export class SbbFocusVisibleWithinController implements ReactiveController { this._host.removeAttribute('data-has-visible-focus-within'); }; - public constructor(private _host: ReactiveControllerHost & HTMLElement) { - this._host.addController(this); - } - public hostConnected(): void { this._host.addEventListener('focusin', this._focusinHandler); this._host.addEventListener('focusout', this._focusoutHandler); diff --git a/src/elements/core/a11y/input-modality-detector.ts b/src/elements/core/a11y/input-modality-detector.ts index 32a5fd53f8..e535c3e0f1 100644 --- a/src/elements/core/a11y/input-modality-detector.ts +++ b/src/elements/core/a11y/input-modality-detector.ts @@ -80,12 +80,6 @@ class SbbInputModalityDetector { return this._mostRecentModality; } - public reset(): void { - this._mostRecentModality = 'mouse'; - this._mostRecentTarget = null; - this._lastTouchMs = 0; - } - /** * The most recent modality must be initialised with the value 'mouse' to cover the case where an action is * performed but no mouse or keyboard event has yet occurred on the page (e.g. `sbb-popover` with hover trigger). @@ -112,6 +106,20 @@ class SbbInputModalityDetector { */ private _lastTouchMs = 0; + public constructor() { + if (!isServer) { + document.addEventListener('keydown', this._onKeydown, modalityEventListenerOptions); + document.addEventListener('mousedown', this._onMousedown, modalityEventListenerOptions); + document.addEventListener('touchstart', this._onTouchstart, modalityEventListenerOptions); + } + } + + public reset(): void { + this._mostRecentModality = 'mouse'; + this._mostRecentTarget = null; + this._lastTouchMs = 0; + } + /** * Handles keydown events. Must be an arrow function in order to preserve the context when it gets * bound. @@ -165,14 +173,6 @@ class SbbInputModalityDetector { this._mostRecentModality = 'touch'; this._mostRecentTarget = getEventTarget(event); }; - - public constructor() { - if (!isServer) { - document.addEventListener('keydown', this._onKeydown, modalityEventListenerOptions); - document.addEventListener('mousedown', this._onMousedown, modalityEventListenerOptions); - document.addEventListener('touchstart', this._onTouchstart, modalityEventListenerOptions); - } - } } export const sbbInputModalityDetector = new SbbInputModalityDetector(); diff --git a/src/elements/core/base-elements/action-base-element.ts b/src/elements/core/base-elements/action-base-element.ts index 99b6a35720..359d8bd37c 100644 --- a/src/elements/core/base-elements/action-base-element.ts +++ b/src/elements/core/base-elements/action-base-element.ts @@ -59,6 +59,7 @@ export abstract class SbbActionBaseElement extends LitElement { } /** Default render method for button-like components. */ + protected override render(): TemplateResult { return html` diff --git a/src/elements/core/base-elements/button-base-element.ts b/src/elements/core/base-elements/button-base-element.ts index f1915a1a05..485bbb0700 100644 --- a/src/elements/core/base-elements/button-base-element.ts +++ b/src/elements/core/base-elements/button-base-element.ts @@ -50,6 +50,28 @@ export abstract class SbbButtonBaseElement extends SbbActionBaseElement { /** The
element to associate the button with. */ @property() public form?: string; + public constructor() { + super(); + if (!isServer) { + this.setupBaseEventHandlers(); + + const passiveOptions = { passive: true }; + this.addEventListener('click', this._handleButtonClick); + this.addEventListener('keydown', this._preventScrollOnSpaceKeydown); + this.addEventListener('keyup', this._dispatchClickEventOnSpaceKeyup, passiveOptions); + this.addEventListener('blur', this._removeActiveMarker, passiveOptions); + this.addEventListener( + 'keypress', + (event: KeyboardEvent): void => { + if (event.key === 'Enter' || event.key === '\n') { + this._dispatchClickEvent(event); + } + }, + passiveOptions, + ); + } + } + private _handleButtonClick = async (event: MouseEvent): Promise => { if (this.type === 'button' || (await isEventPrevented(event))) { return; @@ -117,28 +139,6 @@ export abstract class SbbButtonBaseElement extends SbbActionBaseElement { ); }; - public constructor() { - super(); - if (!isServer) { - this.setupBaseEventHandlers(); - - const passiveOptions = { passive: true }; - this.addEventListener('click', this._handleButtonClick); - this.addEventListener('keydown', this._preventScrollOnSpaceKeydown); - this.addEventListener('keyup', this._dispatchClickEventOnSpaceKeyup, passiveOptions); - this.addEventListener('blur', this._removeActiveMarker, passiveOptions); - this.addEventListener( - 'keypress', - (event: KeyboardEvent): void => { - if (event.key === 'Enter' || event.key === '\n') { - this._dispatchClickEvent(event); - } - }, - passiveOptions, - ); - } - } - public override attributeChangedCallback( name: string, old: string | null, diff --git a/src/elements/core/controllers/slot-state-controller.ts b/src/elements/core/controllers/slot-state-controller.ts index 06d30d4324..8bd0afffae 100644 --- a/src/elements/core/controllers/slot-state-controller.ts +++ b/src/elements/core/controllers/slot-state-controller.ts @@ -22,12 +22,6 @@ import type { ReactiveController, ReactiveControllerHost } from 'lit'; export class SbbSlotStateController implements ReactiveController { public readonly slots = new Set(); - // We avoid using AbortController here, as it would mean creating - // a new instance for every NamedSlotStateController instance. - private _slotchangeHandler = (event: Event): void => { - this._syncSlots(event.target as HTMLSlotElement); - }; - public constructor( private _host: ReactiveControllerHost & HTMLElement, private _onChangeCallback: (() => void) | null = null, @@ -45,6 +39,12 @@ export class SbbSlotStateController implements ReactiveController { this._host.shadowRoot?.removeEventListener('slotchange', this._slotchangeHandler); } + // We avoid using AbortController here, as it would mean creating + // a new instance for every NamedSlotStateController instance. + private _slotchangeHandler = (event: Event): void => { + this._syncSlots(event.target as HTMLSlotElement); + }; + private _syncSlots(...slots: HTMLSlotElement[]): void { for (const slot of slots) { const slotName = slot.name || 'unnamed'; diff --git a/src/elements/datepicker/datepicker/datepicker.ts b/src/elements/datepicker/datepicker/datepicker.ts index 2e2df70a77..46e8d1eb20 100644 --- a/src/elements/datepicker/datepicker/datepicker.ts +++ b/src/elements/datepicker/datepicker/datepicker.ts @@ -174,6 +174,10 @@ export const datepickerControlRegisteredEventFactory = (): CustomEvent => */ @customElement('sbb-datepicker') export class SbbDatepickerElement extends LitElement { + /* eslint-disable @typescript-eslint/member-ordering -- + * We deactivate member-ordering because of the @property decorated methods dateFilter, dateParser and format. + */ + public static override styles: CSSResultGroup = style; public static readonly events = { didChange: 'didChange', @@ -476,6 +480,7 @@ export class SbbDatepickerElement extends LitElement { protected override render(): TemplateResult { return html`

`; } + /* eslint-enable @typescript-eslint/member-ordering */ } declare global { diff --git a/src/elements/file-selector/file-selector.ts b/src/elements/file-selector/file-selector.ts index ca17b5055a..64b3c47a03 100644 --- a/src/elements/file-selector/file-selector.ts +++ b/src/elements/file-selector/file-selector.ts @@ -77,13 +77,6 @@ export class SbbFileSelectorElement extends SbbDisabledMixin(LitElement) { return this._files || []; } - /** - * @deprecated use 'files' property instead - */ - public getFiles(): File[] { - return this._files || []; - } - // Safari has a peculiar behavior when dragging files on the inner button in 'dropzone' variant; // this will require a counter to correctly handle the dragEnter/dragLeave. private _counter: number = 0; @@ -96,6 +89,13 @@ export class SbbFileSelectorElement extends SbbDisabledMixin(LitElement) { private _language = new SbbLanguageController(this); + /** + * @deprecated use 'files' property instead + */ + public getFiles(): File[] { + return this._files || []; + } + private _blockEvent(event: DragEvent): void { event.stopPropagation(); event.preventDefault(); diff --git a/src/elements/option/optgroup/optgroup-base-element.ts b/src/elements/option/optgroup/optgroup-base-element.ts index a1c5ba8f21..c5d0ac000c 100644 --- a/src/elements/option/optgroup/optgroup-base-element.ts +++ b/src/elements/option/optgroup/optgroup-base-element.ts @@ -41,8 +41,6 @@ export abstract class SbbOptgroupBaseElement extends SbbDisabledMixin( private _negativeObserver = new AgnosticMutationObserver(() => this._onNegativeChange()); protected abstract get options(): SbbOptionBaseElement[]; - protected abstract setAttributeFromParent(): void; - protected abstract getAutocompleteParent(): SbbAutocompleteBaseElement | null; public constructor() { super(); @@ -88,6 +86,9 @@ export abstract class SbbOptgroupBaseElement extends SbbDisabledMixin( this._negativeObserver?.disconnect(); } + protected abstract setAttributeFromParent(): void; + protected abstract getAutocompleteParent(): SbbAutocompleteBaseElement | null; + private _handleSlotchange(): void { this.proxyDisabledToOptions(); this._proxyGroupLabelToOptions(); diff --git a/src/elements/option/option/option-base-element.ts b/src/elements/option/option/option-base-element.ts index e63638e429..b215b620e4 100644 --- a/src/elements/option/option/option-base-element.ts +++ b/src/elements/option/option/option-base-element.ts @@ -85,13 +85,6 @@ export abstract class SbbOptionBaseElement extends SbbDisabledMixin( @state() private _inertAriaGroups = false; private _abort = new SbbConnectedAbortController(this); - protected abstract selectByClick(event: MouseEvent): void; - protected abstract setAttributeFromParent(): void; - - protected updateDisableHighlight(disabled: boolean): void { - this.disableLabelHighlight = disabled; - this.toggleAttribute('data-disable-highlight', disabled); - } /** MutationObserver on data attributes. */ private _optionAttributeObserver = new AgnosticMutationObserver((mutationsList) => @@ -171,6 +164,14 @@ export abstract class SbbOptionBaseElement extends SbbDisabledMixin( this._optionAttributeObserver.disconnect(); } + protected abstract selectByClick(event: MouseEvent): void; + protected abstract setAttributeFromParent(): void; + + protected updateDisableHighlight(disabled: boolean): void { + this.disableLabelHighlight = disabled; + this.toggleAttribute('data-disable-highlight', disabled); + } + /** * Whether the option is currently active. * @internal diff --git a/src/elements/overlay/overlay-base-element.ts b/src/elements/overlay/overlay-base-element.ts index d49043c3b5..2e66a04102 100644 --- a/src/elements/overlay/overlay-base-element.ts +++ b/src/elements/overlay/overlay-base-element.ts @@ -37,9 +37,9 @@ export abstract class SbbOverlayBaseElement extends SbbNegativeMixin(SbbOpenClos protected language = new SbbLanguageController(this); protected inertController = new SbbInertController(this); + protected abstract closeAttribute: string; protected abstract onOverlayAnimationEnd(event: AnimationEvent): void; protected abstract setOverlayFocus(): void; - protected abstract closeAttribute: string; /** Closes the component. */ public close(result?: any, target?: HTMLElement): any { diff --git a/src/elements/radio-button/radio-button-group/radio-button-group.ts b/src/elements/radio-button/radio-button-group/radio-button-group.ts index ed100e5c05..f72b5870c0 100644 --- a/src/elements/radio-button/radio-button-group/radio-button-group.ts +++ b/src/elements/radio-button/radio-button-group/radio-button-group.ts @@ -96,14 +96,6 @@ export class SbbRadioButtonGroupElement extends SbbDisabledMixin(LitElement) { private _didLoad = false; private _abort = new SbbConnectedAbortController(this); - private _valueChanged(value: any | undefined): void { - for (const radio of this.radioButtons) { - radio.checked = radio.value === value; - radio.tabIndex = this._getRadioTabIndex(radio); - } - this._setFocusableRadio(); - } - /** * Emits whenever the `sbb-radio-group` value changes. * @deprecated only used for React. Will probably be removed once React 19 is available. @@ -173,6 +165,14 @@ export class SbbRadioButtonGroupElement extends SbbDisabledMixin(LitElement) { } } + private _valueChanged(value: any | undefined): void { + for (const radio of this.radioButtons) { + radio.checked = radio.value === value; + radio.tabIndex = this._getRadioTabIndex(radio); + } + this._setFocusableRadio(); + } + protected override firstUpdated(changedProperties: PropertyValues): void { super.firstUpdated(changedProperties); diff --git a/src/elements/stepper/step-label/step-label.ts b/src/elements/stepper/step-label/step-label.ts index 668dfb8c33..94a6009e8e 100644 --- a/src/elements/stepper/step-label/step-label.ts +++ b/src/elements/stepper/step-label/step-label.ts @@ -36,38 +36,6 @@ export class SbbStepLabelElement extends SbbIconNameMixin(SbbDisabledMixin(SbbBu return this._step; } - /** - * Selects and configures the step label. - * @internal - */ - public select(): void { - this.tabIndex = 0; - this._internals.ariaSelected = 'true'; - this.toggleAttribute('data-selected', true); - } - - /** - * Deselects and configures the step label. - * @internal - */ - public deselect(): void { - this.tabIndex = -1; - this._internals.ariaSelected = 'false'; - this.toggleAttribute('data-selected', false); - } - - /** - * Configures the step label. - * @internal - */ - public configure(posInSet: number, setSize: number, stepperLoaded: boolean): void { - if (stepperLoaded) { - this._step = this._getStep(); - } - this._internals.ariaPosInSet = `${posInSet}`; - this._internals.ariaSetSize = `${setSize}`; - } - private _abort = new SbbConnectedAbortController(this); private _stepper: SbbStepperElement | null = null; private _step: SbbStepElement | null = null; @@ -108,6 +76,38 @@ export class SbbStepLabelElement extends SbbIconNameMixin(SbbDisabledMixin(SbbBu } } + /** + * Selects and configures the step label. + * @internal + */ + public select(): void { + this.tabIndex = 0; + this._internals.ariaSelected = 'true'; + this.toggleAttribute('data-selected', true); + } + + /** + * Deselects and configures the step label. + * @internal + */ + public deselect(): void { + this.tabIndex = -1; + this._internals.ariaSelected = 'false'; + this.toggleAttribute('data-selected', false); + } + + /** + * Configures the step label. + * @internal + */ + public configure(posInSet: number, setSize: number, stepperLoaded: boolean): void { + if (stepperLoaded) { + this._step = this._getStep(); + } + this._internals.ariaPosInSet = `${posInSet}`; + this._internals.ariaSetSize = `${setSize}`; + } + protected override render(): TemplateResult { return html`
diff --git a/src/elements/stepper/stepper/stepper.ts b/src/elements/stepper/stepper/stepper.ts index a6ff30d23a..69f64b6656 100644 --- a/src/elements/stepper/stepper/stepper.ts +++ b/src/elements/stepper/stepper/stepper.ts @@ -82,6 +82,10 @@ export class SbbStepperElement extends SbbHydrationMixin(LitElement) { return this.steps.filter((s) => !s.label?.hasAttribute('disabled')); } + private _loaded: boolean = false; + private _abort = new SbbConnectedAbortController(this); + private _resizeObserverTimeout: ReturnType | null = null; + /** Selects the next step. */ public next(): void { if (this.selectedIndex !== undefined) { @@ -111,10 +115,6 @@ export class SbbStepperElement extends SbbHydrationMixin(LitElement) { } } - private _loaded: boolean = false; - private _abort = new SbbConnectedAbortController(this); - private _resizeObserverTimeout: ReturnType | null = null; - private _isValidStep(step: SbbStepElement): boolean { if (!step || (!this.linear && step.label?.hasAttribute('disabled'))) { return false; diff --git a/src/elements/tabs/tab-group/tab-group.ts b/src/elements/tabs/tab-group/tab-group.ts index 156e00f755..2cd7585efb 100644 --- a/src/elements/tabs/tab-group/tab-group.ts +++ b/src/elements/tabs/tab-group/tab-group.ts @@ -91,18 +91,27 @@ export class SbbTabGroupElement extends SbbHydrationMixin(LitElement) { */ @property({ attribute: 'initial-selected-index', type: Number }) public initialSelectedIndex = 0; - private _updateSize(): void { - for (const tab of this._tabs) { - tab.setAttribute('data-size', this.size); - } - } - /** Emits an event on selected tab change. */ private _selectedTabChanged: EventEmitter = new EventEmitter( this, SbbTabGroupElement.events.didChange, ); + public override connectedCallback(): void { + super.connectedCallback(); + const signal = this._abort.signal; + this.addEventListener('keydown', (e) => this._handleKeyDown(e), { signal }); + } + + protected override firstUpdated(changedProperties: PropertyValues): void { + super.firstUpdated(changedProperties); + + this._tabs = this._getTabs(); + this._tabs.forEach((tab) => this._configure(tab)); + this._initSelection(); + this._tabGroupResizeObserver.observe(this._tabGroupElement); + } + /** * Disables a tab by index. * @param tabIndex The index of the tab you want to disable. @@ -133,25 +142,10 @@ export class SbbTabGroupElement extends SbbHydrationMixin(LitElement) { ) as InterfaceSbbTabGroupTab[]; } - private get _enabledTabs(): InterfaceSbbTabGroupTab[] { + private _enabledTabs(): InterfaceSbbTabGroupTab[] { return this._tabs.filter((t) => !t.hasAttribute('disabled')); } - public override connectedCallback(): void { - super.connectedCallback(); - const signal = this._abort.signal; - this.addEventListener('keydown', (e) => this._handleKeyDown(e), { signal }); - } - - protected override firstUpdated(changedProperties: PropertyValues): void { - super.firstUpdated(changedProperties); - - this._tabs = this._getTabs(); - this._tabs.forEach((tab) => this._configure(tab)); - this._initSelection(); - this._tabGroupResizeObserver.observe(this._tabGroupElement); - } - public override disconnectedCallback(): void { super.disconnectedCallback(); this._tabAttributeObserver.disconnect(); @@ -159,6 +153,12 @@ export class SbbTabGroupElement extends SbbHydrationMixin(LitElement) { this._tabGroupResizeObserver.disconnect(); } + private _updateSize(): void { + for (const tab of this._tabs) { + tab.setAttribute('data-size', this.size); + } + } + private _onContentSlotChange = (): void => { this._tabContentElement = this.shadowRoot!.querySelector('div.tab-content')!; const loadedTabs = this._getTabs().filter((tab) => !this._tabs.includes(tab)); @@ -197,7 +197,7 @@ export class SbbTabGroupElement extends SbbHydrationMixin(LitElement) { ) { this._tabs[this.initialSelectedIndex].tabGroupActions?.select(); } else { - this._enabledTabs[0]?.tabGroupActions?.select(); + this._enabledTabs()[0]?.tabGroupActions?.select(); } } @@ -279,7 +279,7 @@ export class SbbTabGroupElement extends SbbHydrationMixin(LitElement) { if (tabLabel.active) { tabLabel.removeAttribute('active'); tabLabel.active = false; - this._enabledTabs[0]?.tabGroupActions?.select(); + this._enabledTabs()[0]?.tabGroupActions?.select(); } }, enable: (): void => { @@ -348,7 +348,7 @@ export class SbbTabGroupElement extends SbbHydrationMixin(LitElement) { } private _handleKeyDown(evt: KeyboardEvent): void { - const enabledTabs: InterfaceSbbTabGroupTab[] = this._enabledTabs; + const enabledTabs: InterfaceSbbTabGroupTab[] = this._enabledTabs(); if ( !enabledTabs || diff --git a/src/elements/toggle/toggle/toggle.ts b/src/elements/toggle/toggle/toggle.ts index 6c5dc99378..31b93794af 100644 --- a/src/elements/toggle/toggle/toggle.ts +++ b/src/elements/toggle/toggle/toggle.ts @@ -82,35 +82,6 @@ export class SbbToggleElement extends LitElement { private _loaded: boolean = false; private _toggleResizeObserver = new AgnosticResizeObserver(() => this.updatePillPosition(true)); - private _valueChanged(value: any | undefined): void { - const options = this.options; - // If options are not yet defined web components, we can check if attribute is already set as a fallback. - // We do this by checking whether value property is available (defined component). - const selectedOption = - options.find( - (o) => value === ('value' in o ? o.value : (o as HTMLElement).getAttribute('value')), - ) ?? - options.find((o) => o.checked) ?? - options[0]; - - if (!selectedOption) { - if (import.meta.env.DEV && !isServer) { - console.warn(`sbb-toggle: No available options! (${this.id || 'No id'})`); - } - return; - } - if (!selectedOption.checked) { - selectedOption.checked = true; - } - this.updatePillPosition(false); - } - - private _updateDisabled(): void { - for (const toggleOption of this.options) { - toggleOption.disabled = this.disabled; - } - } - /** * @deprecated only used for React. Will probably be removed once React 19 is available. * Emits whenever the toggle value changes. @@ -185,6 +156,35 @@ export class SbbToggleElement extends LitElement { this._updateDisabled(); } + private _valueChanged(value: any | undefined): void { + const options = this.options; + // If options are not yet defined web components, we can check if attribute is already set as a fallback. + // We do this by checking whether value property is available (defined component). + const selectedOption = + options.find( + (o) => value === ('value' in o ? o.value : (o as HTMLElement).getAttribute('value')), + ) ?? + options.find((o) => o.checked) ?? + options[0]; + + if (!selectedOption) { + if (import.meta.env.DEV && !isServer) { + console.warn(`sbb-toggle: No available options! (${this.id || 'No id'})`); + } + return; + } + if (!selectedOption.checked) { + selectedOption.checked = true; + } + this.updatePillPosition(false); + } + + private _updateDisabled(): void { + for (const toggleOption of this.options) { + toggleOption.disabled = this.disabled; + } + } + private _handleInput(): void { this.updatePillPosition(false); this._change.emit(); diff --git a/src/visual-regression-app/src/screenshots.ts b/src/visual-regression-app/src/screenshots.ts index 1fdb18408f..eadbd9a032 100644 --- a/src/visual-regression-app/src/screenshots.ts +++ b/src/visual-regression-app/src/screenshots.ts @@ -8,6 +8,12 @@ const viewportOrder = ['zero', 'micro', 'small', 'medium', 'large', 'wide', 'ult export class ScreenshotStatistics { public static readonly empty = new ScreenshotStatistics(0, 0, 0); + public constructor( + public readonly failedTests: number, + public readonly newTests: number, + public readonly baselines: number, + ) {} + public static fromScreenshotFiles(screenshotsFiles: ScreenshotFiles[]): ScreenshotStatistics { return screenshotsFiles.reduce( (current, next) => @@ -26,12 +32,6 @@ export class ScreenshotStatistics { return list.reduce((current, next) => current.sum(next.stats), ScreenshotStatistics.empty); } - public constructor( - public readonly failedTests: number, - public readonly newTests: number, - public readonly baselines: number, - ) {} - public sum(other: ScreenshotStatistics): ScreenshotStatistics { return new ScreenshotStatistics( this.failedTests + other.failedTests, From 1e6abaa10bc18e923052f32ffbdf8d57a47f84f6 Mon Sep 17 00:00:00 2001 From: Jeremias Peier Date: Mon, 9 Sep 2024 12:06:56 +0200 Subject: [PATCH 2/3] fix: review --- src/elements/core/base-elements/action-base-element.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/elements/core/base-elements/action-base-element.ts b/src/elements/core/base-elements/action-base-element.ts index 359d8bd37c..99b6a35720 100644 --- a/src/elements/core/base-elements/action-base-element.ts +++ b/src/elements/core/base-elements/action-base-element.ts @@ -59,7 +59,6 @@ export abstract class SbbActionBaseElement extends LitElement { } /** Default render method for button-like components. */ - protected override render(): TemplateResult { return html` From 737e08e142205f6b6e333bac95a7cf5bb21625d6 Mon Sep 17 00:00:00 2001 From: Jeremias Peier Date: Mon, 9 Sep 2024 12:08:59 +0200 Subject: [PATCH 3/3] fix: review 2 --- src/elements/calendar/calendar.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/elements/calendar/calendar.ts b/src/elements/calendar/calendar.ts index e98d2eab35..37884e2460 100644 --- a/src/elements/calendar/calendar.ts +++ b/src/elements/calendar/calendar.ts @@ -141,7 +141,7 @@ export class SbbCalendarElement extends SbbHydrationMixin(LitElement) if ( !!this._selectedDate && (!this._isDayInRange(this._dateAdapter.toIso8601(this._selectedDate)) || - this._dateFilter()(this._selectedDate)) + this._dateFilter(this._selectedDate)) ) { this._selected = this._dateAdapter.toIso8601(this._selectedDate); } else { @@ -235,8 +235,8 @@ export class SbbCalendarElement extends SbbHydrationMixin(LitElement) this._setWeekdays(); } - private _dateFilter(): (date: T) => boolean { - return this.dateFilter ?? (() => true); + private _dateFilter(date: T): boolean { + return this.dateFilter?.(date) ?? true; } /** Resets the active month according to the new state of the calendar. */ @@ -966,7 +966,7 @@ export class SbbCalendarElement extends SbbHydrationMixin(LitElement) private _createDayCells(week: Day[], today: string): TemplateResult[] { return week.map((day: Day) => { const isOutOfRange = !this._isDayInRange(day.value); - const isFilteredOut = !this._dateFilter()(this._dateAdapter.deserialize(day.value)!); + const isFilteredOut = !this._dateFilter(this._dateAdapter.deserialize(day.value)!); const selected: boolean = !!this._selected && day.value === this._selected; const dayValue = `${day.dayValue} ${day.monthValue} ${day.yearValue}`; const isToday = day.value === today;