From 8889dd17668c538457fe053bd521310cbee5eaac Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 26 Nov 2024 13:34:53 +0000 Subject: [PATCH 1/7] chore(deps): update dependency prettier to v3.4.1 --- package.json | 4 ++-- yarn.lock | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 37ea83aa47..87a2c29b0d 100644 --- a/package.json +++ b/package.json @@ -129,7 +129,7 @@ "octokit": "4.0.2", "playwright": "1.47.2", "postcss": "8.4.49", - "prettier": "3.4.0", + "prettier": "3.4.1", "react": "18.3.1", "rollup-plugin-postcss-lit": "2.1.0", "sass": "1.81.0", @@ -154,7 +154,7 @@ "jackspeak": "2.1.1", "lit": "3.2.1", "playwright": "1.47.2", - "prettier": "3.4.0" + "prettier": "3.4.1" }, "prettier": { "singleQuote": true, diff --git a/yarn.lock b/yarn.lock index c24b0ad450..50ab275158 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7361,10 +7361,10 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier@3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.4.0.tgz#9dcd5e617cf103db8e4bd00924baecfd32bf9971" - integrity sha512-/OXNZcLyWkfo13ofOW5M7SLh+k5pnIs07owXK2teFpnfaOEcycnSy7HQxldaVX1ZP/7Q8oO1eDuQJNwbomQq5Q== +prettier@3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.4.1.tgz#e211d451d6452db0a291672ca9154bc8c2579f7b" + integrity sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg== pretty-format@^27.0.2: version "27.5.1" From ad6b19ff5dc4e824c5f99f83eedf3f208b4492c4 Mon Sep 17 00:00:00 2001 From: Jeri Peier Date: Tue, 26 Nov 2024 16:49:18 +0100 Subject: [PATCH 2/7] fix(sbb-datepicker): remove deprecated methods and properties (#3247) BREAKING CHANGE: This refactoring introduces multiple breaking changes to the datepicker: - DateAdapter: return value for invalid dates changed from undefined to null - Datepicker: removed functions `getAvailableDate()` and `isDateAvailable()` - Datepicker: moved functions `findPreviousAvailableDate()` and `findNextAvailableDate()` into `SbbDatepickerElement` and removed all params but `date` - Datepicker: removed properties `dateParser` and `format`, as alternative use custom DateAdapter - Datepicker: `now` property newly accepts `null` instead of `undefined` - Datepicker: removed methods `getValueAsDate()` and `setValueAsDate()`. Use getter/setter `valueAsDate` instead. --- src/elements/core/datetime/date-adapter.ts | 5 +- .../core/datetime/native-date-adapter.spec.ts | 6 +- .../core/datetime/native-date-adapter.ts | 6 +- .../datepicker/common/datepicker-button.ts | 39 +- .../datepicker-next-day.ts | 11 +- .../datepicker-previous-day.ts | 11 +- .../datepicker/datepicker/datepicker.spec.ts | 378 ++++++++---------- .../datepicker/datepicker.stories.ts | 49 --- .../datepicker/datepicker/datepicker.ts | 247 ++++-------- src/elements/datepicker/datepicker/readme.md | 58 +-- 10 files changed, 292 insertions(+), 518 deletions(-) diff --git a/src/elements/core/datetime/date-adapter.ts b/src/elements/core/datetime/date-adapter.ts index f37df0adb8..8be5ef8bed 100644 --- a/src/elements/core/datetime/date-adapter.ts +++ b/src/elements/core/datetime/date-adapter.ts @@ -5,8 +5,6 @@ export const YEARS_PER_PAGE: number = 24; export const FORMAT_DATE = /(^0?[1-9]?|[12]?[0-9]?|3?[01]?)[.,\\/\-\s](0?[1-9]?|1?[0-2]?)?[.,\\/\-\s](\d{1,4}$)?/; -// TODO(breaking-change): Change undefined return types to null. - /** * Abstract date functionality. * @@ -139,11 +137,12 @@ export abstract class DateAdapter { * @param value The date in the format DD.MM.YYYY. * @param now The current date as Date. */ - public abstract parse(value: string | null | undefined, now: T): T | undefined; + public abstract parse(value: string | null | undefined, now: T): T | null; /** * Format the given date as string. * @param date The date to format. + * @param options options object with weekdayStyle as property */ public format( date: T | null | undefined, diff --git a/src/elements/core/datetime/native-date-adapter.spec.ts b/src/elements/core/datetime/native-date-adapter.spec.ts index 54a846060d..f9c9258638 100644 --- a/src/elements/core/datetime/native-date-adapter.spec.ts +++ b/src/elements/core/datetime/native-date-adapter.spec.ts @@ -194,9 +194,9 @@ describe('NativeDateAdapter', () => { it('parseDate should return the correct value', function () { const now = new Date(2023, 8, 15, 0, 0, 0, 0); - expect(nativeDateAdapter.parse(null, now)).to.be.undefined; - expect(nativeDateAdapter.parse('Test', now)).to.be.undefined; - expect(nativeDateAdapter.parse('1.1', now)).to.be.undefined; + expect(nativeDateAdapter.parse(null, now)).to.be.null; + expect(nativeDateAdapter.parse('Test', now)).to.be.null; + expect(nativeDateAdapter.parse('1.1', now)).to.be.null; let formattedDate = nativeDateAdapter.parse('1/1/2000', now)!; expect(formattedDate.getFullYear()).to.be.equal(2000); expect(formattedDate.getMonth()).to.be.equal(0); diff --git a/src/elements/core/datetime/native-date-adapter.ts b/src/elements/core/datetime/native-date-adapter.ts index 1b07e2ff7e..401a33d4a4 100644 --- a/src/elements/core/datetime/native-date-adapter.ts +++ b/src/elements/core/datetime/native-date-adapter.ts @@ -173,9 +173,9 @@ export class NativeDateAdapter extends DateAdapter { } /** Returns the right format for the `valueAsDate` property. */ - public parse(value: string | null | undefined, now: Date): Date | undefined { + public parse(value: string | null | undefined, now: Date): Date | null { if (!value) { - return undefined; + return null; } const strippedValue = value.replace(/\D/g, ' ').trim(); @@ -188,7 +188,7 @@ export class NativeDateAdapter extends DateAdapter { match.some((e) => e === undefined) || !this.isValid(this.createDate(+match[3], +match[2], +match[1])) ) { - return undefined; + return null; } let year = +match[3]; diff --git a/src/elements/datepicker/common/datepicker-button.ts b/src/elements/datepicker/common/datepicker-button.ts index 5b42fe392d..d691ef95c7 100644 --- a/src/elements/datepicker/common/datepicker-button.ts +++ b/src/elements/datepicker/common/datepicker-button.ts @@ -41,13 +41,7 @@ export abstract class SbbDatepickerButton extends SbbNegativeMixin(Sbb protected abstract iconName: string; protected abstract i18nOffBoundaryDay: Record; protected abstract i18nSelectOffBoundaryDay: (_currentDate: string) => Record; - protected abstract findAvailableDate: ( - _date: T, - _dateFilter: ((date: T) => boolean) | null, - _dateAdapter: DateAdapter, - _boundary: string | number | null, - ) => T; - protected abstract onInputUpdated(event: CustomEvent): void; + protected abstract findAvailableDate(_date: T): T; public override connectedCallback(): void { super.connectedCallback(); @@ -71,8 +65,8 @@ export abstract class SbbDatepickerButton extends SbbNegativeMixin(Sbb this._datePickerController?.abort(); } - protected setDisabledState(datepicker: SbbDatepickerElement | null | undefined): void { - const pickerValueAsDate = datepicker?.valueAsDate; + private _setDisabledState(): void { + const pickerValueAsDate = this.datePickerElement?.valueAsDate; if (!pickerValueAsDate) { this._disabled = true; @@ -80,12 +74,7 @@ export abstract class SbbDatepickerButton extends SbbNegativeMixin(Sbb return; } - const availableDate: T = this.findAvailableDate( - pickerValueAsDate, - datepicker?.dateFilter || null, - this._dateAdapter, - this.boundary, - ); + const availableDate: T = this.findAvailableDate(pickerValueAsDate); this._disabled = this._dateAdapter.compareDate(availableDate, pickerValueAsDate) === 0; this._setDisabledRenderAttributes(); } @@ -95,12 +84,7 @@ export abstract class SbbDatepickerButton extends SbbNegativeMixin(Sbb return; } const startingDate: T = this.datePickerElement.valueAsDate ?? this.datePickerElement.now; - const date: T = this.findAvailableDate( - startingDate, - this.datePickerElement.dateFilter, - this._dateAdapter, - this.boundary, - ); + const date: T = this.findAvailableDate(startingDate); if (this._dateAdapter.compareDate(date, startingDate) !== 0) { this.datePickerElement.valueAsDate = date; } @@ -125,7 +109,7 @@ export abstract class SbbDatepickerButton extends SbbNegativeMixin(Sbb this._datePickerController?.abort(); this._datePickerController = new AbortController(); this.datePickerElement = getDatePicker(this, picker); - this.setDisabledState(this.datePickerElement); + this._setDisabledState(); if (!this.datePickerElement) { // If the component is attached to the DOM before the datepicker, it has to listen for the datepicker init, // assuming that the two components share the same parent element. @@ -140,16 +124,16 @@ export abstract class SbbDatepickerButton extends SbbNegativeMixin(Sbb this.datePickerElement.addEventListener( 'change', - (event: Event) => { - this.setDisabledState(event.target as SbbDatepickerElement); + () => { + this._setDisabledState(); this._setAriaLabel(); }, { signal: this._datePickerController.signal }, ); this.datePickerElement.addEventListener( 'datePickerUpdated', - (event: Event) => { - this.setDisabledState(event.target as SbbDatepickerElement); + () => { + this._setDisabledState(); this._setAriaLabel(); }, { signal: this._datePickerController.signal }, @@ -160,7 +144,7 @@ export abstract class SbbDatepickerButton extends SbbNegativeMixin(Sbb this._inputDisabled = !!(event.detail.disabled || event.detail.readonly); this._setDisabledRenderAttributes(); this._setAriaLabel(); - this.onInputUpdated(event); + this._setDisabledState(); }, { signal: this._datePickerController.signal }, ); @@ -176,7 +160,6 @@ export abstract class SbbDatepickerButton extends SbbNegativeMixin(Sbb return; } - // TODO: use toIsoString() instead of toDateString() const currentDateString = this.datePickerElement && this._dateAdapter.compareDate(this.datePickerElement.now, currentDate) === 0 diff --git a/src/elements/datepicker/datepicker-next-day/datepicker-next-day.ts b/src/elements/datepicker/datepicker-next-day/datepicker-next-day.ts index 54f6643045..7a56d25c72 100644 --- a/src/elements/datepicker/datepicker-next-day/datepicker-next-day.ts +++ b/src/elements/datepicker/datepicker-next-day/datepicker-next-day.ts @@ -4,9 +4,7 @@ import { customElement } from 'lit/decorators.js'; import { hostAttributes } from '../../core/decorators.js'; import { i18nNextDay, i18nSelectNextDay } from '../../core/i18n.js'; import { SbbDatepickerButton } from '../common.js'; -import { findNextAvailableDate, type SbbInputUpdateEvent } from '../datepicker.js'; -import '../../icon.js'; import style from './datepicker-next-day.scss?lit&inline'; /** @@ -23,13 +21,10 @@ class SbbDatepickerNextDayElement extends SbbDatepickerButton { protected iconName: string = 'chevron-small-right-small'; protected i18nOffBoundaryDay: Record = i18nNextDay; protected i18nSelectOffBoundaryDay = i18nSelectNextDay; - protected findAvailableDate = findNextAvailableDate; - protected onInputUpdated(event: CustomEvent): void { - if (this.boundary !== event.detail.max) { - this.boundary = event.detail.max!; - this.setDisabledState(this.datePickerElement!); - } + protected findAvailableDate(date: T): T { + // When calling findAvailableDate, datepickerElement is always defined. + return this.datePickerElement!.findNextAvailableDate(date); } } diff --git a/src/elements/datepicker/datepicker-previous-day/datepicker-previous-day.ts b/src/elements/datepicker/datepicker-previous-day/datepicker-previous-day.ts index 65c7e04fc1..f434de68eb 100644 --- a/src/elements/datepicker/datepicker-previous-day/datepicker-previous-day.ts +++ b/src/elements/datepicker/datepicker-previous-day/datepicker-previous-day.ts @@ -4,8 +4,6 @@ import { customElement } from 'lit/decorators.js'; import { hostAttributes } from '../../core/decorators.js'; import { i18nPreviousDay, i18nSelectPreviousDay } from '../../core/i18n.js'; import { SbbDatepickerButton } from '../common.js'; -import { findPreviousAvailableDate, type SbbInputUpdateEvent } from '../datepicker.js'; -import '../../icon.js'; import style from './datepicker-previous-day.scss?lit&inline'; @@ -23,13 +21,10 @@ class SbbDatepickerPreviousDayElement extends SbbDatepickerButton { protected iconName: string = 'chevron-small-left-small'; protected i18nOffBoundaryDay: Record = i18nPreviousDay; protected i18nSelectOffBoundaryDay = i18nSelectPreviousDay; - protected findAvailableDate = findPreviousAvailableDate; - protected onInputUpdated(event: CustomEvent): void { - if (this.boundary !== event.detail.min) { - this.boundary = event.detail.min!; - this.setDisabledState(this.datePickerElement!); - } + protected findAvailableDate(date: T): T { + // When calling findAvailableDate, datepickerElement is always defined. + return this.datePickerElement!.findPreviousAvailableDate(date); } } diff --git a/src/elements/datepicker/datepicker/datepicker.spec.ts b/src/elements/datepicker/datepicker/datepicker.spec.ts index fb79348164..3e4b6047f2 100644 --- a/src/elements/datepicker/datepicker/datepicker.spec.ts +++ b/src/elements/datepicker/datepicker/datepicker.spec.ts @@ -2,9 +2,7 @@ import { assert, expect } from '@open-wc/testing'; import { sendKeys } from '@web/test-runner-commands'; import type { TemplateResult } from 'lit'; import { html } from 'lit/static-html.js'; -import type { Context } from 'mocha'; -import { NativeDateAdapter } from '../../core/datetime.js'; import { findInput } from '../../core/dom.js'; import { i18nDateChangedTo } from '../../core/i18n.js'; import { fixture, tabKey, typeInElement } from '../../core/testing/private.js'; @@ -13,14 +11,7 @@ import type { SbbFormFieldElement } from '../../form-field.js'; import type { SbbDatepickerNextDayElement } from '../datepicker-next-day.js'; import type { SbbDatepickerPreviousDayElement } from '../datepicker-previous-day.js'; -import { - findNextAvailableDate, - findPreviousAvailableDate, - getAvailableDate, - getDatePicker, - isDateAvailable, - SbbDatepickerElement, -} from './datepicker.js'; +import { getDatePicker, SbbDatepickerElement } from './datepicker.js'; import '../../form-field.js'; import '../datepicker-previous-day.js'; @@ -89,7 +80,7 @@ describe(`sbb-datepicker`, () => { ); }); - it('renders and emit event on value change', async function (this: Context) { + it('renders and emit event on value change', async () => { const changeSpy = new EventSpy('change', element); typeInElement(input, '20/01/2023'); button.focus(); @@ -98,7 +89,7 @@ describe(`sbb-datepicker`, () => { expect(changeSpy.count).to.be.equal(1); }); - it('renders and interpret two digit year correctly in 2000s', async function (this: Context) { + it('renders and interpret two digit year correctly in 2000s', async () => { const changeSpy = new EventSpy('change', element); typeInElement(input, '20/01/12'); button.focus(); @@ -107,7 +98,7 @@ describe(`sbb-datepicker`, () => { expect(changeSpy.count).to.be.equal(1); }); - it('renders and interpret two digit year correctly in 1900s', async function (this: Context) { + it('renders and interpret two digit year correctly in 1900s', async () => { const changeSpy = new EventSpy('change', element); typeInElement(input, '20/01/99'); button.focus(); @@ -171,33 +162,6 @@ describe(`sbb-datepicker`, () => { expect(datePickerUpdatedSpy.count).to.be.equal(2); }); - it('renders and interprets date with custom parse and format functions', async () => { - const changeSpy = new EventSpy('change', element); - - element.dateParser = (s) => { - s = s.replace(/\D/g, ' ').trim(); - const date = s.split(' '); - const now = new Date(2023, 8, 15, 0, 0, 0, 0); - return new Date(now.getFullYear(), +date[1] - 1, +date[0]); - }; - element.format = (d) => { - //Intl.DateTimeFormat API is not available in test environment. - const weekdays = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']; - const weekday = weekdays[d.getDay()]; - const date = `${String(d.getDate()).padStart(2, '0')}.${String(d.getMonth() + 1).padStart( - 2, - '0', - )}`; - return `${weekday}, ${date}`; - }; - await waitForLitRender(element); - typeInElement(input, '7.8', { key: 'Enter', keyCode: 13 }); - await changeSpy.calledOnce(); - await waitForLitRender(element); - expect(input.value).to.be.equal('Mo, 07.08'); - expect(changeSpy.count).to.be.equal(1); - }); - it('should emit validation change event', async () => { let validationChangeSpy = new EventSpy('validationChange', element); @@ -231,7 +195,7 @@ describe(`sbb-datepicker`, () => { expect(input).not.to.have.attribute('data-sbb-invalid'); }); - it('should interpret valid values and set accessibility labels', async function (this: Context) { + it('should interpret valid values and set accessibility labels', async () => { const testCases = [ { value: '5.5.0', @@ -293,7 +257,7 @@ describe(`sbb-datepicker`, () => { } }); - it('should not touch invalid values', async function (this: Context) { + it('should not touch invalid values', async () => { const testCases = [ { value: '.12.2020', interpretedAs: '.12.2020' }, { value: '24..1995', interpretedAs: '24..1995' }, @@ -339,6 +303,170 @@ describe(`sbb-datepicker`, () => { `); }); + + describe('date validation', () => { + let formField: SbbFormFieldElement; + let datePicker: SbbDatepickerElement; + let input: HTMLInputElement; + + beforeEach(async () => { + formField = await fixture( + html` + + + `, + ); + + datePicker = formField.querySelector('sbb-datepicker')!; + input = formField.querySelector('input')!; + }); + + describe(`findPreviousAvailableDate`, () => { + it('get date without dateFilter and without min', async () => { + const availableDate: Date = datePicker.findPreviousAvailableDate( + new Date(2023, 1, 26, 0, 0, 0, 0), + ); + expect(availableDate.getTime()).to.equal(new Date(2023, 1, 25, 0, 0, 0, 0).getTime()); + }); + + it('get date without dateFilter and with current date equal to min date', async () => { + const date = new Date(2023, 1, 26, 0, 0, 0, 0); + input.min = String(date.valueOf() / 1000); + const availableDate: Date = datePicker.findPreviousAvailableDate(date); + expect(availableDate.getTime()).to.equal(date.getTime()); + }); + + it('get date with dateFilter and min', async () => { + const minDate = new Date(2023, 1, 26, 0, 0, 0, 0); + + datePicker.dateFilter = (d: Date | null) => d?.getDate() !== 27; + input.min = String(minDate.valueOf() / 1000); + + const availableDate: Date = datePicker.findPreviousAvailableDate( + new Date(2023, 1, 28, 0, 0, 0, 0), + ); + expect(availableDate.getTime()).to.equal(minDate.getTime()); + }); + }); + + describe(`findNextAvailableDate`, () => { + it('get date without max and without dateFilter', async () => { + const availableDate: Date = datePicker.findNextAvailableDate( + new Date(2023, 1, 26, 0, 0, 0, 0), + ); + expect(availableDate.getTime()).to.equal(new Date(2023, 1, 27, 0, 0, 0, 0).getTime()); + }); + + it('get date without dateFilter with current date equal to max date', async () => { + const date: Date = new Date(2023, 1, 26, 0, 0, 0, 0); + input.max = String(date.valueOf() / 1000); + const availableDate: Date = datePicker.findNextAvailableDate(date); + expect(availableDate.getTime()).to.equal(date.getTime()); + }); + + it('get date with dateFilter and max', async () => { + const maxDate = new Date(2023, 1, 28, 0, 0, 0, 0); + + datePicker.dateFilter = (d: Date | null) => d?.getDate() !== 27; + input.min = String(maxDate.valueOf() / 1000); + + const availableDate: Date = datePicker.findNextAvailableDate( + new Date(2023, 1, 26, 0, 0, 0, 0), + ); + expect(availableDate.getTime()).to.equal(maxDate.getTime()); + }); + }); + + describe(`validation`, () => { + describe('invalid', () => { + it('get invalid date with min', async () => { + input.min = new Date('2023-02-26').toISOString(); + await waitForLitRender(formField); + + typeInElement(input, '20.02.2023'); + input.blur(); + await waitForLitRender(formField); + + expect(input.value).to.be.equal('Mo, 20.02.2023'); + expect(input).to.have.attribute('data-sbb-invalid'); + }); + + it('get invalid date with max', async () => { + input.max = new Date('2023-02-26').toISOString(); + await waitForLitRender(formField); + + typeInElement(input, '28.02.2023'); + input.blur(); + await waitForLitRender(formField); + + expect(input.value).to.be.equal('Tu, 28.02.2023'); + expect(input).to.have.attribute('data-sbb-invalid'); + }); + + it('get invalid date with dateFilter', async () => { + datePicker.dateFilter = (d: Date | null) => + d!.getTime() > new Date('2024-12-31').valueOf(); + await waitForLitRender(formField); + + typeInElement(input, '28.02.2023'); + input.blur(); + await waitForLitRender(formField); + + expect(input.value).to.be.equal('Tu, 28.02.2023'); + expect(input).to.have.attribute('data-sbb-invalid'); + }); + }); + + describe('valid', () => { + it('get valid date without dateFilter, min and max', async () => { + typeInElement(input, '25.02.2023'); + input.blur(); + await waitForLitRender(formField); + + expect(input.value).to.be.equal('Sa, 25.02.2023'); + expect(input).not.to.have.attribute('data-sbb-invalid'); + }); + + it('get valid date with min', async () => { + input.min = new Date('2023-02-01').toISOString(); + await waitForLitRender(formField); + + typeInElement(input, '20.02.2023'); + input.blur(); + await waitForLitRender(formField); + + expect(input.value).to.be.equal('Mo, 20.02.2023'); + expect(input).not.to.have.attribute('data-sbb-invalid'); + }); + + it('get valid date with max', async () => { + input.max = new Date('2023-03-31').toISOString(); + await waitForLitRender(formField); + + typeInElement(input, '28.02.2023'); + input.blur(); + await waitForLitRender(formField); + + expect(input.value).to.be.equal('Tu, 28.02.2023'); + expect(input).not.to.have.attribute('data-sbb-invalid'); + }); + + it('get invalid date with dateFilter', async () => { + datePicker.dateFilter = (d: Date | null) => + d!.getTime() > new Date('2022-01-01').valueOf(); + await waitForLitRender(formField); + + typeInElement(input, '28.02.2023'); + input.blur(); + + await waitForLitRender(formField); + + expect(input.value).to.be.equal('Tu, 28.02.2023'); + expect(input).not.to.have.attribute('data-sbb-invalid'); + }); + }); + }); + }); }); describe(`getDatePicker`, () => { @@ -400,170 +528,4 @@ describe(`sbb-datepicker`, () => { expect(findInput(picker, 'input')).to.equal(input); }); }); - - describe(`getAvailableDate`, () => { - it('with dateFilter', async () => { - const availableDate: Date = getAvailableDate( - new Date(2024, 0, 1, 0, 0, 0, 0), - 1, - (d: Date) => d.getDay() === 1, - new NativeDateAdapter(), - ); - expect(availableDate.getTime()).to.equal(new Date(2024, 0, 8, 0, 0, 0, 0).getTime()); - }); - - it('without dateFilter', async () => { - const availableDate: Date = getAvailableDate( - new Date(2024, 0, 1, 0, 0, 0, 0), - 1, - () => true, - new NativeDateAdapter(), - ); - expect(availableDate.getTime()).to.equal(new Date(2024, 0, 2, 0, 0, 0, 0).getTime()); - }); - }); - - describe(`findPreviousAvailableDate`, () => { - it('get date without dateFilter and without min', async () => { - const availableDate: Date = findPreviousAvailableDate( - new Date(2023, 1, 26, 0, 0, 0, 0), - null, - new NativeDateAdapter(), - null, - ); - expect(availableDate.getTime()).to.equal(new Date(2023, 1, 25, 0, 0, 0, 0).getTime()); - }); - - it('get date without dateFilter and with current date equal to min date', async () => { - const date = new Date(2023, 1, 26, 0, 0, 0, 0); - const availableDate: Date = findPreviousAvailableDate( - date, - null, - new NativeDateAdapter(), - date.valueOf() / 1000, - ); - expect(availableDate.getTime()).to.equal(date.getTime()); - }); - - it('get date with dateFilter and min', async () => { - const minDate = new Date(2023, 1, 26, 0, 0, 0, 0); - const availableDate: Date = findPreviousAvailableDate( - new Date(2023, 1, 28, 0, 0, 0, 0), - (d: Date) => d.getDate() !== 27, - new NativeDateAdapter(), - minDate.valueOf() / 1000, - ); - expect(availableDate.getTime()).to.equal(minDate.getTime()); - }); - }); - - describe(`findNextAvailableDate`, () => { - it('get date without max and without dateFilter', async () => { - const availableDate: Date = findNextAvailableDate( - new Date(2023, 1, 26, 0, 0, 0, 0), - null, - new NativeDateAdapter(), - null, - ); - expect(availableDate.getTime()).to.equal(new Date(2023, 1, 27, 0, 0, 0, 0).getTime()); - }); - - it('get date without dateFilter with current date equal to max date', async () => { - const date: Date = new Date(2023, 1, 26, 0, 0, 0, 0); - const availableDate: Date = findNextAvailableDate( - date, - null, - new NativeDateAdapter(), - date.valueOf() / 1000, - ); - expect(availableDate.getTime()).to.equal(date.getTime()); - }); - - it('get date with dateFilter and max', async () => { - const maxDate = new Date(2023, 1, 28, 0, 0, 0, 0); - const availableDate: Date = findNextAvailableDate( - new Date(2023, 1, 26, 0, 0, 0, 0), - (d: Date) => d.getDate() !== 27, - new NativeDateAdapter(), - maxDate.valueOf() / 1000, - ); - expect(availableDate.getTime()).to.equal(maxDate.getTime()); - }); - }); - - describe(`isDateAvailable`, () => { - describe('invalid', () => { - it('get invalid date with min', async () => { - expect( - isDateAvailable( - new Date('2023-02-20'), - null, - new Date('2023-02-26').valueOf() / 1000, - null, - ), - ).to.be.false; - }); - - it('get invalid date with max', async () => { - expect( - isDateAvailable( - new Date('2023-02-28'), - null, - null, - new Date('2023-02-26').valueOf() / 1000, - ), - ).to.be.false; - }); - - it('get invalid date with dateFilter', async () => { - expect( - isDateAvailable( - new Date('2023-02-28'), - (d: Date) => d.getTime() > new Date('2024-12-31').valueOf(), - null, - null, - ), - ).to.be.false; - }); - }); - - describe('valid', function () { - it('get valid date without dateFilter, min and max', async () => { - expect(isDateAvailable(new Date('2023-02-25'), null, null, null)).to.be.true; - }); - - it('get valid date with min', async () => { - expect( - isDateAvailable( - new Date('2023-02-20'), - null, - new Date('2023-02-01').valueOf() / 1000, - null, - ), - ).to.be.true; - }); - - it('get valid date with max', async () => { - expect( - isDateAvailable( - new Date('2023-02-28'), - null, - null, - new Date('2023-03-31').valueOf() / 1000, - ), - ).to.be.true; - }); - - it('get invalid date with dateFilter', async () => { - expect( - isDateAvailable( - new Date('2023-02-28'), - (d: Date) => d.getTime() > new Date('2022-01-01').valueOf(), - null, - null, - ), - ).to.be.true; - }); - }); - }); }); diff --git a/src/elements/datepicker/datepicker/datepicker.stories.ts b/src/elements/datepicker/datepicker/datepicker.stories.ts index e5d82c10ea..56a6740bbf 100644 --- a/src/elements/datepicker/datepicker/datepicker.stories.ts +++ b/src/elements/datepicker/datepicker/datepicker.stories.ts @@ -118,44 +118,6 @@ const dateFilter: InputType = { }, }; -const handlingFunctions = [ - { dateParser: undefined, format: undefined }, - { - dateParser: (s: string) => new Date(s), - format: (d: Date) => - `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String( - d.getDate(), - ).padStart(2, '0')}`, - }, - { - dateParser: (s: string) => - new Date(+s.substring(4, s.length), +s.substring(2, 4) - 1, +s.substring(0, 2)), - format: (d: Date) => - `${String(d.getDate()).padStart(2, '0')}${String(d.getMonth() + 1).padStart( - 2, - '0', - )}${d.getFullYear()}`, - }, -]; -const dateHandling: InputType = { - name: 'Date Handling', - description: - 'Change the default date handling option with a combination of `dateParser` and `format` properties.', - options: Object.keys(filterFunctions), - mapping: handlingFunctions, - control: { - type: 'select', - labels: { - 0: 'Default', - 1: 'ISO String (YYYY-MM-DD)', - 2: 'Business (DDMMYY)', - }, - }, - table: { - category: 'Datepicker attribute', - }, -}; - const ariaLabel: InputType = { control: { type: 'text', @@ -230,7 +192,6 @@ const basicArgTypes: ArgTypes = { max, wide, dateFilter, - dateHandling, now, 'aria-label': ariaLabel, }; @@ -245,7 +206,6 @@ const basicArgs: Args = { max: undefined, wide: false, dateFilter: dateFilter.options![0], - dateHandling: dateHandling.options![0], now: undefined, 'aria-label': undefined, }; @@ -318,7 +278,6 @@ const TemplateFormField = ({ negative, wide, dateFilter, - dateHandling, now, ...args }: Args): TemplateResult => { @@ -341,8 +300,6 @@ const TemplateFormField = ({ /> changeEventHandler(event)} now=${convertMillisecondsToSeconds(now)} @@ -415,12 +372,6 @@ export const InFormFieldWithDateFilter: StoryObj = { args: { ...formFieldBasicArgs, dateFilter: dateFilter.options![1] }, }; -export const InFormFieldWithDateParser: StoryObj = { - render: TemplateFormField, - argTypes: { ...formFieldBasicArgsTypes }, - args: { ...formFieldBasicArgs, value: '2023-02-12', dateHandling: dateHandling.options![1] }, -}; - export const InFormFieldSmall: StoryObj = { render: TemplateFormField, argTypes: { ...formFieldBasicArgsTypes }, diff --git a/src/elements/datepicker/datepicker/datepicker.ts b/src/elements/datepicker/datepicker/datepicker.ts index 3cfff34bbd..3db9792ed2 100644 --- a/src/elements/datepicker/datepicker/datepicker.ts +++ b/src/elements/datepicker/datepicker/datepicker.ts @@ -28,9 +28,6 @@ export interface SbbInputUpdateEvent { max?: string | number; } -// TODO(breaking-change): Inline deprecated functions in SbbDatepickerElement as public methods -// where possible and use these methods where the functions are currently used. - /** * Given a SbbDatepickerPreviousDayElement, a SbbDatepickerNextDayElement or a SbbDatepickerToggleElement component, * it returns the related SbbDatepickerElement reference, if exists. @@ -50,118 +47,6 @@ export function getDatePicker( return findReferencedElement>(trigger); } -/** - * Returns the first available date before or after a given one, considering the SbbDatepickerElement `dateFilter` property. - * @param date The starting date for calculations. - * @param delta The number of days to add/subtract from the starting one. - * @param dateFilter The dateFilter function from the SbbDatepickerElement. - * @param dateAdapter The adapter class. - * - * @deprecated Not intended as public API. - */ -export function getAvailableDate( - date: T, - delta: number, - dateFilter: ((date: T) => boolean) | null, - dateAdapter: DateAdapter, -): T { - let availableDate = dateAdapter.addCalendarDays(date, delta); - - if (dateFilter) { - while (!dateFilter(availableDate)) { - availableDate = dateAdapter.addCalendarDays(availableDate, delta); - } - } - - return availableDate; -} - -/** - * Calculates the first available date before the given one, - * considering the SbbDatepickerElement `dateFilter` property and `min` parameter (e.g. from the self-named input's attribute). - * @param date The starting date for calculations. - * @param dateFilter The dateFilter function from the SbbDatepickerElement. - * @param dateAdapter The adapter class. - * @param min The minimum value to consider in calculations. - * - * @deprecated Not intended as public API. - */ -export function findPreviousAvailableDate( - date: T, - dateFilter: ((date: T) => boolean) | null, - dateAdapter: DateAdapter, - min: string | number | null, -): T { - const previousDate = getAvailableDate(date, -1, dateFilter, dateAdapter); - const dateMin = dateAdapter.deserialize(min); - - if ( - !dateMin || - (dateAdapter.isValid(dateMin) && dateAdapter.compareDate(previousDate, dateMin) >= 0) - ) { - return previousDate; - } - return date; -} - -/** - * Calculates the first available date after the given one, - * considering the SbbDatepickerElement `dateFilter` property and `max` parameter (e.g. from the self-named input's attribute). - * @param date The starting date for calculations. - * @param dateFilter The dateFilter function from the SbbDatepickerElement. - * @param dateAdapter The adapter class. - * @param max The maximum value to consider in calculations. - * - * @deprecated Not intended as public API. - */ -export function findNextAvailableDate( - date: T, - dateFilter: ((date: T) => boolean) | null, - dateAdapter: DateAdapter, - max: string | number | null, -): T { - const nextDate = getAvailableDate(date, 1, dateFilter, dateAdapter); - const dateMax = dateAdapter.deserialize(max); - - if ( - !dateMax || - (dateAdapter.isValid(dateMax) && dateAdapter.compareDate(nextDate, dateMax) <= 0) - ) { - return nextDate; - } - return date; -} - -/** - * Checks if the provided date is a valid one, considering the SbbDatepickerElement `dateFilter` property - * and `min` and `max` parameters (e.g. from the self-named input's attributes). - * @param date The starting date for calculations. - * @param dateFilter The dateFilter function from the SbbDatepickerElement. - * @param min The minimum value to consider in calculations. - * @param max The maximum value to consider in calculations. - * - * @deprecated Not intended as public API. - */ -export function isDateAvailable( - date: T, - dateFilter: ((date: T) => boolean) | null, - min: string | number | null | undefined, - max: string | number | null | undefined, -): boolean { - const dateAdapter: DateAdapter = readConfig().datetime?.dateAdapter ?? defaultDateAdapter; - const dateMin = dateAdapter.deserialize(min); - const dateMax = dateAdapter.deserialize(max); - - if ( - (dateAdapter.isValid(dateMin) && dateAdapter.compareDate(date, dateMin!) < 0) || - (dateAdapter.isValid(dateMax) && dateAdapter.compareDate(date, dateMax!) > 0) - ) { - return false; - } - - return dateFilter ? dateFilter(date) : true; -} - export const datepickerControlRegisteredEventFactory = (): CustomEvent => new CustomEvent('datepickerControlRegistered', { bubbles: false, @@ -198,25 +83,12 @@ class SbbDatepickerElement extends LitElement { @property({ attribute: false }) public accessor dateFilter: (date: T | null) => boolean = () => true; - /** - * A function used to parse string value into dates. - * @deprecated No longer required. - */ - @property({ attribute: false }) public accessor dateParser: ((value: string) => T) | null = null; - - /** - * A function used to format dates into the preferred string format. - * @deprecated No longer required. - */ - @property({ attribute: false }) public accessor format: ((date: T) => string) | null = null; - /** Reference of the native input connected to the datepicker. */ @property() public accessor input: string | HTMLElement | null = null; - // TODO: Change undefined to null as a breaking change. /** A configured date which acts as the current date instead of the real current date. Recommended for testing purposes. */ @property() - public set now(value: SbbDateLike | undefined) { + public set now(value: SbbDateLike | null) { this._now = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); } public get now(): T { @@ -301,7 +173,7 @@ class SbbDatepickerElement extends LitElement { this._inputElement.placeholder = i18nDatePickerPlaceholder[this._language.current]; } if (this.valueAsDate) { - this._inputElement.value = this._format(this.valueAsDate); + this._inputElement.value = this._dateAdapter.format(this.valueAsDate); } } }); @@ -346,22 +218,6 @@ class SbbDatepickerElement extends LitElement { this._setAriaLiveMessage(); } - /** - * Gets the input value with the correct date format. - * @deprecated Use property valueAsDate instead. - */ - public getValueAsDate(): T | undefined { - return this.valueAsDate ?? undefined; - } - - /** - * Set the input value to the correctly formatted value. - * @deprecated Use property valueAsDate instead. - */ - public setValueAsDate(date: SbbDateLike): void { - this.valueAsDate = date; - } - /** * @internal * Whether a custom now is configured. @@ -421,7 +277,7 @@ class SbbDatepickerElement extends LitElement { return false; } - const formattedDate = this.valueAsDate ? this._format(this.valueAsDate!) : ''; + const formattedDate = this.valueAsDate ? this._dateAdapter.format(this.valueAsDate!) : ''; if (formattedDate && this._inputElement.value !== formattedDate) { this._inputElement.value = formattedDate; this._inputElement.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true })); @@ -437,15 +293,7 @@ class SbbDatepickerElement extends LitElement { return; } - const isEmptyOrValid = - !this._inputElement.value || - (!!this.valueAsDate && - isDateAvailable( - this.valueAsDate, - this.dateFilter, - this._inputElement?.min, - this._inputElement?.max, - )); + const isEmptyOrValid = !this._inputElement.value || this._isDateAvailable(); const wasValid = !this._inputElement.hasAttribute('data-sbb-invalid'); this._inputElement.toggleAttribute('data-sbb-invalid', !isEmptyOrValid); if (wasValid !== isEmptyOrValid) { @@ -455,10 +303,11 @@ class SbbDatepickerElement extends LitElement { private _parseInput(deserializeAsFallback = false): void { const value = this._inputElement!.value; - const parse = this.dateParser ?? ((v: string) => this._dateAdapter.parse(v, this.now)); // We are assigning directly to the private backing property of valueAsDate // as we don't want to trigger a blur event during this time. - this._valueAsDate = this._dateAdapter.getValidDateOrNull(parse(value)); + this._valueAsDate = this._dateAdapter.getValidDateOrNull( + this._dateAdapter.parse(value, this.now), + ); if (deserializeAsFallback && !this._valueAsDate) { this._valueAsDate = this._dateAdapter.getValidDateOrNull( this._dateAdapter.deserialize(value), @@ -466,10 +315,6 @@ class SbbDatepickerElement extends LitElement { } } - private _format(date: T): string { - return this.format ? this.format(date) : this._dateAdapter.format(date); - } - private _setAriaLiveMessage(): void { const containerElement: HTMLParagraphElement | null | undefined = this.shadowRoot?.querySelector?.('#status-container'); @@ -487,6 +332,84 @@ class SbbDatepickerElement extends LitElement { protected override render(): TemplateResult { return html`

`; } + + /** + * Calculates the first available date before the given one, + * considering the SbbDatepickerElement `dateFilter` property and `min` parameter (e.g. from the self-named input's attribute). + * @param date The starting date for calculations. + */ + public findPreviousAvailableDate(date: T): T { + const previousDate = this._findAvailableDate(date, -1); + const dateMin = this._dateAdapter.deserialize(this._inputElement?.min); + + if ( + !dateMin || + (this._dateAdapter.isValid(dateMin) && + this._dateAdapter.compareDate(previousDate, dateMin) >= 0) + ) { + return previousDate; + } + return date; + } + + /** + * Calculates the first available date after the given one, + * considering the SbbDatepickerElement `dateFilter` property and `max` parameter (e.g. from the self-named input's attribute). + * @param date The starting date for calculations. + */ + public findNextAvailableDate(date: T): T { + const nextDate = this._findAvailableDate(date, 1); + const dateMax = this._dateAdapter.deserialize(this._inputElement?.max); + + if ( + !dateMax || + (this._dateAdapter.isValid(dateMax) && this._dateAdapter.compareDate(nextDate, dateMax) <= 0) + ) { + return nextDate; + } + return date; + } + + /** + * Returns the first available date before or after a given one, considering the `dateFilter` property. + * @param date The starting date for calculations. + * @param delta The number of days to add/subtract from the starting one. + */ + private _findAvailableDate(date: T, delta: number): T { + let availableDate = this._dateAdapter.addCalendarDays(date, delta); + + if (this.dateFilter) { + while (!this.dateFilter(availableDate)) { + availableDate = this._dateAdapter.addCalendarDays(availableDate, delta); + } + } + + return availableDate; + } + + /** + * Checks if valueAsDate is valid, considering the SbbDatepickerElement `dateFilter` property + * and `min` and `max` parameters (e.g. from the self-named input's attributes). + */ + private _isDateAvailable(): boolean { + if (!this.valueAsDate) { + return false; + } + + const dateMin = this._dateAdapter.deserialize(this._inputElement?.min); + const dateMax = this._dateAdapter.deserialize(this._inputElement?.max); + + if ( + (this._dateAdapter.isValid(dateMin) && + this._dateAdapter.compareDate(this.valueAsDate, dateMin!) < 0) || + (this._dateAdapter.isValid(dateMax) && + this._dateAdapter.compareDate(this.valueAsDate, dateMax!) > 0) + ) { + return false; + } + + return this.dateFilter ? this.dateFilter(this.valueAsDate) : true; + } } declare global { diff --git a/src/elements/datepicker/datepicker/readme.md b/src/elements/datepicker/datepicker/readme.md index a8ca76d0c0..ed9eccb436 100644 --- a/src/elements/datepicker/datepicker/readme.md +++ b/src/elements/datepicker/datepicker/readme.md @@ -56,44 +56,12 @@ current value (e.g. from `event.target.valueAsDate`) or to set the value program When the `valueAsDate` property is programmatically assigned, a `blur` event is fired on the input to ensure compatibility with any framework that relies on that event to update the current state. -## Custom date formats +## Custom current date To simulate the current date, you can use the `now` property, which accepts a `Date` or a timestamp in seconds (as number or string). This is helpful if you need a specific state of the component. -Using a combination of the `dateParser` and `format` properties, it's possible to configure the datepicker -to accept date formats other than the default `EE, dd.mm.yyyy`. -In the following example the datepicker is set to accept dates in the format `yyyy-mm-dd`. -In particular, `dateParser` is the function that the component uses internally to decode strings and parse them into `Date` objects, -while the `format` function is the one that the component uses internally to display a given `Date` object as a string. - -```ts -// datePicker is a SbbDatepickerElement element -datePicker.dateParser = (value: string) => { - // You should implement some kind of input validation - if (!value || !isValid(value)) { - return undefined; - } - - return new Date(value); -}; - -datePicker.format = (value: Date) => { - if (!value) { - return ''; - } - - const offset = value.getTimezoneOffset(); - value = new Date(yourDate.getTime() - offset * 60 * 1000); - return yourDate.toISOString().split('T')[0]; -}; -``` - -Usually these functions need to be changed together, although in simple cases where the default `dateParser` might still work properly -(e.g., in case we wanted to accept the format `dd.mm.yyyy`), it's possible to provide just the `format` function. -For custom `format` functions is recommended to use the `Intl.DateTimeFormat` API, as it's done in the default implementation. - ## Validation Change @@ -104,22 +72,20 @@ Whenever the validation state changes (e.g., a valid value becomes invalid or vi ## Properties -| Name | Attribute | Privacy | Type | Default | Description | -| ------------- | --------- | ------- | -------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------- | -| `dateFilter` | - | public | `(date: T \| null) => boolean` | | A function used to filter out dates. | -| `dateParser` | - | public | `((value: string) => T) \| null` | `null` | A function used to parse string value into dates. | -| `format` | - | public | `((date: T) => string) \| null` | `null` | A function used to format dates into the preferred string format. | -| `input` | `input` | public | `string \| HTMLElement \| null` | `null` | Reference of the native input connected to the datepicker. | -| `now` | `now` | public | `T` | | A configured date which acts as the current date instead of the real current date. Recommended for testing purposes. | -| `valueAsDate` | - | public | `T \| null` | | The currently selected date as a Date or custom date provider instance. | -| `wide` | `wide` | public | `boolean` | `false` | If set to true, two months are displayed. | +| Name | Attribute | Privacy | Type | Default | Description | +| ------------- | --------- | ------- | ------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------- | +| `dateFilter` | - | public | `(date: T \| null) => boolean` | | A function used to filter out dates. | +| `input` | `input` | public | `string \| HTMLElement \| null` | `null` | Reference of the native input connected to the datepicker. | +| `now` | `now` | public | `T` | | A configured date which acts as the current date instead of the real current date. Recommended for testing purposes. | +| `valueAsDate` | - | public | `T \| null` | | The currently selected date as a Date or custom date provider instance. | +| `wide` | `wide` | public | `boolean` | `false` | If set to true, two months are displayed. | ## Methods -| Name | Privacy | Description | Parameters | Return | Inherited From | -| ---------------- | ------- | ----------------------------------------------------- | ---------------------- | ---------------- | -------------- | -| `getValueAsDate` | public | Gets the input value with the correct date format. | | `T \| undefined` | | -| `setValueAsDate` | public | Set the input value to the correctly formatted value. | `date: SbbDateLike` | `void` | | +| Name | Privacy | Description | Parameters | Return | Inherited From | +| --------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------ | -------------- | +| `findNextAvailableDate` | public | Calculates the first available date after the given one, considering the SbbDatepickerElement `dateFilter` property and `max` parameter (e.g. from the self-named input's attribute). | `date: T` | `T` | | +| `findPreviousAvailableDate` | public | Calculates the first available date before the given one, considering the SbbDatepickerElement `dateFilter` property and `min` parameter (e.g. from the self-named input's attribute). | `date: T` | `T` | | ## Events From da97f779108e94e49853ab0ee2e8ea4a24783fa7 Mon Sep 17 00:00:00 2001 From: Lukas Spirig Date: Wed, 27 Nov 2024 16:29:02 +0100 Subject: [PATCH 3/7] build: define scss api configuration (#3258) This should facilitate the Vite 6 upgrade. --- vite.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/vite.config.ts b/vite.config.ts index c30f0df392..7ce13e4f86 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -11,6 +11,7 @@ export default defineConfig({ scss: { // https://vitejs.dev/config/shared-options#css-preprocessoroptions // TODO: api: 'modern', + api: 'legacy', silenceDeprecations: ['legacy-js-api'], }, }, From ce8c31817facb2dbbfcc0214d3e7553b3d8caf1c Mon Sep 17 00:00:00 2001 From: Davide Mininni <101575400+DavideMininni-Fincons@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:42:10 +0100 Subject: [PATCH 4/7] fix(sbb-mini-button): remove useless base class (#3257) BREAKING CHANGE: the `SbbMiniButtonBaseElement` is used only in the `sbb-mini-button-component`, so it can be safely removed to avoid redundant code. --- src/elements/button/mini-button.ts | 1 - .../mini-button/mini-button-base-element.ts | 16 ---------------- src/elements/button/mini-button/mini-button.ts | 17 +++++++++++++---- 3 files changed, 13 insertions(+), 21 deletions(-) delete mode 100644 src/elements/button/mini-button/mini-button-base-element.ts diff --git a/src/elements/button/mini-button.ts b/src/elements/button/mini-button.ts index 379817a955..65b7e88ef6 100644 --- a/src/elements/button/mini-button.ts +++ b/src/elements/button/mini-button.ts @@ -1,2 +1 @@ -export * from './mini-button/mini-button-base-element.js'; export * from './mini-button/mini-button.js'; diff --git a/src/elements/button/mini-button/mini-button-base-element.ts b/src/elements/button/mini-button/mini-button-base-element.ts deleted file mode 100644 index 31a57fda41..0000000000 --- a/src/elements/button/mini-button/mini-button-base-element.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { TemplateResult } from 'lit'; - -import { SbbButtonBaseElement } from '../../core/base-elements.js'; -import { slotState } from '../../core/decorators.js'; -import { SbbNegativeMixin } from '../../core/mixins.js'; -import { SbbIconNameMixin } from '../../icon.js'; - -export -@slotState() -abstract class SbbMiniButtonBaseElement extends SbbNegativeMixin( - SbbIconNameMixin(SbbButtonBaseElement), -) { - protected override renderTemplate(): TemplateResult { - return super.renderIconSlot(); - } -} diff --git a/src/elements/button/mini-button/mini-button.ts b/src/elements/button/mini-button/mini-button.ts index c4ce6c4f71..04c07518a3 100644 --- a/src/elements/button/mini-button/mini-button.ts +++ b/src/elements/button/mini-button/mini-button.ts @@ -1,9 +1,11 @@ -import type { CSSResultGroup } from 'lit'; +import type { CSSResultGroup, TemplateResult } from 'lit'; import { customElement } from 'lit/decorators.js'; -import { SbbDisabledTabIndexActionMixin } from '../../core/mixins.js'; +import { SbbButtonBaseElement } from '../../core/base-elements.js'; +import { slotState } from '../../core/decorators.js'; +import { SbbDisabledTabIndexActionMixin, SbbNegativeMixin } from '../../core/mixins.js'; +import { SbbIconNameMixin } from '../../icon.js'; -import { SbbMiniButtonBaseElement } from './mini-button-base-element.js'; import style from './mini-button.scss?lit&inline'; /** @@ -14,8 +16,15 @@ import style from './mini-button.scss?lit&inline'; */ export @customElement('sbb-mini-button') -class SbbMiniButtonElement extends SbbDisabledTabIndexActionMixin(SbbMiniButtonBaseElement) { +@slotState() +class SbbMiniButtonElement extends SbbDisabledTabIndexActionMixin( + SbbNegativeMixin(SbbIconNameMixin(SbbButtonBaseElement)), +) { public static override styles: CSSResultGroup = style; + + protected override renderTemplate(): TemplateResult { + return super.renderIconSlot(); + } } declare global { From da64d5d3affba1e98f57ec702c1651f17d842e35 Mon Sep 17 00:00:00 2001 From: Jeri Peier Date: Thu, 28 Nov 2024 07:56:34 +0100 Subject: [PATCH 5/7] fix: remove deprecated `didChange` events where possible (#3253) Closes #3255 BREAKING CHANGE: remove deprecated `didChange` events from `sbb-checkbox`, `sbb-checkbox-panel`, `sbb-toggle-check`, `sbb-select`, `sbb-toggle` and `sbb-datepicker`. Use `change` event as alternative. --- .../autocomplete/autocomplete-base-element.ts | 5 +++- .../checkbox/checkbox-panel/checkbox-panel.ts | 2 -- .../checkbox/checkbox-panel/readme.md | 9 +++--- src/elements/checkbox/checkbox/checkbox.ts | 5 ---- src/elements/checkbox/checkbox/readme.md | 9 +++--- .../mixins/form-associated-checkbox-mixin.ts | 1 - .../datepicker/datepicker/datepicker.spec.ts | 4 +++ .../datepicker/datepicker/datepicker.ts | 29 ++++++++++--------- src/elements/datepicker/datepicker/readme.md | 2 +- .../form-field/form-field/form-field.ts | 19 ++++++++++-- src/elements/select/readme.md | 17 +++++------ src/elements/select/select.ts | 9 ------ src/elements/time-input/time-input.spec.ts | 18 ++++++++++-- src/elements/time-input/time-input.ts | 27 +++++++++++------ src/elements/toggle-check/readme.md | 9 +++--- src/elements/toggle-check/toggle-check.ts | 4 --- src/elements/toggle/toggle/readme.md | 7 ++--- src/elements/toggle/toggle/toggle.ts | 12 -------- 18 files changed, 96 insertions(+), 92 deletions(-) diff --git a/src/elements/autocomplete/autocomplete-base-element.ts b/src/elements/autocomplete/autocomplete-base-element.ts index fa4b931dc2..613efb10a2 100644 --- a/src/elements/autocomplete/autocomplete-base-element.ts +++ b/src/elements/autocomplete/autocomplete-base-element.ts @@ -179,7 +179,10 @@ export abstract class SbbAutocompleteBaseElement extends SbbNegativeMixin( if (this.triggerElement) { // Set the option value - this.triggerElement.value = target.value as string; + // In order to support React onChange event, we have to get the setter and call it. + // https://github.com/facebook/react/issues/11600#issuecomment-345813130 + const setValue = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')!.set!; + setValue.call(this.triggerElement, target.value); // Manually trigger the change events this.triggerElement.dispatchEvent(new Event('change', { bubbles: true })); diff --git a/src/elements/checkbox/checkbox-panel/checkbox-panel.ts b/src/elements/checkbox/checkbox-panel/checkbox-panel.ts index c59c514fde..38dd538a56 100644 --- a/src/elements/checkbox/checkbox-panel/checkbox-panel.ts +++ b/src/elements/checkbox/checkbox-panel/checkbox-panel.ts @@ -38,7 +38,6 @@ export type SbbCheckboxPanelStateChange = Extract< * @slot subtext - Slot used to render a subtext under the label (only visible within a selection panel). * @slot suffix - Slot used to render additional content after the label (only visible within a selection panel). * @slot badge - Use this slot to provide a `sbb-card-badge` (optional). - * @event {CustomEvent} didChange - Deprecated. used for React. Will probably be removed once React 19 is available. * @event {Event} change - Event fired on change. * @event {InputEvent} input - Event fired on input. */ @@ -52,7 +51,6 @@ class SbbCheckboxPanelElement extends SbbPanelMixin( // FIXME using ...super.events requires: https://github.com/sbb-design-systems/lyne-components/issues/2600 public static readonly events = { - didChange: 'didChange', stateChange: 'stateChange', panelConnected: 'panelConnected', } as const; diff --git a/src/elements/checkbox/checkbox-panel/readme.md b/src/elements/checkbox/checkbox-panel/readme.md index 774ba72acc..c8a8577501 100644 --- a/src/elements/checkbox/checkbox-panel/readme.md +++ b/src/elements/checkbox/checkbox-panel/readme.md @@ -92,11 +92,10 @@ If you don't want the label to appear next to the checkbox, you can use `aria-la ## Events -| Name | Type | Description | Inherited From | -| ----------- | ------------------- | -------------------------------------------------------------------------------- | -------------- | -| `change` | `Event` | Event fired on change. | | -| `didChange` | `CustomEvent` | Deprecated. used for React. Will probably be removed once React 19 is available. | | -| `input` | `InputEvent` | Event fired on input. | | +| Name | Type | Description | Inherited From | +| -------- | ------------ | ---------------------- | -------------- | +| `change` | `Event` | Event fired on change. | | +| `input` | `InputEvent` | Event fired on input. | | ## Slots diff --git a/src/elements/checkbox/checkbox/checkbox.ts b/src/elements/checkbox/checkbox/checkbox.ts index 01afd89802..63bda13bdd 100644 --- a/src/elements/checkbox/checkbox/checkbox.ts +++ b/src/elements/checkbox/checkbox/checkbox.ts @@ -19,7 +19,6 @@ import '../../visual-checkbox.js'; * * @slot - Use the unnamed slot to add content to the `sbb-checkbox`. * @slot icon - Slot used to render the checkbox icon (disabled inside a selection panel). - * @event {CustomEvent} didChange - Deprecated. used for React. Will probably be removed once React 19 is available. * @event {Event} change - Event fired on change. * @event {InputEvent} input - Event fired on input. */ @@ -29,10 +28,6 @@ export class SbbCheckboxElement extends SbbCheckboxCommonElementMixin(SbbIconNameMixin(LitElement)) { public static override styles: CSSResultGroup = [checkboxCommonStyle, checkboxStyle]; - public static readonly events = { - didChange: 'didChange', - } as const; - /** Size variant. */ @property({ reflect: true }) @getOverride((i, v) => i.group?.size ?? v) diff --git a/src/elements/checkbox/checkbox/readme.md b/src/elements/checkbox/checkbox/readme.md index fd0d8e8de9..7af0b7b5ec 100644 --- a/src/elements/checkbox/checkbox/readme.md +++ b/src/elements/checkbox/checkbox/readme.md @@ -98,11 +98,10 @@ If you don't want the label to appear next to the checkbox, you can use `aria-la ## Events -| Name | Type | Description | Inherited From | -| ----------- | ------------------- | -------------------------------------------------------------------------------- | -------------- | -| `change` | `Event` | Event fired on change. | | -| `didChange` | `CustomEvent` | Deprecated. used for React. Will probably be removed once React 19 is available. | | -| `input` | `InputEvent` | Event fired on input. | | +| Name | Type | Description | Inherited From | +| -------- | ------------ | ---------------------- | -------------- | +| `change` | `Event` | Event fired on change. | | +| `input` | `InputEvent` | Event fired on input. | | ## Slots diff --git a/src/elements/core/mixins/form-associated-checkbox-mixin.ts b/src/elements/core/mixins/form-associated-checkbox-mixin.ts index fb7aa4911f..05f99dc8f8 100644 --- a/src/elements/core/mixins/form-associated-checkbox-mixin.ts +++ b/src/elements/core/mixins/form-associated-checkbox-mixin.ts @@ -189,7 +189,6 @@ export const SbbFormAssociatedCheckboxMixin = this.dispatchEvent(new InputEvent('input', { composed: true, bubbles: true })); this.dispatchEvent(new Event('change', { bubbles: true })); - this.dispatchEvent(new CustomEvent('didChange', { bubbles: true })); }; } diff --git a/src/elements/datepicker/datepicker/datepicker.spec.ts b/src/elements/datepicker/datepicker/datepicker.spec.ts index 3e4b6047f2..c62ceafbc0 100644 --- a/src/elements/datepicker/datepicker/datepicker.spec.ts +++ b/src/elements/datepicker/datepicker/datepicker.spec.ts @@ -82,11 +82,15 @@ describe(`sbb-datepicker`, () => { it('renders and emit event on value change', async () => { const changeSpy = new EventSpy('change', element); + const inputSpy = new EventSpy('input', element); typeInElement(input, '20/01/2023'); + expect(inputSpy.count).to.be.equal(10); + button.focus(); await changeSpy.calledOnce(); expect(input.value).to.be.equal('Fr, 20.01.2023'); expect(changeSpy.count).to.be.equal(1); + expect(inputSpy.count).to.be.equal(11); }); it('renders and interpret two digit year correctly in 2000s', async () => { diff --git a/src/elements/datepicker/datepicker/datepicker.ts b/src/elements/datepicker/datepicker/datepicker.ts index 3db9792ed2..1e23d0c3dc 100644 --- a/src/elements/datepicker/datepicker/datepicker.ts +++ b/src/elements/datepicker/datepicker/datepicker.ts @@ -13,7 +13,7 @@ import { SbbConnectedAbortController, SbbLanguageController } from '../../core/c import { type DateAdapter, defaultDateAdapter } from '../../core/datetime.js'; import { forceType } from '../../core/decorators.js'; import { findInput, findReferencedElement } from '../../core/dom.js'; -import { EventEmitter } from '../../core/eventing.js'; +import { EventEmitter, forwardEventToHost } from '../../core/eventing.js'; import { i18nDateChangedTo, i18nDatePickerPlaceholder } from '../../core/i18n.js'; import type { SbbDateLike, SbbValidationChangeEvent } from '../../core/interfaces.js'; import type { SbbDatepickerButton } from '../common.js'; @@ -56,8 +56,8 @@ export const datepickerControlRegisteredEventFactory = (): CustomEvent => /** * Combined with a native input, it displays the input's value as a formatted date. * - * @event {CustomEvent} didChange - Deprecated. used for React. Will probably be removed once React 19 is available. * @event {CustomEvent} change - Notifies that the connected input has changes. + * @event {CustomEvent} input - Notifies that the connected input fired the input event. * @event {CustomEvent} inputUpdated - Notifies that the attributes of the input connected to the datepicker have changes. * @event {CustomEvent} datePickerUpdated - Notifies that the attributes of the datepicker have changes. * @event {CustomEvent} validationChange - Emits whenever the internal validation state changes. @@ -67,7 +67,6 @@ export class SbbDatepickerElement extends LitElement { public static override styles: CSSResultGroup = style; public static readonly events = { - didChange: 'didChange', change: 'change', inputUpdated: 'inputUpdated', datePickerUpdated: 'datePickerUpdated', @@ -111,14 +110,6 @@ class SbbDatepickerElement extends LitElement { } private _valueAsDate?: T | null; - /** - * @deprecated only used for React. Will probably be removed once React 19 is available. - */ - private _didChange: EventEmitter = new EventEmitter(this, SbbDatepickerElement.events.didChange, { - bubbles: true, - cancelable: true, - }); - /** Notifies that the connected input has changes. */ private _change: EventEmitter = new EventEmitter(this, SbbDatepickerElement.events.change, { bubbles: true, @@ -249,7 +240,14 @@ class SbbDatepickerElement extends LitElement { } const options: AddEventListenerOptions = { signal: this._datePickerController.signal }; - input.addEventListener('input', () => this._parseInput(), options); + input.addEventListener( + 'input', + (e) => { + forwardEventToHost(e, this); + this._parseInput(); + }, + options, + ); input.addEventListener('change', () => this._handleInputChange(), options); this._parseInput(true); this._tryApplyFormatToInput(); @@ -269,7 +267,6 @@ class SbbDatepickerElement extends LitElement { this._validateDate(); this._setAriaLiveMessage(); this._change.emit(); - this._didChange.emit(); } private _tryApplyFormatToInput(): boolean { @@ -279,7 +276,11 @@ class SbbDatepickerElement extends LitElement { const formattedDate = this.valueAsDate ? this._dateAdapter.format(this.valueAsDate!) : ''; if (formattedDate && this._inputElement.value !== formattedDate) { - this._inputElement.value = formattedDate; + // In order to support React onChange event, we have to get the setter and call it. + // https://github.com/facebook/react/issues/11600#issuecomment-345813130 + const setValue = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')!.set!; + setValue.call(this._inputElement, formattedDate); + this._inputElement.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true })); this._inputElement.dispatchEvent(new Event('change', { bubbles: true, composed: true })); return true; diff --git a/src/elements/datepicker/datepicker/readme.md b/src/elements/datepicker/datepicker/readme.md index ed9eccb436..c20f66281a 100644 --- a/src/elements/datepicker/datepicker/readme.md +++ b/src/elements/datepicker/datepicker/readme.md @@ -93,6 +93,6 @@ Whenever the validation state changes (e.g., a valid value becomes invalid or vi | ------------------- | --------------------------------------- | ----------------------------------------------------------------------------------- | -------------- | | `change` | `CustomEvent` | Notifies that the connected input has changes. | | | `datePickerUpdated` | `CustomEvent` | Notifies that the attributes of the datepicker have changes. | | -| `didChange` | `CustomEvent` | Deprecated. used for React. Will probably be removed once React 19 is available. | | +| `input` | `CustomEvent` | Notifies that the connected input fired the input event. | | | `inputUpdated` | `CustomEvent` | Notifies that the attributes of the input connected to the datepicker have changes. | | | `validationChange` | `CustomEvent` | Emits whenever the internal validation state changes. | | diff --git a/src/elements/form-field/form-field/form-field.ts b/src/elements/form-field/form-field/form-field.ts index 3dd7db95de..4a5b2a32cb 100644 --- a/src/elements/form-field/form-field/form-field.ts +++ b/src/elements/form-field/form-field/form-field.ts @@ -287,10 +287,23 @@ class SbbFormFieldElement extends SbbNegativeMixin(SbbHydrationMixin(LitElement) signal: this._inputAbortController.signal, }); - inputFocusElement = (this._input as SbbSelectElement).inputElement; + const selectInput = this._input as SbbSelectElement; + inputFocusElement = selectInput.inputElement; + + // If inputElement is not yet ready, try a second time after updating. + if (!inputFocusElement) { + const controller = { + hostUpdated: () => { + selectInput.removeController(controller); + this._registerInputListener(); + }, + }; + + selectInput.addController(controller); + } } - inputFocusElement.addEventListener( + inputFocusElement?.addEventListener( 'focusin', () => { this.toggleAttribute('data-input-focused', true); @@ -304,7 +317,7 @@ class SbbFormFieldElement extends SbbNegativeMixin(SbbHydrationMixin(LitElement) }, ); - inputFocusElement.addEventListener( + inputFocusElement?.addEventListener( 'focusout', () => ['data-focus-origin', 'data-input-focused'].forEach((name) => this.removeAttribute(name)), diff --git a/src/elements/select/readme.md b/src/elements/select/readme.md index 3ab77cdccd..629e0ae9ee 100644 --- a/src/elements/select/readme.md +++ b/src/elements/select/readme.md @@ -163,15 +163,14 @@ Opened panel: ## Events -| Name | Type | Description | Inherited From | -| ----------- | ------------------- | -------------------------------------------------------------------------------- | ----------------------- | -| `change` | `CustomEvent` | Notifies that the component's value has changed. | | -| `didChange` | `CustomEvent` | Deprecated. used for React. Will probably be removed once React 19 is available. | | -| `didClose` | `CustomEvent` | Emits whenever the `sbb-select` is closed. | SbbOpenCloseBaseElement | -| `didOpen` | `CustomEvent` | Emits whenever the `sbb-select` is opened. | SbbOpenCloseBaseElement | -| `input` | `CustomEvent` | Notifies that an option value has been selected. | | -| `willClose` | `CustomEvent` | Emits whenever the `sbb-select` begins the closing transition. Can be canceled. | SbbOpenCloseBaseElement | -| `willOpen` | `CustomEvent` | Emits whenever the `sbb-select` starts the opening transition. Can be canceled. | SbbOpenCloseBaseElement | +| Name | Type | Description | Inherited From | +| ----------- | ------------------- | ------------------------------------------------------------------------------- | ----------------------- | +| `change` | `CustomEvent` | Notifies that the component's value has changed. | | +| `didClose` | `CustomEvent` | Emits whenever the `sbb-select` is closed. | SbbOpenCloseBaseElement | +| `didOpen` | `CustomEvent` | Emits whenever the `sbb-select` is opened. | SbbOpenCloseBaseElement | +| `input` | `CustomEvent` | Notifies that an option value has been selected. | | +| `willClose` | `CustomEvent` | Emits whenever the `sbb-select` begins the closing transition. Can be canceled. | SbbOpenCloseBaseElement | +| `willOpen` | `CustomEvent` | Emits whenever the `sbb-select` starts the opening transition. Can be canceled. | SbbOpenCloseBaseElement | ## CSS Properties diff --git a/src/elements/select/select.ts b/src/elements/select/select.ts index 3ce7b3932e..a2e5eade15 100644 --- a/src/elements/select/select.ts +++ b/src/elements/select/select.ts @@ -43,7 +43,6 @@ export interface SelectChange { * It displays a panel with selectable options. * * @slot - Use the unnamed slot to add options. - * @event {CustomEvent} didChange - Deprecated. used for React. Will probably be removed once React 19 is available. * @event {CustomEvent} change - Notifies that the component's value has changed. * @event {CustomEvent} input - Notifies that an option value has been selected. * @event {CustomEvent} willOpen - Emits whenever the `sbb-select` starts the opening transition. Can be canceled. @@ -77,7 +76,6 @@ class SbbSelectElement extends SbbUpdateSchedulerMixin( // FIXME using ...super.events requires: https://github.com/sbb-design-systems/lyne-components/issues/2600 public static override readonly events = { - didChange: 'didChange', change: 'change', input: 'input', stateChange: 'stateChange', @@ -113,11 +111,6 @@ class SbbSelectElement extends SbbUpdateSchedulerMixin( /** The value displayed by the component. */ @state() private accessor _displayValue: string | null = null; - /** - * @deprecated only used for React. Will probably be removed once React 19 is available. - */ - private _didChange: EventEmitter = new EventEmitter(this, SbbSelectElement.events.didChange); - /** Notifies that the component's value has changed. */ private _change: EventEmitter = new EventEmitter(this, SbbSelectElement.events.change); @@ -519,7 +512,6 @@ class SbbSelectElement extends SbbUpdateSchedulerMixin( this._input.emit(); this._change.emit(); - this._didChange.emit(); } /** When an option is unselected in `multiple`, removes it from value and updates displayValue. */ @@ -531,7 +523,6 @@ class SbbSelectElement extends SbbUpdateSchedulerMixin( this._input.emit(); this._change.emit(); - this._didChange.emit(); } } diff --git a/src/elements/time-input/time-input.spec.ts b/src/elements/time-input/time-input.spec.ts index 601c22321a..de59150ff5 100644 --- a/src/elements/time-input/time-input.spec.ts +++ b/src/elements/time-input/time-input.spec.ts @@ -39,13 +39,25 @@ describe(`sbb-time-input`, () => { it('should emit form events', async () => { const changeSpy = new EventSpy('change', element); const inputSpy = new EventSpy('input', element); + const nativeInputSpy = new EventSpy('input', input); + const nativeChangeSpy = new EventSpy('change', input); - typeInElement(input, '1'); + input.focus(); + await sendKeys({ press: '1' }); input.blur(); await waitForLitRender(element); - expect(changeSpy.count).to.be.greaterThan(0); - expect(inputSpy.count).to.be.greaterThan(0); + await nativeChangeSpy.calledOnce().then(() => { + expect(input.value).to.be.equal('01:00'); + }); + await changeSpy.calledOnce().then(() => { + expect(input.value).to.be.equal('01:00'); + }); + + expect(inputSpy.count, 'sbb-time-input input event').to.be.equal(2); + expect(changeSpy.count, 'sbb-time-input change event').to.be.equal(1); + expect(nativeInputSpy.count, 'input input event').to.be.equal(2); + expect(nativeChangeSpy.count, 'input change event').to.be.equal(1); }); it('should emit validation change event', async () => { diff --git a/src/elements/time-input/time-input.ts b/src/elements/time-input/time-input.ts index 3759b087c1..4ec0a839e0 100644 --- a/src/elements/time-input/time-input.ts +++ b/src/elements/time-input/time-input.ts @@ -144,18 +144,22 @@ class SbbTimeInputElement extends LitElement { ); this._inputElement.addEventListener( 'change', - (event: Event) => this._updateValueAndEmitChange(event), + (event: Event) => this._updateValue((event.target as HTMLInputElement).value), + { + signal: this._abortController.signal, + capture: true, + }, + ); + this._inputElement.addEventListener( + 'change', + (event: Event) => { + this._emitChange(event); + this._updateAccessibilityMessage(); + }, { signal: this._abortController.signal, }, ); - } - - /** Applies the correct format to values and triggers event dispatch. */ - private _updateValueAndEmitChange(event: Event): void { - this._updateValue((event.target as HTMLInputElement).value); - this._emitChange(event); - this._updateAccessibilityMessage(); } /** @@ -175,7 +179,12 @@ class SbbTimeInputElement extends LitElement { const isTimeValid = !!time && this._isTimeValid(time); const isEmptyOrValid = !value || value.trim() === '' || isTimeValid; if (isEmptyOrValid && time) { - this._inputElement.value = this._formatValue(time); + // In order to support React onChange event, we have to get the setter and call it. + // https://github.com/facebook/react/issues/11600#issuecomment-345813130 + const setValue = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')!.set!; + setValue.call(this._inputElement, this._formatValue(time)); + + this._inputElement.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true })); } const wasValid = !this._inputElement.hasAttribute('data-sbb-invalid'); diff --git a/src/elements/toggle-check/readme.md b/src/elements/toggle-check/readme.md index 734c4f79b1..60b307c4c2 100644 --- a/src/elements/toggle-check/readme.md +++ b/src/elements/toggle-check/readme.md @@ -79,11 +79,10 @@ you can not provide it and then use `aria-label` to specify an appropriate label ## Events -| Name | Type | Description | Inherited From | -| ----------- | ------------------- | -------------------------------------------------------------------------------- | -------------- | -| `change` | `Event` | Event fired on change. | | -| `didChange` | `CustomEvent` | Deprecated. used for React. Will probably be removed once React 19 is available. | | -| `input` | `InputEvent` | Event fired on input. | | +| Name | Type | Description | Inherited From | +| -------- | ------------ | ---------------------- | -------------- | +| `change` | `Event` | Event fired on change. | | +| `input` | `InputEvent` | Event fired on input. | | ## Slots diff --git a/src/elements/toggle-check/toggle-check.ts b/src/elements/toggle-check/toggle-check.ts index 85b0e81c12..67744a5208 100644 --- a/src/elements/toggle-check/toggle-check.ts +++ b/src/elements/toggle-check/toggle-check.ts @@ -13,7 +13,6 @@ import style from './toggle-check.scss?lit&inline'; * * @slot - Use the unnamed slot to add content to the toggle label. * @slot icon - Use this slot to provide an icon. If `icon-name` is set, a sbb-icon will be used. - * @event {CustomEvent} didChange - Deprecated. used for React. Will probably be removed once React 19 is available. * @event {Event} change - Event fired on change. * @event {InputEvent} input - Event fired on input. */ @@ -22,9 +21,6 @@ export @slotState() class SbbToggleCheckElement extends SbbFormAssociatedCheckboxMixin(SbbIconNameMixin(LitElement)) { public static override styles: CSSResultGroup = style; - public static readonly events = { - didChange: 'didChange', - } as const; /** Size variant, either m, s or xs. */ @property({ reflect: true }) public accessor size: 'xs' | 's' | 'm' = 's'; diff --git a/src/elements/toggle/toggle/readme.md b/src/elements/toggle/toggle/readme.md index 4b2b55e154..b2d57bf8ec 100644 --- a/src/elements/toggle/toggle/readme.md +++ b/src/elements/toggle/toggle/readme.md @@ -44,10 +44,9 @@ The component has two different sizes, `s` and `m` (default), which can be set u ## Events -| Name | Type | Description | Inherited From | -| ----------- | ------------------- | -------------------------------------------------------------------------------- | -------------- | -| `change` | `CustomEvent` | Emits whenever the toggle value changes. | | -| `didChange` | `CustomEvent` | Deprecated. used for React. Will probably be removed once React 19 is available. | | +| Name | Type | Description | Inherited From | +| -------- | ------------------- | ---------------------------------------- | -------------- | +| `change` | `CustomEvent` | Emits whenever the toggle value changes. | | ## Slots diff --git a/src/elements/toggle/toggle/toggle.ts b/src/elements/toggle/toggle/toggle.ts index a228c57640..61fd82a8d3 100644 --- a/src/elements/toggle/toggle/toggle.ts +++ b/src/elements/toggle/toggle/toggle.ts @@ -21,7 +21,6 @@ import style from './toggle.scss?lit&inline'; * It can be used as a container for two `sbb-toggle-option`, acting as a toggle button. * * @slot - Use the unnamed slot to add `` elements to the toggle. - * @event {CustomEvent} didChange - Deprecated. used for React. Will probably be removed once React 19 is available. * @event {CustomEvent} change - Emits whenever the toggle value changes. */ export @@ -32,7 +31,6 @@ export class SbbToggleElement extends LitElement { public static override styles: CSSResultGroup = style; public static readonly events = { - didChange: 'didChange', change: 'change', } as const; @@ -84,15 +82,6 @@ class SbbToggleElement extends LitElement { callback: () => this.updatePillPosition(true), }); - /** - * @deprecated only used for React. Will probably be removed once React 19 is available. - * Emits whenever the toggle value changes. - */ - private _didChange: EventEmitter = new EventEmitter(this, SbbToggleElement.events.didChange, { - bubbles: true, - composed: true, - }); - /** Emits whenever the toggle value changes. */ private _change: EventEmitter = new EventEmitter(this, SbbToggleElement.events.change, { bubbles: true, @@ -185,7 +174,6 @@ class SbbToggleElement extends LitElement { private _handleInput(): void { this.updatePillPosition(false); this._change.emit(); - this._didChange.emit(); } private _handleKeyDown(evt: KeyboardEvent): void { From dc129ae4874f2cb299e6b288aaf49251eeaad79e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 28 Nov 2024 08:18:03 +0100 Subject: [PATCH 6/7] chore(deps): update dependency vite to v6 (main) (#3248) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 164 ++++----------------------------------------------- 2 files changed, 11 insertions(+), 155 deletions(-) diff --git a/package.json b/package.json index 87a2c29b0d..2c89f58cea 100644 --- a/package.json +++ b/package.json @@ -144,7 +144,7 @@ "typescript": "5.7.2", "typescript-eslint": "8.16.0", "urlpattern-polyfill": "10.0.0", - "vite": "5.4.11", + "vite": "6.0.1", "vite-plugin-dts": "4.3.0" }, "resolutions": { diff --git a/yarn.lock b/yarn.lock index 50ab275158..f0b6386b42 100644 --- a/yarn.lock +++ b/yarn.lock @@ -272,181 +272,91 @@ resolved "https://registry.yarnpkg.com/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz#519c1549b0e147759e7825701ecffd25e5819f7b" integrity sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg== -"@esbuild/aix-ppc64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" - integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== - "@esbuild/aix-ppc64@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz#b57697945b50e99007b4c2521507dc613d4a648c" integrity sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw== -"@esbuild/android-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" - integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== - "@esbuild/android-arm64@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz#1add7e0af67acefd556e407f8497e81fddad79c0" integrity sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w== -"@esbuild/android-arm@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" - integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== - "@esbuild/android-arm@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.24.0.tgz#ab7263045fa8e090833a8e3c393b60d59a789810" integrity sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew== -"@esbuild/android-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" - integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== - "@esbuild/android-x64@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.24.0.tgz#e8f8b196cfdfdd5aeaebbdb0110983460440e705" integrity sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ== -"@esbuild/darwin-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" - integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== - "@esbuild/darwin-arm64@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz#2d0d9414f2acbffd2d86e98253914fca603a53dd" integrity sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw== -"@esbuild/darwin-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" - integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== - "@esbuild/darwin-x64@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz#33087aab31a1eb64c89daf3d2cf8ce1775656107" integrity sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA== -"@esbuild/freebsd-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" - integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== - "@esbuild/freebsd-arm64@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz#bb76e5ea9e97fa3c753472f19421075d3a33e8a7" integrity sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA== -"@esbuild/freebsd-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" - integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== - "@esbuild/freebsd-x64@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz#e0e2ce9249fdf6ee29e5dc3d420c7007fa579b93" integrity sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ== -"@esbuild/linux-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" - integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== - "@esbuild/linux-arm64@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz#d1b2aa58085f73ecf45533c07c82d81235388e75" integrity sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g== -"@esbuild/linux-arm@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" - integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== - "@esbuild/linux-arm@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz#8e4915df8ea3e12b690a057e77a47b1d5935ef6d" integrity sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw== -"@esbuild/linux-ia32@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" - integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== - "@esbuild/linux-ia32@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz#8200b1110666c39ab316572324b7af63d82013fb" integrity sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA== -"@esbuild/linux-loong64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" - integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== - "@esbuild/linux-loong64@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz#6ff0c99cf647504df321d0640f0d32e557da745c" integrity sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g== -"@esbuild/linux-mips64el@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" - integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== - "@esbuild/linux-mips64el@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz#3f720ccd4d59bfeb4c2ce276a46b77ad380fa1f3" integrity sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA== -"@esbuild/linux-ppc64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" - integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== - "@esbuild/linux-ppc64@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz#9d6b188b15c25afd2e213474bf5f31e42e3aa09e" integrity sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ== -"@esbuild/linux-riscv64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" - integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== - "@esbuild/linux-riscv64@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz#f989fdc9752dfda286c9cd87c46248e4dfecbc25" integrity sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw== -"@esbuild/linux-s390x@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" - integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== - "@esbuild/linux-s390x@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz#29ebf87e4132ea659c1489fce63cd8509d1c7319" integrity sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g== -"@esbuild/linux-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" - integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== - "@esbuild/linux-x64@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz#4af48c5c0479569b1f359ffbce22d15f261c0cef" integrity sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA== -"@esbuild/netbsd-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" - integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== - "@esbuild/netbsd-x64@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz#1ae73d23cc044a0ebd4f198334416fb26c31366c" @@ -457,51 +367,26 @@ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz#5d904a4f5158c89859fd902c427f96d6a9e632e2" integrity sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg== -"@esbuild/openbsd-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" - integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== - "@esbuild/openbsd-x64@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz#4c8aa88c49187c601bae2971e71c6dc5e0ad1cdf" integrity sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q== -"@esbuild/sunos-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" - integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== - "@esbuild/sunos-x64@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz#8ddc35a0ea38575fa44eda30a5ee01ae2fa54dd4" integrity sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA== -"@esbuild/win32-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" - integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== - "@esbuild/win32-arm64@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz#6e79c8543f282c4539db684a207ae0e174a9007b" integrity sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA== -"@esbuild/win32-ia32@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" - integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== - "@esbuild/win32-ia32@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz#057af345da256b7192d18b676a02e95d0fa39103" integrity sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw== -"@esbuild/win32-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" - integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== - "@esbuild/win32-x64@0.24.0": version "0.24.0" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz#168ab1c7e1c318b922637fad8f339d48b01e1244" @@ -4095,7 +3980,7 @@ esbuild-register@^3.5.0: dependencies: debug "^4.3.4" -esbuild@0.24.0, "esbuild@^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0": +esbuild@0.24.0, "esbuild@^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0", esbuild@^0.24.0: version "0.24.0" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.24.0.tgz#f2d470596885fcb2e91c21eb3da3b3c89c0b55e7" integrity sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ== @@ -4125,35 +4010,6 @@ esbuild@0.24.0, "esbuild@^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || "@esbuild/win32-ia32" "0.24.0" "@esbuild/win32-x64" "0.24.0" -esbuild@^0.21.3: - version "0.21.5" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" - integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== - optionalDependencies: - "@esbuild/aix-ppc64" "0.21.5" - "@esbuild/android-arm" "0.21.5" - "@esbuild/android-arm64" "0.21.5" - "@esbuild/android-x64" "0.21.5" - "@esbuild/darwin-arm64" "0.21.5" - "@esbuild/darwin-x64" "0.21.5" - "@esbuild/freebsd-arm64" "0.21.5" - "@esbuild/freebsd-x64" "0.21.5" - "@esbuild/linux-arm" "0.21.5" - "@esbuild/linux-arm64" "0.21.5" - "@esbuild/linux-ia32" "0.21.5" - "@esbuild/linux-loong64" "0.21.5" - "@esbuild/linux-mips64el" "0.21.5" - "@esbuild/linux-ppc64" "0.21.5" - "@esbuild/linux-riscv64" "0.21.5" - "@esbuild/linux-s390x" "0.21.5" - "@esbuild/linux-x64" "0.21.5" - "@esbuild/netbsd-x64" "0.21.5" - "@esbuild/openbsd-x64" "0.21.5" - "@esbuild/sunos-x64" "0.21.5" - "@esbuild/win32-arm64" "0.21.5" - "@esbuild/win32-ia32" "0.21.5" - "@esbuild/win32-x64" "0.21.5" - escalade@^3.1.1: version "3.2.0" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" @@ -7326,7 +7182,7 @@ postcss-values-parser@^6.0.2: is-url-superb "^4.0.0" quote-unquote "^1.0.0" -postcss@8.4.49, postcss@^8.4.40, postcss@^8.4.43, postcss@^8.4.47, postcss@^8.4.48: +postcss@8.4.49, postcss@^8.4.40, postcss@^8.4.47, postcss@^8.4.48, postcss@^8.4.49: version "8.4.49" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19" integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA== @@ -7733,7 +7589,7 @@ rollup-plugin-postcss-lit@2.1.0: "@rollup/pluginutils" "^5.0.2" transform-ast "^2.4.4" -rollup@^4.20.0, rollup@^4.4.0: +rollup@^4.23.0, rollup@^4.4.0: version "4.27.4" resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.27.4.tgz#b23e4ef4fe4d0d87f5237dacf63f95a499503897" integrity sha512-RLKxqHEMjh/RGLsDxAEsaLO3mWgyoU6x9w6n1ikAzet4B3gI2/3yP6PWY2p9QzRTh6MfEIXB3MwsOY0Iv3vNrw== @@ -8863,14 +8719,14 @@ vite-plugin-dts@4.3.0: local-pkg "^0.5.0" magic-string "^0.30.11" -vite@5.4.11: - version "5.4.11" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.11.tgz#3b415cd4aed781a356c1de5a9ebafb837715f6e5" - integrity sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q== +vite@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/vite/-/vite-6.0.1.tgz#24c9caf24998f0598de37bed67e50ec5b9dfeaf0" + integrity sha512-Ldn6gorLGr4mCdFnmeAOLweJxZ34HjKnDm4HGo6P66IEqTxQb36VEdFJQENKxWjupNfoIjvRUnswjn1hpYEpjQ== dependencies: - esbuild "^0.21.3" - postcss "^8.4.43" - rollup "^4.20.0" + esbuild "^0.24.0" + postcss "^8.4.49" + rollup "^4.23.0" optionalDependencies: fsevents "~2.3.3" From 0f0066d528439263304895f754b55fae85d9f32f Mon Sep 17 00:00:00 2001 From: Tommaso Menga Date: Thu, 28 Nov 2024 08:58:56 +0100 Subject: [PATCH 7/7] refactor(sbb-header): removed 'logo' slot (#3230) Closes #3093 BREAKING CHANGE: removed the `logo` slot and the default `sbb-logo` from the `sbb-header`. Alternatively, add the `sbb-header-logo` CSS class to the logo or to an ``-element containing the logo and use the default slot. To align the logo to the right, use a spacer element with the `sbb-header-spacer` CSS class applied. For more information, see `sbb-header` docs. --- .storybook/preview-head.html | 7 +- .../header.snapshot.spec.snap.js | 16 +--- src/elements/header/header/header.scss | 16 ++-- .../header/header/header.snapshot.spec.ts | 3 +- src/elements/header/header/header.stories.ts | 8 +- src/elements/header/header/header.ts | 10 +- .../header/header/header.visual.spec.ts | 15 ++- src/elements/header/header/readme.md | 93 +++++++++++++------ .../map-container/map-container.stories.ts | 3 + .../map-container.visual.spec.ts | 3 + .../pages/home/home--logged-in.stories.ts | 3 +- src/storybook/pages/home/home.stories.ts | 3 +- .../src/components/test-case/test-case.ts | 2 +- 13 files changed, 111 insertions(+), 71 deletions(-) diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html index 347ec33232..4d6747b90f 100644 --- a/.storybook/preview-head.html +++ b/.storybook/preview-head.html @@ -27,7 +27,8 @@ @@ -43,9 +43,14 @@ describe(`sbb-header`, () => { Christina Müller English + ${size === 's' - ? html`` - : html``} + ? html`` + : html``}
${loremIpsumTemplate} @@ -76,7 +81,7 @@ describe(`sbb-header`, () => { padding: '0', }); setup.withStateElement( - setup.snapshotElement.querySelector(`a[slot='logo']`)!, + setup.snapshotElement.querySelector('.sbb-header-logo')!, ); }), ); diff --git a/src/elements/header/header/readme.md b/src/elements/header/header/readme.md index 650ed05872..7b7ec42a71 100644 --- a/src/elements/header/header/readme.md +++ b/src/elements/header/header/readme.md @@ -2,12 +2,13 @@ The `sbb-header` component is a container for actions and a logo, and it is disp ## Slots -It has two slots: -the first one can contain one or more action ([sbb-header-button](/docs/elements-sbb-header-sbb-header-button--docs) or -[sbb-header-link](/docs/elements-sbb-header-sbb-header-link--docs)) -or other action items like [sbb-button](/docs/elements-sbb-button--docs) or [sbb-link](/docs/elements-sbb-link--docs), -and it is displayed at the left end of the component; the second slot is displayed at the right end, -and it can contain a logo or a signet, which by default is the [sbb-logo](/docs/elements-sbb-logo--docs). +The slot can contain: + +- one or more actions ([sbb-header-button](/docs/elements-sbb-header-sbb-header-button--docs) or [sbb-header-link](/docs/elements-sbb-header-sbb-header-link--docs)) +- other action items like [sbb-button](/docs/elements-sbb-button--docs) or [sbb-link](/docs/elements-sbb-link--docs) +- a logo or a signet with the `.sbb-header-logo` class (see [sbb-logo](/docs/elements-sbb-logo--docs)) + +Slotted elements are aligned to the left. Use `
` to align elements after it to the right. ```html @@ -15,7 +16,8 @@ and it can contain a logo or a signet, which by default is the [sbb-logo](/docs/ Menu Search - +
+
@@ -38,8 +40,9 @@ For the latter, the usage of the `sbb-signet` with `protective-room='panel'` is Menu Search - - +
+
``` @@ -61,8 +64,9 @@ From accessibility perspective `aria-current="page"` should be set whenever the > Overview - - +
+
``` @@ -79,7 +83,8 @@ using the `scrollOrigin` property, which accepts an `HTMLElement` or the id of t ```html Search - +
+
@@ -92,18 +97,26 @@ by adding classes to `sbb-header-button`/`sbb-header-link` elements and then def [All the examples in Storybook](/story/elements-sbb-header-sbb-header--basic) have the following requirements: -- four action items (with custom icons); -- the first item is always left aligned and has `expand-from` set to `small`; -- the other three items are left aligned in breakpoints zero to medium, and right aligned from large to ultra; -- the last item is not visible in breakpoints zero to small. +1. four action items (with custom icons); +2. the first item is always left aligned and has `expand-from` set to `small`; +3. the other three items are left aligned in breakpoints zero to medium, and right aligned from large to ultra; +4. the last item is not visible in breakpoints zero to small; +5. the logo is always aligned to the right. + +To achieve the alignment requirements, two `div` tags with a CSS class named `sbb-header-spacer` were added: + +- one after the first `sbb-header-button` item (that will be hidden on smaller screen sizes); +- the second, before the logo. Since this spacer will only be shown on small screen sizes, we need a new class to target it (in this example `sbb-header-spacer-logo`); + +We also need a class (in this example `last-element`) on the last `sbb-header-button` to achieve requirement n° 4. -To achieve this result, a `div` tag with a CSS class named `sbb-header-spacer` was added between the first -and the second `sbb-header-button` item, then a class named `last-element` was added to the last one. Finally, the following custom CSS has been added(\*). -The result can be seen in the [home](/story/pages-home--home) and [home-logged-in](/story/pages-home--home-logged-in) stories. + +The result can also be seen in the [home](/story/pages-home--home) and [home-logged-in](/story/pages-home--home-logged-in) stories. ```css -.last-element { +.last-element, +.sbb-header-spacer-logo { display: none; } @@ -117,9 +130,33 @@ The result can be seen in the [home](/story/pages-home--home) and [home-logged-i .sbb-header-spacer { display: none; } + + .sbb-header-spacer-logo { + display: block; + } } ``` +```html + + ... + + +
+ + ... + ... + ... + + + + + +
+``` + The `sbb-header` can be also customized by adding the application's name and version: a helper class named `sbb-header-info` is provided to achieve the correct visual result. @@ -134,8 +171,10 @@ a helper class named `sbb-header-info` is provided to achieve the correct visual V. 1.1 - - +
+ +
``` @@ -153,7 +192,8 @@ set the CSS class `sbb-header-shrinkable` on the desired `sbb-header-button`/`sb Christina Müller has a long name - +
+
@@ -183,7 +223,6 @@ so they were wrapped into a `style` tag and added to the Storybook's configurati ## Slots -| Name | Description | -| ------ | --------------------------------------------------------------------- | -| | Use the unnamed slot to add actions or content to the header. | -| `logo` | Slot used to render the logo on the right side (sbb-logo as default). | +| Name | Description | +| ---- | -------------------------------------------------------------------- | +| | Use the unnamed slot to add actions, content and logo to the header. | diff --git a/src/elements/map-container/map-container.stories.ts b/src/elements/map-container/map-container.stories.ts index 5342657986..f987015355 100644 --- a/src/elements/map-container/map-container.stories.ts +++ b/src/elements/map-container/map-container.stories.ts @@ -12,6 +12,7 @@ import '../form-field.js'; import '../icon.js'; import '../title.js'; import '../header.js'; +import '../logo.js'; const hideScrollUpButton: InputType = { control: { @@ -86,6 +87,8 @@ const meta: Meta = { Menu +
+ ${story()} `, diff --git a/src/elements/map-container/map-container.visual.spec.ts b/src/elements/map-container/map-container.visual.spec.ts index 970e48e8f5..a31975606d 100644 --- a/src/elements/map-container/map-container.visual.spec.ts +++ b/src/elements/map-container/map-container.visual.spec.ts @@ -6,6 +6,7 @@ import { describeViewports, visualDiffDefault } from '../core/testing/private.js import './map-container.js'; import '../header.js'; import '../title.js'; +import '../logo.js'; describe(`sbb-map-container`, () => { const template = (stickyOffset = false): TemplateResult => html` @@ -13,6 +14,8 @@ describe(`sbb-map-container`, () => { Menu +
+ html` Italiano English - + + diff --git a/src/storybook/pages/home/home.stories.ts b/src/storybook/pages/home/home.stories.ts index 0ff03467ad..99304aba45 100644 --- a/src/storybook/pages/home/home.stories.ts +++ b/src/storybook/pages/home/home.stories.ts @@ -55,7 +55,8 @@ const Template = (args: Args): TemplateResult => html` Italiano English - + + diff --git a/src/visual-regression-app/src/components/test-case/test-case.ts b/src/visual-regression-app/src/components/test-case/test-case.ts index c8d45f37d6..1cbf27f59c 100644 --- a/src/visual-regression-app/src/components/test-case/test-case.ts +++ b/src/visual-regression-app/src/components/test-case/test-case.ts @@ -110,7 +110,7 @@ class TestCase extends LitElement {
-
+