Description
Check existing issues
- I have checked for existing issues to avoid duplicates
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
- Locate the first
calcite-input-number
(the one labeled "Set to readOnly immediately on change").
a. The event handler for thecalciteInputNumberInput
sets this input'sreadOnly
property totrue
. Then, usingsetTimeout
, it schedules a handler to setreadOnly
back tofalse
one second later. - Click the input's increase button.
- 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