From 77f76febbc00df91a3d27f43845f2cfadd9234ac Mon Sep 17 00:00:00 2001 From: Daniel Leroux Date: Fri, 6 Dec 2024 11:52:17 +0100 Subject: [PATCH] fix(core/select): replace element ref with make-ref function (#1595) --- .changeset/lazy-birds-dream.md | 5 ++ .../text-value-accessor.ts | 2 +- packages/core/src/components.d.ts | 16 +++- .../src/components/date-input/date-input.tsx | 10 +-- .../components/field-label/field-label.tsx | 11 ++- .../field-wrapper/field-wrapper.tsx | 5 +- .../core/src/components/input/input.fc.tsx | 10 +-- packages/core/src/components/input/input.tsx | 4 +- .../src/components/input/number-input.tsx | 4 +- .../core/src/components/input/textarea.tsx | 4 +- .../core/src/components/select/select.tsx | 74 ++++++++++--------- 11 files changed, 81 insertions(+), 64 deletions(-) create mode 100644 .changeset/lazy-birds-dream.md diff --git a/.changeset/lazy-birds-dream.md b/.changeset/lazy-birds-dream.md new file mode 100644 index 0000000000..9b6887b18c --- /dev/null +++ b/.changeset/lazy-birds-dream.md @@ -0,0 +1,5 @@ +--- +'@siemens/ix': patch +--- + +Fix undefined access of **ix-select** during rendering of label diff --git a/packages/angular/src/control-value-accessors/text-value-accessor.ts b/packages/angular/src/control-value-accessors/text-value-accessor.ts index 02f24376d1..a403da590a 100644 --- a/packages/angular/src/control-value-accessors/text-value-accessor.ts +++ b/packages/angular/src/control-value-accessors/text-value-accessor.ts @@ -11,7 +11,7 @@ import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { ValueAccessor } from './value-accessor'; @Directive({ - selector: 'ix-input,ix-number-input,ix-textarea-field', + selector: 'ix-input,ix-number-input,ix-textarea', providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/packages/core/src/components.d.ts b/packages/core/src/components.d.ts index d7d1d4d9e2..6285dce4be 100644 --- a/packages/core/src/components.d.ts +++ b/packages/core/src/components.d.ts @@ -1236,7 +1236,9 @@ export namespace Components { "value": string; } interface IxFieldLabel { - "controlRef"?: MakeRef; + "controlRef"?: | MakeRef + | MakeRef + | MakeRef; /** * The id of the form element that the label is associated with */ @@ -1251,7 +1253,9 @@ export namespace Components { /** * The control element that the label is associated with */ - "controlRef"?: MakeRef; + "controlRef"?: | MakeRef + | MakeRef + | MakeRef; /** * Show text below the field component */ @@ -6340,7 +6344,9 @@ declare namespace LocalJSX { "value"?: string; } interface IxFieldLabel { - "controlRef"?: MakeRef; + "controlRef"?: | MakeRef + | MakeRef + | MakeRef; /** * The id of the form element that the label is associated with */ @@ -6355,7 +6361,9 @@ declare namespace LocalJSX { /** * The control element that the label is associated with */ - "controlRef"?: MakeRef; + "controlRef"?: | MakeRef + | MakeRef + | MakeRef; /** * Show text below the field component */ diff --git a/packages/core/src/components/date-input/date-input.tsx b/packages/core/src/components/date-input/date-input.tsx index 92de60cfc3..652c6e212d 100644 --- a/packages/core/src/components/date-input/date-input.tsx +++ b/packages/core/src/components/date-input/date-input.tsx @@ -7,6 +7,7 @@ * LICENSE file in the root directory of this source tree. */ +import { iconCalendar } from '@siemens/ix-icons/icons'; import { AttachInternals, Component, @@ -22,7 +23,8 @@ import { } from '@stencil/core'; import { DateTime } from 'luxon'; import { dropdownController } from '../dropdown/dropdown-controller'; -import { MakeRef, makeRef } from '../utils/make-ref'; +import { SlotEnd, SlotStart } from '../input/input.fc'; +import { adjustPaddingForStartAndEnd } from '../input/input.util'; import { ClassMutationObserver, HookValidationLifecycle, @@ -30,9 +32,7 @@ import { ValidationResults, createClassMutationObserver, } from '../utils/input'; -import { SlotEnd, SlotStart } from '../input/input.fc'; -import { adjustPaddingForStartAndEnd } from '../input/input.util'; -import { iconCalendar } from '@siemens/ix-icons/icons'; +import { makeRef } from '../utils/make-ref'; export type DateInputValidityState = { patternMismatch: boolean; @@ -435,7 +435,7 @@ export class DateInput implements IxInputFieldComponent { validText={this.validText} showTextAsTooltip={this.showTextAsTooltip} required={this.required} - controlRef={this.inputElementRef as unknown as MakeRef} + controlRef={this.inputElementRef} > {this.renderInput()} diff --git a/packages/core/src/components/field-label/field-label.tsx b/packages/core/src/components/field-label/field-label.tsx index e8480725b4..14b51cbffd 100644 --- a/packages/core/src/components/field-label/field-label.tsx +++ b/packages/core/src/components/field-label/field-label.tsx @@ -7,15 +7,15 @@ * LICENSE file in the root directory of this source tree. */ -import { Component, Host, Prop, h, Element, Watch } from '@stencil/core'; -import { IxComponent } from '../utils/internal'; +import { Component, Element, h, Host, Prop, Watch } from '@stencil/core'; +import { A11yAttributes, a11yHostAttributes } from '../utils/a11y'; import { ClassMutationObserver, createClassMutationObserver, HTMLIxFormComponentElement, isIxInputFieldComponent, } from '../utils/input'; -import { A11yAttributes, a11yHostAttributes } from '../utils/a11y'; +import { IxComponent } from '../utils/internal'; import { MakeRef, makeRef } from '../utils/make-ref'; @Component({ @@ -37,7 +37,10 @@ export class FormFieldLabel implements IxComponent { @Prop({ reflect: true }) htmlFor?: string; /** @internal */ - @Prop() controlRef?: MakeRef; + @Prop() controlRef?: + | MakeRef + | MakeRef + | MakeRef; /** @internal */ @Prop({ mutable: true }) isInvalid: boolean = false; diff --git a/packages/core/src/components/field-wrapper/field-wrapper.tsx b/packages/core/src/components/field-wrapper/field-wrapper.tsx index dca0bec7c6..e1cae44f0b 100644 --- a/packages/core/src/components/field-wrapper/field-wrapper.tsx +++ b/packages/core/src/components/field-wrapper/field-wrapper.tsx @@ -88,7 +88,10 @@ export class FieldWrapper implements FieldWrapperInterface { /** * The control element that the label is associated with */ - @Prop() controlRef?: MakeRef; + @Prop() controlRef?: + | MakeRef + | MakeRef + | MakeRef; private readonly slotRef = makeRef(); diff --git a/packages/core/src/components/input/input.fc.tsx b/packages/core/src/components/input/input.fc.tsx index 3b79e2e57a..0ebc44d44e 100644 --- a/packages/core/src/components/input/input.fc.tsx +++ b/packages/core/src/components/input/input.fc.tsx @@ -45,13 +45,10 @@ export function TextareaElement(props: { required={props.required} value={props.value} placeholder={props.placeholder} - onChange={(changeEvent) => { - const target = changeEvent.target as HTMLInputElement; - props.valueChange(target.value); - }} onInput={(inputEvent) => { const target = inputEvent.target as HTMLInputElement; props.updateFormInternalValue(target.value); + props.valueChange(target.value); }} onBlur={() => props.onBlur()} style={{ @@ -105,13 +102,10 @@ export function InputElement(props: { value={props.value} placeholder={props.placeholder} onKeyPress={(event) => props.onKeyPress(event)} - onChange={(changeEvent) => { - const target = changeEvent.target as HTMLInputElement; - props.valueChange(target.value); - }} onInput={(inputEvent) => { const target = inputEvent.target as HTMLInputElement; props.updateFormInternalValue(target.value); + props.valueChange(target.value); }} onBlur={() => props.onBlur()} {...props.ariaAttributes} diff --git a/packages/core/src/components/input/input.tsx b/packages/core/src/components/input/input.tsx index ad79a0b50a..bf83c7c5e9 100644 --- a/packages/core/src/components/input/input.tsx +++ b/packages/core/src/components/input/input.tsx @@ -27,7 +27,7 @@ import { IxInputFieldComponent, ValidationResults, } from '../utils/input'; -import { MakeRef, makeRef } from '../utils/make-ref'; +import { makeRef } from '../utils/make-ref'; import { InputElement, SlotEnd, SlotStart } from './input.fc'; import { adjustPaddingForStartAndEnd, @@ -255,7 +255,7 @@ export class Input implements IxInputFieldComponent { isValid={this.isValid} isInfo={this.isInfo} isWarning={this.isWarning} - controlRef={this.inputRef as unknown as MakeRef} + controlRef={this.inputRef} >
{ isValid={this.isValid} isInfo={this.isInfo} isWarning={this.isWarning} - controlRef={this.inputRef as unknown as MakeRef} + controlRef={this.inputRef} >
{ isValid={this.isValid} isInfo={this.isInfo} isWarning={this.isWarning} - controlRef={this.textAreaRef as unknown as MakeRef} + controlRef={this.textAreaRef} > {!!this.maxLength && this.maxLength > 0 && ( diff --git a/packages/core/src/components/select/select.tsx b/packages/core/src/components/select/select.tsx index 13983ee2ce..e567eb551d 100644 --- a/packages/core/src/components/select/select.tsx +++ b/packages/core/src/components/select/select.tsx @@ -21,18 +21,18 @@ import { State, Watch, } from '@stencil/core'; +import { DropdownItemWrapper } from '../dropdown/dropdown-controller'; import { IxSelectItemLabelChangeEvent } from '../select-item/events'; +import { a11yBoolean } from '../utils/a11y'; import { ArrowFocusController } from '../utils/focus'; -import { OnListener } from '../utils/listener'; -import { createMutationObserver } from '../utils/mutation-observer'; -import { DropdownItemWrapper } from '../dropdown/dropdown-controller'; import { HookValidationLifecycle, - ValidationResults, IxInputFieldComponent, + ValidationResults, } from '../utils/input'; -import { MakeRef, makeRef } from '../utils/make-ref'; -import { a11yBoolean } from '../utils/a11y'; +import { OnListener } from '../utils/listener'; +import { makeRef } from '../utils/make-ref'; +import { createMutationObserver } from '../utils/mutation-observer'; /** * @form-ready 2.6.0 @@ -221,12 +221,13 @@ export class Select implements IxInputFieldComponent { @State() isWarning = false; private readonly dropdownWrapperRef = makeRef(); - private readonly dropdownAnchor = makeRef(); + private readonly dropdownAnchorRef = makeRef(); + private readonly inputRef = makeRef(); - private inputRef?: HTMLInputElement; - private dropdownRef?: HTMLIxDropdownElement; - private customItemsContainerRef?: HTMLDivElement; - private addItemRef?: HTMLIxDropdownItemElement; + private inputElement?: HTMLInputElement; + private dropdownElement?: HTMLIxDropdownElement; + private customItemsContainerElement?: HTMLDivElement; + private addItemElement?: HTMLIxDropdownItemElement; private arrowFocusController?: ArrowFocusController; private readonly itemObserver = createMutationObserver(() => { @@ -302,16 +303,16 @@ export class Select implements IxInputFieldComponent { @Watch('dropdownShow') watchDropdownShow(show: boolean) { - if (show && this.dropdownRef) { + if (show && this.dropdownElement) { this.arrowFocusController = new ArrowFocusController( this.visibleNonShadowItems, - this.dropdownRef, + this.dropdownElement, this.focusControllerCallbackBind ); this.arrowFocusController.wrap = !this.editable; - this.itemObserver.observe(this.dropdownRef, { + this.itemObserver.observe(this.dropdownElement, { childList: true, subtree: true, }); @@ -402,7 +403,7 @@ export class Select implements IxInputFieldComponent { newItem.value = value; newItem.label = value; - this.customItemsContainerRef?.appendChild(newItem); + this.customItemsContainerElement?.appendChild(newItem); this.clearInput(); this.itemClick(value); @@ -457,7 +458,7 @@ export class Select implements IxInputFieldComponent { this.inputValue = ''; } - this.inputRef && (this.inputRef.value = this.inputValue); + this.inputElement && (this.inputElement.value = this.inputValue); } private emitValueChange(value: string | string[]) { @@ -478,9 +479,9 @@ export class Select implements IxInputFieldComponent { } componentDidLoad() { - this.inputRef?.addEventListener('input', () => { + this.inputElement?.addEventListener('input', () => { this.dropdownShow = true; - this.inputChange.emit(this.inputRef?.value); + this.inputChange.emit(this.inputElement?.value); }); } @@ -513,8 +514,8 @@ export class Select implements IxInputFieldComponent { this.dropdownShow = event.detail; if (event.detail) { - this.inputRef?.focus(); - this.inputRef?.select(); + this.inputElement?.focus(); + this.inputElement?.select(); this.removeHiddenFromItems(); this.isDropdownEmpty = this.isEveryDropdownItemHidden; @@ -624,7 +625,7 @@ export class Select implements IxInputFieldComponent { if ( this.isAddItemVisible() && - this.addItemRef?.contains( + this.addItemElement?.contains( await this.navigationItem.getDropdownItemElement() ) ) { @@ -682,12 +683,12 @@ export class Select implements IxInputFieldComponent { private focusAddItemButton() { if (this.addItemButton) { this.addItemButton.shadowRoot?.querySelector('button')?.focus(); - this.navigationItem = this.addItemRef; + this.navigationItem = this.addItemElement; } } private filterItemsWithTypeahead() { - this.inputFilterText = this.inputRef?.value ?? ''; + this.inputFilterText = this.inputElement?.value ?? ''; if (this.isSingleMode && this.inputFilterText === this.selectedLabels[0]) { return; @@ -722,8 +723,8 @@ export class Select implements IxInputFieldComponent { } private clearInput() { - if (this.inputRef) { - this.inputRef.value = ''; + if (this.inputElement) { + this.inputElement.value = ''; } this.inputFilterText = ''; } @@ -803,8 +804,8 @@ export class Select implements IxInputFieldComponent { */ @Method() getNativeInputElement(): Promise { - if (this.inputRef) { - return Promise.resolve(this.inputRef); + if (this.inputElement) { + return Promise.resolve(this.inputElement); } else { return Promise.reject(new Error('Input element not found')); } @@ -842,7 +843,7 @@ export class Select implements IxInputFieldComponent { isValid={this.isValid} isInfo={this.isInfo} isWarning={this.isWarning} - controlRef={this.inputRef as unknown as MakeRef} + controlRef={this.inputRef} >
{ readonly: this.readonly, }} ref={(ref) => { - this.dropdownAnchor(ref); + this.dropdownAnchorRef(ref); if (!this.editable) this.dropdownWrapperRef(ref); }} > @@ -886,7 +887,10 @@ export class Select implements IxInputFieldComponent { }} placeholder={this.placeholderValue()} value={this.inputValue ?? ''} - ref={(ref) => (this.inputRef = ref)} + ref={(ref) => { + this.inputElement = ref; + this.inputRef(ref); + }} onBlur={(e) => this.onInputBlur(e)} onFocus={() => { this.navigationItem = undefined; @@ -928,13 +932,13 @@ export class Select implements IxInputFieldComponent {
(this.dropdownRef = ref!)} + ref={(ref) => (this.dropdownElement = ref!)} show={this.dropdownShow} closeBehavior={this.isMultipleMode ? 'outside' : 'both'} class={{ 'd-none': this.disabled || this.readonly, }} - anchor={this.dropdownAnchor.waitForCurrent()} + anchor={this.dropdownAnchorRef.waitForCurrent()} trigger={this.dropdownWrapperRef.waitForCurrent()} onShowChanged={(e) => this.dropdownVisibilityChanged(e)} placement="bottom-start" @@ -964,7 +968,7 @@ export class Select implements IxInputFieldComponent { }} >
(this.customItemsContainerRef = ref!)} + ref={(ref) => (this.customItemsContainerElement = ref!)} class="d-contents" >
{this.isAddItemVisible() ? ( @@ -980,9 +984,9 @@ export class Select implements IxInputFieldComponent { e.stopPropagation(); this.emitAddItem(this.inputFilterText); }} - onFocus={() => (this.navigationItem = this.addItemRef)} + onFocus={() => (this.navigationItem = this.addItemElement)} ref={(ref) => { - this.addItemRef = ref!; + this.addItemElement = ref!; }} > ) : null}