NumberInput: use text input, handle invalid/pending values, and match input-family conventions (align with XDSNumberInput)
- Dominant language
- TypeScript
- Stars
- 13.1k
- Forks
- 1.1k
- Avg merge
- 1d 14h
- Merged PRs (30d)
- 669
Description
## Summary
`NumberInput` needs a refinement pass to bring it in line with the internal `XDSNumberInput` behavior and the rest of the Astryx input-family conventions. Three related problems:
## 1. Use `type="text"` (with numeric input mode), not `type="number"`
Today the internal `` is `type="number"` (`NumberInput.tsx`). The code itself already documents the pain — there's a comment: *"With `type='number'`, we can't use formatted display values."* Native number inputs are annoying to type into (scroll-wheel value changes, locale/decimal quirks, no formatted display, inconsistent cross-browser behavior).
Match the internal `XDSNumberInput` approach: render a **`type="text"`** input with an appropriate `inputMode` (e.g. `numeric`/`decimal`) and handle numeric parsing/validation in the component. This unlocks formatted display values and a much less annoying typing experience.
## 2. Account for invalid values by preserving pending input separate from the true value
There's already the seed of this — `pendingInput` and an `isInputValid` memo exist — but the behavior should match the internal component: **when the input is invalid, mute the input visually while preserving what the user typed, kept separate from the committed/true numeric value.** The user's in-progress text shouldn't be destroyed or silently coerced; the true value stays last-valid until a valid value is committed. Formalize the "display/pending value vs. true value" split so invalid intermediate states are a first-class, visible state rather than an edge case.
## 3. Match input-family conventions it's currently missing
`NumberInput` is missing conventions the rest of the input family (and `XDSNumberInput`) provide. Audit against the family and add the missing ones, e.g.:
- **`hasSpinner`** — increment/decrement stepper affordance.
- **`changeAction`/`onChangeAction`** — the async action-style change callback convention used across inputs.
- Any other standard input props it lacks (review against `TextInput`/the input-consistency checklist): `startIcon`, status handling, `isLoading`/`isBusy`, `isOptional`/`isRequired`, `isLabelHidden`, `description`, etc.
The goal is that `NumberInput` behaves consistently with both `XDSNumberInput` internally and Astryx's own input-family contract.
## Acceptance criteria
- [ ] Input renders as `type="text"` with a numeric `inputMode`; formatted display values work; typing is not degraded by native number-input behavior.
- [ ] Invalid/pending input is preserved separately from the true value, and invalid state is shown by muting the input (matching internal behavior).
- [ ] `hasSpinner`, `changeAction`/`onChangeAction`, and any other missing input-family conventions are added and consistent with sibling inputs.
- [ ] Behavior reconciled with `XDSNumberInput`; API differences resolved to Astryx conventions.
- [ ] Tests, `.doc.mjs`, Storybook story, and showcase block updated to match.
Contributor guide
Assessment
This issue has not been assessed yet.