From b959191df69011e609dd13cab537b22f3f3d4855 Mon Sep 17 00:00:00 2001 From: Jeremias Peier Date: Thu, 5 Dec 2024 13:55:00 +0100 Subject: [PATCH] fix(sbb-form-field): update floating label on programmatic changes --- .../form-field/form-field/form-field.spec.ts | 9 +--- .../form-field/form-field/form-field.ts | 46 ++++++++++++++++++- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/elements/form-field/form-field/form-field.spec.ts b/src/elements/form-field/form-field/form-field.spec.ts index 50eb9817d2..340a504b2a 100644 --- a/src/elements/form-field/form-field/form-field.spec.ts +++ b/src/elements/form-field/form-field/form-field.spec.ts @@ -433,14 +433,7 @@ describe(`sbb-form-field`, () => { input.value = ''; await waitForLitRender(element); - // Then empty state is not updated - expect(element).not.to.have.attribute('data-input-empty'); - - // When manually calling reset method - element.reset(); - await waitForLitRender(element); - - // Then empty state should be updated + // Then the empty state is updated expect(element).to.have.attribute('data-input-empty'); }); }); diff --git a/src/elements/form-field/form-field/form-field.ts b/src/elements/form-field/form-field/form-field.ts index 19684f5bc6..37ac298bf3 100644 --- a/src/elements/form-field/form-field/form-field.ts +++ b/src/elements/form-field/form-field/form-field.ts @@ -20,6 +20,44 @@ let nextFormFieldErrorId = 0; const supportedPopupTagNames = ['sbb-autocomplete', 'sbb-autocomplete-grid', 'sbb-select']; +interface PatchedInputElement extends HTMLInputElement { + // eslint-disable-next-line @typescript-eslint/naming-convention + _originalValueDescriptor?: PropertyDescriptor; +} + +function patchInputValue(inputElement: PatchedInputElement, callback: (value: any) => void): void { + // Speichern der ursprünglichen Getter und Setter + const originalDescriptor = Object.getOwnPropertyDescriptor( + Object.getPrototypeOf(inputElement), + 'value', + ); + const originalGetter = originalDescriptor!.get!; + const originalSetter = originalDescriptor!.set!; + + inputElement._originalValueDescriptor = originalDescriptor; + + // Neue Getter und Setter definieren + Object.defineProperty(inputElement, 'value', { + get() { + return originalGetter.call(this); + }, + set(newValue) { + originalSetter.call(this, newValue); + callback(newValue); + }, + configurable: true, + enumerable: true, + }); +} + +function unpatchInputValue(inputElement: PatchedInputElement): void { + const originalDescriptor = inputElement._originalValueDescriptor; + if (originalDescriptor) { + Object.defineProperty(inputElement, 'value', originalDescriptor); + delete inputElement._originalValueDescriptor; + } +} + /** * It wraps an input element adding label, errors, icon, etc. * @@ -155,6 +193,9 @@ class SbbFormFieldElement extends SbbNegativeMixin(SbbHydrationMixin(LitElement) super.disconnectedCallback(); this._formFieldAttributeObserver?.disconnect(); this._inputAbortController.abort(); + if (this._input?.localName === 'input') { + unpatchInputValue(this._input as HTMLInputElement); + } } private _onPopupOpen({ target }: CustomEvent): void { @@ -285,7 +326,10 @@ class SbbFormFieldElement extends SbbNegativeMixin(SbbHydrationMixin(LitElement) let inputFocusElement = this._input; - if (this._input.localName === 'sbb-select') { + if (this._input.localName === 'input') { + // Patch value of HTMLInputElement in order to update floating label + patchInputValue(this._input as HTMLInputElement, () => this.reset()); + } else if (this._input.localName === 'sbb-select') { this._input.addEventListener('stateChange', () => this._checkAndUpdateInputEmpty(), { signal: this._inputAbortController.signal, });