Skip to content

[Input Number]: infinite increment/decrement when set to readOnly after value change #12407

Open
@tristan-morrison

Description

@tristan-morrison

Check existing issues

Actual Behavior

This issue occurs when the handler for the calciteInputNumberInput event of a calcite-input-number sets the readOnly property of the component to false. In such cases, clicking the "increase" or "decrease" buttons will cause the input value to be continually incremented or decremented, respectively.

Expected Behavior

Each single click on the increase or decrease buttons results in a single incrementing or decrementing of the number value. Setting the readOnly property immediately after a value change has no effect on or interference with this behavior.

Reproduction Sample

https://codepen.io/tristan-morrison/pen/ZYGwOOg

Reproduction Steps

  1. Locate the first calcite-input-number (the one labeled "Set to readOnly immediately on change").
    a. The event handler for the calciteInputNumberInput sets this input's readOnly property to true. Then, using setTimeout, it schedules a handler to set readOnly back to false one second later.
  2. Click the input's increase button.
  3. Observe that the value continues to increase ceaselessly.

Reproduction Version

3.2.1

Relevant Info

Related issue

See issue 72364 in the JS SDK repo.

Analysis

The problem occurs due to the way in which pointer events on the increase/decrease buttons trigger increments/decrements of the input's value. The pointer-down handler for the increase/decrease buttons does things. First, it increments/decrements the input value. Then, it uses setInterval to install a handler that continually increments/decrements the value once every 150 milliseconds. The pointer-up handler for the increase/decrease buttons clears the setInterval, thus stopping the repeated value changes.

I gather that the reason for this setInterval handler is that it allows holding down the increment/decrement buttons to work as expected — the value continues changing until you let go of the mouse button. Clicking the button also works as expected — i.e., it changes the value only once — because the pointer-up event comes less than 150 milliseconds after the pointer-down event, so the setInterval handler is cleared before it can trigger any additional value changes. This works as it should in most cases.

Separately, note that when readOnly is set on a calcite-input-number, the increase and decrease buttons are not rendered at all.

The problem described here occurs if the handler for the calciteInputNumberInput event causes the component's readOnly property to be set to true. It occurs due to the following sequence of events. When you press the mouse button down on the increment button, the pointer-down handler for the increment button runs. It increments the value once, and it installs a setInterval handler that will repeatedly increment the value every 150 milliseconds until the button receives the pointer-up event. The value change triggers the calciteInputNumberInput. The event handler sets readOnly to true on the input. This causes the increment button to disappear (since it is not rendered when the component is readOnly). But now that the increment button is no longer in the DOM, it cannot receive the pointer-up event! Even if you release the mouse button immediately (like in the case of a click), the increment button is already gone and never receives the pointer-up event. This means that the setInterval handler is never cleared, and it continues incrementing the value infinitely.

Regression?

No response

Priority impact

impact - p2 - want for an upcoming milestone

Impact

This issue affects the attribute form widgets of the Maps SDK for JavaScript, i.e. BatchAttributeForm and FeatureForm. In turn, it also affects the Editor widget.

In attribute forms, Arcade expressions can be used to control certain behaviors, like the value of a field, whether a field is visible, whether a field is editable, etc. If a form input has an editableExpression that evaluates to false, we set the readOnly property on the corresponding input, since the user is not allowed to change that input's value.

Form expressions can reference the values in the form itself, via the attributes of the profile variable $feature. Thus, the expressions may need to be re-executed when the value of a field changes. Suppose I have a number field FieldA. It has an editableExpression that evaluates to true, so the user is allowed to change its value. The user clicks the increase button to change the input value. In response to the value changing, the editableExpression re-executes, and this time, it evaluates to false. We then set the readOnly property on the corresponding input, and thus we encounter the problem described in this issue.

It may seem strange that changing the value of a form field can cause that same form input to become read-only. I can certainly appreciate the view that this simply shouldn't happen. However, it is a logical consequence of the WebMap form specification that this is indeed possible, so we need to be able to handle it properly.

Calcite package

  • @esri/calcite-components
  • @esri/calcite-components-react
  • @esri/calcite-design-tokens
  • @esri/calcite-ui-icons
  • @esri/eslint-plugin-calcite-components

Esri team

ArcGIS Maps SDK for JavaScript

Metadata

Metadata

Assignees

No one assigned

    Labels

    0 - newNew issues that need assignment.ArcGIS Maps SDK for JavaScriptIssues logged by ArcGIS SDK for JavaScript team members.bugBug reports for broken functionality. Issues should include a reproduction of the bug.impact - p2 - want for an upcoming milestoneUser set priority impact status of p2 - want for an upcoming milestoneneeds triagePlanning workflow - pending design/dev review.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions