Esri / calcite-design-system

A monorepo containing the packages for Esri's Calcite Design System
https://developers.arcgis.com/calcite-design-system/
Other
289 stars 76 forks source link

[Input] Setting .value before render() throws an error #10405

Open maxpatiiuk opened 1 month ago

maxpatiiuk commented 1 month ago

Check existing issues

Actual Behavior

calcite-input has a watcher on value property. When watcher triggers, it updates the HTML element:

https://github.com/Esri/calcite-design-system/blob/c99be674d3bb3b721ce0e8d39b0cb266b5aac840/packages/calcite-components/src/components/input/input.tsx#L976-L984

If .value property is set after component's constructor() (because Stencil watchers are disabled before that), but before render() (because .childEl ref is set after render()), the above watcher will fail with this exception:

TypeError: Cannot set properties of undefined (setting 'value')

The issue is that while the watcher checks for this.childEl being undefined, it only does so when input type is text. This might be an artifact back from when this input only support number and input types?

Expected Behavior

Setting value should not produce an exception

Reproduction Sample

https://codepen.io/maxpatiiuk/pen/ZEgGWod?editors=1010

Reproduction Steps

  1. Set .value property or a calcite-input with a type not equal to text and not equal to number after component's constructor() triggered but before render()
  2. See exception in the console

Reproduction Version

2.13.0

Relevant Info

No response

Regression?

No response

Priority impact

impact - p3 - not time sensitive

Impact

No response

Calcite package

Esri team

N/A

maxpatiiuk commented 1 month ago

Side node:

this line in the watcher has several issues:

this[child${this.type === "number" ? "Number" : ""}El].value = newInputValue;

maxpatiiuk commented 1 month ago

Changing the setInputValue like this seems to fix the issue:

  private setInputValue(newInputValue: string): void {
    if (this.type === "number" && this.childNumberEl) {
      this.childNumberEl.value = newInputValue;
    } else if (this.childEl) {
      this.childEl.value = newInputValue;
    }
  }