From def6fe415a7d26888c9ff34a0a7c5e306dc4122e Mon Sep 17 00:00:00 2001 From: Jeremias Peier Date: Wed, 27 Nov 2024 10:44:21 +0100 Subject: [PATCH 1/3] fix: remove deprecated `didChange` events where possible 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.ts | 11 ----------- src/elements/datepicker/datepicker/readme.md | 1 - .../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 | 7 ++++++- 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 ------------ 17 files changed, 64 insertions(+), 81 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.ts b/src/elements/datepicker/datepicker/datepicker.ts index 3db9792ed2..56e2ab93b7 100644 --- a/src/elements/datepicker/datepicker/datepicker.ts +++ b/src/elements/datepicker/datepicker/datepicker.ts @@ -56,7 +56,6 @@ 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} 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. @@ -67,7 +66,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 +109,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, @@ -269,7 +259,6 @@ class SbbDatepickerElement extends LitElement { this._validateDate(); this._setAriaLiveMessage(); this._change.emit(); - this._didChange.emit(); } private _tryApplyFormatToInput(): boolean { diff --git a/src/elements/datepicker/datepicker/readme.md b/src/elements/datepicker/datepicker/readme.md index ed9eccb436..e181918f61 100644 --- a/src/elements/datepicker/datepicker/readme.md +++ b/src/elements/datepicker/datepicker/readme.md @@ -93,6 +93,5 @@ 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. | | | `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..d5f0c2c864 100644 --- a/src/elements/time-input/time-input.ts +++ b/src/elements/time-input/time-input.ts @@ -175,7 +175,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 0e78beadc8e7fb16657e8baafb83bfe872f42a39 Mon Sep 17 00:00:00 2001 From: Jeremias Peier Date: Wed, 27 Nov 2024 12:21:56 +0100 Subject: [PATCH 2/3] fix: fix datepicker --- .../datepicker/datepicker/datepicker.spec.ts | 4 ++++ .../datepicker/datepicker/datepicker.ts | 18 +++++++++++++++--- src/elements/datepicker/datepicker/readme.md | 1 + 3 files changed, 20 insertions(+), 3 deletions(-) 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 56e2ab93b7..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'; @@ -57,6 +57,7 @@ export const datepickerControlRegisteredEventFactory = (): CustomEvent => * Combined with a native input, it displays the input's value as a formatted date. * * @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. @@ -239,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(); @@ -268,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 e181918f61..c20f66281a 100644 --- a/src/elements/datepicker/datepicker/readme.md +++ b/src/elements/datepicker/datepicker/readme.md @@ -93,5 +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. | | +| `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. | | From dc894e3a18f1a3d5966e6dafffe9045f477ee2d3 Mon Sep 17 00:00:00 2001 From: Jeremias Peier Date: Wed, 27 Nov 2024 16:50:19 +0100 Subject: [PATCH 3/3] fix: review --- src/elements/time-input/time-input.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/elements/time-input/time-input.ts b/src/elements/time-input/time-input.ts index d5f0c2c864..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(); } /**