diff --git a/.changeset/lovely-garlics-pretend.md b/.changeset/lovely-garlics-pretend.md new file mode 100644 index 00000000000..c84be6575af --- /dev/null +++ b/.changeset/lovely-garlics-pretend.md @@ -0,0 +1,5 @@ +--- +'@siemens/ix': patch +--- + +fix(core/select): check if value is defined, before updating selection diff --git a/packages/core/component-doc.json b/packages/core/component-doc.json index eb3cbe2cf3b..15c74bf5562 100644 --- a/packages/core/component-doc.json +++ b/packages/core/component-doc.json @@ -13413,6 +13413,11 @@ "capture": false, "passive": false }, + { + "event": "ix-select-item:valueChange", + "capture": false, + "passive": false + }, { "event": "ix-select-item:labelChange", "capture": false, @@ -13469,7 +13474,7 @@ "type": "string" } ], - "optional": false, + "optional": true, "required": false }, { diff --git a/packages/core/src/components.d.ts b/packages/core/src/components.d.ts index b3d310448ac..00edf5ba082 100644 --- a/packages/core/src/components.d.ts +++ b/packages/core/src/components.d.ts @@ -1923,7 +1923,7 @@ export namespace Components { /** * Displayed name of the item */ - "label": string; + "label"?: string; /** * @param event */ diff --git a/packages/core/src/components/dropdown/dropdown-controller.ts b/packages/core/src/components/dropdown/dropdown-controller.ts index e36da76313e..d0382e83b5b 100644 --- a/packages/core/src/components/dropdown/dropdown-controller.ts +++ b/packages/core/src/components/dropdown/dropdown-controller.ts @@ -32,7 +32,7 @@ export function hasDropdownItemWrapperImplemented( item: unknown ): item is DropdownItemWrapper { return ( - item && + item !== null && (item as DropdownItemWrapper).getDropdownItemElement !== undefined && typeof (item as DropdownItemWrapper).getDropdownItemElement === 'function' ); @@ -91,7 +91,7 @@ class DropdownController { } present(dropdown: DropdownInterface) { - if (!dropdown.isPresent() && dropdown.willPresent()) { + if (!dropdown.isPresent() && dropdown.willPresent?.()) { this.submenuIds[dropdown.getId()] = dropdown.getAssignedSubmenuIds(); dropdown.present(); } @@ -100,12 +100,15 @@ class DropdownController { dismissChildren(uid: string) { const childIds = this.submenuIds[uid] || []; for (const id of childIds) { - this.dismiss(this.dropdowns.get(id)); + const dropdown = this.dropdowns.get(id); + if (dropdown) { + this.dismiss(dropdown); + } } } dismiss(dropdown: DropdownInterface) { - if (dropdown.isPresent() && dropdown.willDismiss()) { + if (dropdown.isPresent() && dropdown.willDismiss?.()) { this.dismissChildren(dropdown.getId()); dropdown.dismiss(); delete this.submenuIds[dropdown.getId()]; @@ -175,7 +178,8 @@ class DropdownController { private pathIncludesDropdown(eventTargets: EventTarget[]) { return !!eventTargets.find( - (element: HTMLElement) => element.tagName === 'IX-DROPDOWN' + (element: EventTarget) => + (element as HTMLElement).tagName === 'IX-DROPDOWN' ); } @@ -196,7 +200,7 @@ class DropdownController { private addOverlayListeners() { this.isWindowListenerActive = true; - window.addEventListener('click', (event: PointerEvent) => { + window.addEventListener('click', (event: MouseEvent) => { const hasTrigger = this.pathIncludesTrigger(event.composedPath()); const hasDropdown = this.pathIncludesDropdown(event.composedPath()); diff --git a/packages/core/src/components/select-item/events.ts b/packages/core/src/components/select-item/events.ts index b42d897c10e..de58c65bdea 100644 --- a/packages/core/src/components/select-item/events.ts +++ b/packages/core/src/components/select-item/events.ts @@ -17,3 +17,15 @@ export class IxSelectItemLabelChangeEvent extends CustomEvent<{ }); } } + +export class IxSelectItemValueChangeEvent extends CustomEvent<{ + oldValue: string; + newValue: string; +}> { + constructor(detail: { oldValue: string; newValue: string }) { + super('ix-select-item:valueChange', { + bubbles: true, + detail, + }); + } +} diff --git a/packages/core/src/components/select-item/select-item.tsx b/packages/core/src/components/select-item/select-item.tsx index 53369d41a86..780c6db6029 100644 --- a/packages/core/src/components/select-item/select-item.tsx +++ b/packages/core/src/components/select-item/select-item.tsx @@ -18,7 +18,10 @@ import { Prop, Watch, } from '@stencil/core'; -import { IxSelectItemLabelChangeEvent } from './events'; +import { + IxSelectItemLabelChangeEvent, + IxSelectItemValueChangeEvent, +} from './events'; import { DropdownItemWrapper } from '../dropdown/dropdown-controller'; @Component({ @@ -27,12 +30,12 @@ import { DropdownItemWrapper } from '../dropdown/dropdown-controller'; shadow: true, }) export class SelectItem implements DropdownItemWrapper { - @Element() hostElement: HTMLIxSelectItemElement; + @Element() hostElement!: HTMLIxSelectItemElement; /** * Displayed name of the item */ - @Prop({ reflect: true }) label: string; + @Prop({ reflect: true }) label?: string; /** * The value of the item. @@ -55,12 +58,14 @@ export class SelectItem implements DropdownItemWrapper { /** * Item clicked */ - @Event() itemClick: EventEmitter; + @Event() itemClick!: EventEmitter; + + private componentLoaded = false; /** @internal */ @Method() async getDropdownItemElement(): Promise { - return this.dropdownItem; + return this.dropdownItem!; } /** @@ -83,16 +88,31 @@ export class SelectItem implements DropdownItemWrapper { if (this.value === undefined || this.value === null) { throw Error('ix-select-item must have a `value` property'); } + this.componentLoaded = true; + } + + @Watch('value') + onValueChange(newValue: string, oldValue: string) { + if (this.componentLoaded) { + this.hostElement.dispatchEvent( + new IxSelectItemValueChangeEvent({ + newValue: newValue, + oldValue: oldValue, + }) + ); + } } @Watch('label') labelChange(newValue: string, oldValue: string) { - this.hostElement.dispatchEvent( - new IxSelectItemLabelChangeEvent({ - newValue: newValue, - oldValue: oldValue, - }) - ); + if (this.componentLoaded) { + this.hostElement.dispatchEvent( + new IxSelectItemLabelChangeEvent({ + newValue: newValue, + oldValue: oldValue, + }) + ); + } } render() { diff --git a/packages/core/src/components/select/select.tsx b/packages/core/src/components/select/select.tsx index 0bf3079bb8d..9d0271d9622 100644 --- a/packages/core/src/components/select/select.tsx +++ b/packages/core/src/components/select/select.tsx @@ -142,11 +142,10 @@ export class Select { private arrowFocusController?: ArrowFocusController; - private itemObserver = createMutationObserver(() => { + private readonly itemObserver = createMutationObserver(() => { if (!this.arrowFocusController) { return; } - this.arrowFocusController.items = this.visibleNonShadowItems; }); @@ -326,7 +325,7 @@ export class Select { this.items.forEach((item) => { item.selected = ids.some((i) => { - if (typeof i !== typeof item.value) { + if (item.value !== undefined && typeof i !== typeof item.value) { return i.toString() === item.value.toString(); } else { return i === item.value; @@ -375,6 +374,7 @@ export class Select { this.updateSelection(); } + @Listen('ix-select-item:valueChange') @Listen('ix-select-item:labelChange') onLabelChange(event: IxSelectItemLabelChangeEvent) { event.preventDefault();