S-number-field: contents cannot be selected, so typing over a value appends instead of replacing

Selecting the contents of an s-number-field is not possible, which makes typing over a value append instead of replace.

The inner control is an <input type="number">, and the selection APIs are inert on that input type. Select-all also does not reach the field, so the next keystroke lands at the caret rather than replacing what is there.

In our case the field is a receive quantity that starts at 0 and is nearly always typed over. Typing 10 leaves “010”, and typing 0 over an existing 10 leaves “100”. On a goods-receipt screen that is a ten-fold quantity error a merchant has to spot before saving.

Reproducing it:

  1. Render an <s-number-field> with a value of 0
  2. Click into the field and type 10
  3. Select all, then type 0

Expected: 10, then 0. Actual: “010”, then “100”.

What I found when I looked into it, logged from a keydown listener on the element, reading event.composedPath()[0] to reach the real input:

inputType: "number"
value: "10"
selectionStart: null
selectionEnd: null
setSelectionRange: InvalidStateError: Failed to execute 'setSelectionRange' on
  'HTMLInputElement': The input element's type ('number') does not support selection.

So setSelectionRange throws, selectionStart reads null, and select() is a no-op. That is standard behaviour for input type="number" rather than anything Polaris is doing wrong, but it leaves no way to select the field’s contents from application code.

The things I tried, none of which work:

  • select() on the s-number-field element itself. It is not on the element.
  • select() on the inner input via composedPath()[0]. Runs without error, does nothing.
  • setSelectionRange(0, value.length) on the inner input. Throws as above.
  • An onKeyDown prop on the component. The React types reject it, so the listener has to be attached natively via a ref.

Intercepting the shortcut and emulating the replacement in application code is possible, but it means reconstructing what the user typed by diffing the old and new values, which is fragile and something every app using a number field would have to write for itself.

A few questions:

  1. Is the appending behaviour on select-all expected, or is the shortcut not reaching the input because of the shadow root?
  2. Is there a supported way to select an s-number-field’s contents, or to have it select on focus? I could not find one in the docs.
  3. Would inputMode="numeric" on an <input type="text"> be considered instead for this component, since that keeps the numeric keypad on mobile while leaving the selection APIs usable?

Happy to put together a minimal reproduction if that would help.

For anyone hitting this before it is resolved: normalising the value on input to strip a leading zero fixes the “010” case, though not the case where a shorter value is typed over a longer one.

Hey @Luke - thanks for flagging. We’re looking into this on our end. As always, can’t guarantee anything in terms of implementation or a timeline, but just wanted to let you know we have eyes on this :slight_smile: