Skip to main content

NumberInput

Allows users to enter a number into a UI.

Submit feedback
github

Migration Information

For teams migrating from the V1 to V2 component, please refer to the migration guide for changes to the component.
Component Guide
import { NumberInput } from '@uhg-abyss/web/ui/NumberInput';
() => {
const [value, setValue] = useState('5');
return (
<NumberInput
label="NumberInput Sandbox"
value={value}
onChange={(e) => setValue(e)}
/>
);
};

Using the useForm hook lets the DOM handle form data.

Using useForm and FormProvider simplifies form management by providing out-of-the-box validation, form state handling, and performance optimizations, leveraging the power of react-hook-form.


useState

Using the useState hook gets values from the component state.

Display properties

Label

Use the label prop to display a label above the input. To hide the input label set hideLabel to true.

Use isRequired for further customization.

Note: If using useForm, do not use isRequired. The same functionality can be achieved with required: true in validators.

Helper

Use the helper prop to display a help icon next to the label. Simply passing a string value will render the default helper, a Tooltip containing that string. The helper can be customized by passing in a node. It is recommended to use either a Tooltip or a Popover. See When should I use a Tooltip vs. a Popover? for more information on best practices regarding the two.

Subtext

Use the subText prop to display helpful information related to the input field. The prop accepts either a string or an object of the form:

{
text: string;
position: 'above' | 'below';
}

The position property determines where the subtext will be displayed in relation to the input field. The default value is 'below'.

Where appropriate, use it for detailing the increment/step value, as shown in the example below. When doing so, avoid using abbreviations like "Inc." or "Dec." and instead use full descriptive terms like "increment" or "decrement".

Stepper only

Use the stepperOnly prop to disable the input field and only allow value changes through the increment and decrement buttons. The default is false.

Note: When using stepperOnly, the input field is still accessible through keyboard navigation.

Width

Use the width prop to set the width of the number input field. The default value is 48px. A width value should be chosen to allow the maximum value to be fully visible, but the default behavior is an ellipsis displayed to signify overflow.

Validation

Validators (useForm)

Form Compatibility
useState
useForm

Use the validators prop to display a custom error below the number input field or do other validation when using useForm.

Note: The default error message when required is true is minimally acceptable for accessibility. It is highly recommended to customize it to be more specific to the use of the field and form.

Error message

Form Compatibility
useState
useForm

Use the errorMessage prop to display a custom error message below the input field when using useState.

Success message

Form Compatibility
useState
useForm

Use the successMessage prop to display a custom success message below the input field. To provide a single success message across all form input components using useForm/FormProvider, you can provide successMessage to FormProvider as shown here.

Highlighted

Form Compatibility
useState
useForm

Use the highlighted prop to enable a distinct background color when fields are required. To supply this across all form input components using useForm/FormProvider, you can provide highlighted to FormProvider as shown here.

Min and max values

Use the minValue and maxValue props to apply min/max limits to the number input field. The default minValue is -9007199254740991 and maxValue is 9007199254740991.

When minValue and maxValue are provided, input will be validated against the min/max values upon blur. For example, if the maxValue is 5, and the user types 7, 5 will replace 7.

Step value

Use the step prop to increase and decrease the value by the given step. The default value is set to 1.

Masks

Mask config

Use the maskConfig prop to pass a masking configuration to the number input field. The default value for decimalScale is 0.

We use react-number-format internally for mask configuration. Please visit the provided link for more details on the available options.

Decimal step value

Use the decimalScale prop within maskConfig and set to a value greater than 0 to allow decimal values within the number input field. The step prop can also be used to increase/decrease by decimal values.

NumberInput Props

NameTypeDefaultRequiredDescription
className
string
--
CSS class name to apply to each element within the component
css
Abyss.CSSProperties | Partial<Record<`abyss-${T}`, Abyss.CSSProperties>>
--
Object containing CSS styling to apply; uses the classes listed in the "Integration" tab of the component's documentation

See CSS Prop Styling for more information
data-testid
string
--
Suffix used to create a unique ID used for automated testing

See Component Testing for more information
disableDefaultProviderProps
boolean
false
-
If set to true, the component will not use the DefaultPropsProvider values.

If you aren’t using the DefaultPropsProvider, this prop can be ignored.
errorMessage
string | never
--
Error message to display for the input. Only used when not in a FormProvider.
errorMessage should not exist in useForm mode.
helper
React.ReactNode
--
Helper element next to label
hideLabel
boolean
false
-
If true, the label will be visually hidden
highlighted
boolean
false
-
If true, the input field will be highlighted
isDisabled
boolean
false
-
If true, the input will be disabled
isRequired
boolean | never
--
Whether the input is required. Only used when not in a FormProvider.
isRequired should not exist in useForm mode.
label
string
-
The label for the input
maskConfig
NumericFormatProps
--
Mask configuration for the input field
NumericFormatProps is only used when mask is 'numeric'

See the Numeric Format for more information.
maxValue
number
'Number.MAX_SAFE_INTEGER'
-
The maximum value for the number input
minValue
number
'-Number.MAX_SAFE_INTEGER'
-
The minimum value for the number input
model
never | string
--
model should not exist in useState mode.
Model name for form validation. Only used when in a FormProvider.
onBlur
React.FocusEventHandler<HTMLInputElement>
--
Callback function executed when the input is blurred
onFocus
React.FocusEventHandler<HTMLInputElement>
--
Callback function executed when the input is focused
step
number
1
-
The step value for the number input
stepperOnly
boolean
false
-
Whether to use the stepper buttons only or also use the input field
subText
string | { text: string; position: SubTextPosition; }
--
Additional descriptive text for the input
successMessage
string
--
Success message to display for the input
validators
never | RegisterOptions
--
validators should not exist in useState mode.
Validators for the input. Only used when in a FormProvider.
value
ValueType | never
--
The value of the input. Only used when not in a FormProvider.
value should not exist in useForm mode.

Below are the link(s) to the relevant GitHub type files:

Abyss.d.ts

NumberInput Classes

Class NameDescription
.abyss-number-input-rootNumberInput root container
.abyss-number-input-wrapper-containerNumberInput wrapper container
.abyss-number-input-labelNumberInput label
.abyss-number-inputNumberInput input field
.abyss-number-input-helperNumberInput helper text
.abyss-number-input-sub-textNumberInput subtext
.abyss-number-input-decrementNumberInput decrement button
.abyss-number-input-incrementNumberInput increment button
.abyss-number-input-formNumberInput form wrapper
.abyss-number-input-descriptorsNumberInput descriptors

NumberInput is a spinbutton

NumberInput is an implementation of the HTML5 <input type=”number”> and WAI-ARIA spinbutton pattern .

Spinbutton “single field” implementation

Standard spinbutton functionality (both in HTML5 and WAI-ARIA) is provided by the up and down arrows. This makes the increment and decrement buttons redundant for keyboard operation. This same approach is applied in NumberInput.

This is why in NumberInput the visible [-] and [+] do not receive keyboard focus.

If they received focus the field would:

  • become a grouping of one field and two buttons -- making it a non-standard for spinbutton
  • more cumbersome to use -- adding extra tab stops for unnecessary buttons
  • require lengthier announcements for grouping and value

Button behavior on click: set focus to field

The buttons do remain clickable/tappable for mouse/touch operation. When selected, NumberInput sets focus to the field to provide missing context to the announced button.

This provides the expected behavior for HTML5 type=”input” when clicking the same control onHover. It also improves on the WAI-ARIA example that only announces the button and provides no context for what it controls.

A known potential issue with this implementation is that the full description of the field is announced on each click which can be lengthy. This should not be a significant issue to any sighted mouse users that also use screen readers who choose to use them since new clicks interrupt announcements of previous ones.

Screen reader operation (errors)

VoiceOver (MacOS Sonoma 14.4.1) announces "50%" when focus is (re-)set to the field. Using up/down arrows announces correct current value. Exiting and returning to field will still announce "50%" on focus.

This appears to be a bug in VoiceOver interpretation of the field's value. This fails in WAI-ARIA Date Picker Spin Button Example (for the year field). Adding aria-valuenow or aria-valuetext has no effect on the announcement. As of this writing (May 2024) There is little information found about how to address this.

Keyboard Interactions

KeyDescription
Up ArrowIncrement value
Down ArrowDecrement value
Left ArrowMove to the previous character in the input field
Right ArrowMove to the next character in the input field
Page UpCurrently not supported
Page DownCurrently not supported

Component Tokens

Note: Click on the token row to copy the token to your clipboard.

NumberInput Tokens

Token NameValue
number-input.color.text.stepper.disabled
#7D7F81
number-input.sizing.height.stepper
40px
number-input.spacing.gap.vertical.container
16px
number-input.spacing.gap.horizontal.container
12px

Input Tokens

Token NameValue
input.color.surface.field.default
#FFFFFF
input.color.surface.field.highlighted
#E5F8FB
input.color.surface.field.disabled
#F3F3F3
input.color.border.field.rest
#4B4D4F
input.color.border.field.hover.default
#196ECF
input.color.border.field.hover.error
#990000
input.color.border.field.hover.success
#007000
input.color.border.field.active.default
#004BA0
input.color.border.field.active.error
#990000
input.color.border.field.active.success
#007000
input.color.text.input
#4B4D4F
input.color.text.hint
#4B4D4F
input.color.text.required
#990000
input.color.icon.utility.rest
#4B4D4F
input.color.icon.utility.hover
#323334
input.color.icon.utility.active
#000000
input.color.icon.content
#323334
input.border-radius.all.field
4px
input.border-width.all.field.default
1px
input.border-width.all.field.active
3px
input.sizing.all.icon
20px
input.spacing.gap.vertical.container
8px
input.spacing.gap.horizontal.field
12px
input.spacing.gap.horizontal.input-indicator
2px
input.spacing.gap.horizontal.prefix-input
2px
input.spacing.gap.horizontal.suffix-clear
2px
input.spacing.padding.all.focus-container
2px
input.spacing.padding.horizontal.field
12px
input.spacing.padding.left.field
12px
input.spacing.padding.right.focus-field
44px

Header Tokens

Token NameValue
input-header.color.text.label
#4B4D4F
input-header.color.text.hint
#4B4D4F
input-header.color.icon.info.rest
#196ECF
input-header.color.icon.info.hover
#004BA0
input-header.color.icon.info.active
#002677
input-header.sizing.all.icon
24px
input-header.spacing.gap.horizontal.container
4px
input-header.spacing.gap.horizontal.label
4px
input-header.spacing.gap.vertical.content
4px
input-header.spacing.padding.top.content
2px

Validation Tokens

Token NameValue
input-validation.color.surface.container
#FFFFFF
input-validation.color.text.error
#990000
input-validation.color.text.success
#007000
input-validation.color.icon.error
#990000
input-validation.color.icon.success
#007000
input-validation.sizing.all.icon
20px
input-validation.spacing.gap.horizontal.container
4px
Table of Contents