| describes | NumberInput |
|---|
A controlled number input field.
By deafult, it renders a <intput type="number"> to the DOM. However, if you need any string value, use the allowStringValue flag. Only use this if absolutely necessary, since it could be confusing for screenreader users.
Note that this field does not work
uncontrolled - you must pass event handlers if you want it to respond to
user input.
This example handles arrow buttons, up/down arrow keys, and typing into the input. It also includes an onBlur handler that displays an error message if the input is invalid or missing.
---
type: example
---
const Example = () => {
const MIN = 0
const MAX = 999
const [disabled, setDisabled] = useState(false)
const [inline, setInline] = useState(false)
const [messages, setMessages] = useState(null)
const [number, setNumber] = useState(null)
const [readOnly, setReadOnly] = useState(false)
const [showArrows, setShowArrows] = useState(true)
const [value, setValue] = useState('')
const handleChange = (event, value) => {
setMessages(null)
setNumber(value ? Number(value) : null)
setValue(value)
}
const handleDecrement = (event) => {
if (!isNaN(number)) {
const newNumber =
number === null
? MIN
: Number.isInteger(number)
? number - 1
: Math.floor(number)
updateNumber(newNumber)
}
}
const handleIncrement = (event) => {
if (!isNaN(number)) {
const newNumber =
number === null
? MIN + 1
: Number.isInteger(number)
? number + 1
: Math.ceil(number)
updateNumber(newNumber)
}
}
const handleBlur = (event) => {
if (isNaN(number)) return invalidNumber(value)
if (number === null) return required()
return updateNumber(Math.round(number))
}
const updateNumber = (n) => {
const number = bound(n)
setMessages(null)
setNumber(number)
setValue(number)
}
const bound = (n) => {
if (n < MIN) return MIN
if (n > MAX) return MAX
return n
}
const invalidNumber = (value) => {
setMessages([
{ text: `'${value}' is not a valid number.`, type: 'error' }
])
}
const required = () => {
setMessages([{ text: 'This is required.', type: 'error' }])
}
const toggleDisabled = (event) => {
setDisabled((prevDisabled) => !prevDisabled)
}
const toggleInline = (event) => {
setInline((prevInline) => !prevInline)
}
const toggleReadOnly = (event) => {
setReadOnly((prevReadOnly) => !prevReadOnly)
}
const toggleShowArrows = (event) => {
setShowArrows((prevShowArrows) => !prevShowArrows)
}
return (
<FormFieldGroup description="Controlled NumberInput example">
<Checkbox
checked={showArrows}
label="Show arrows"
onChange={toggleShowArrows}
/>
<Checkbox
checked={disabled}
label="Disabled"
onChange={toggleDisabled}
/>
<Checkbox
checked={readOnly}
label="Read only"
onChange={toggleReadOnly}
/>
<Checkbox
checked={inline}
label="Inline display"
onChange={toggleInline}
/>
<Text>Fields marked with an asterisk <span aria-hidden="true">(*)</span> are required.</Text>
<NumberInput
renderLabel={`How old are you? (${MIN}-${MAX})`}
display={inline ? 'inline-block' : null}
messages={messages}
onBlur={handleBlur}
onChange={handleChange}
onDecrement={handleDecrement}
onIncrement={handleIncrement}
placeholder="Age (in years)"
interaction={
disabled ? 'disabled' : readOnly ? 'readonly' : 'enabled'
}
isRequired
showArrows={showArrows}
value={value}
/>
</FormFieldGroup>
)
}
render(<Example />)Note:
NumberInputaccepts a string or number as itsvalue. However, the value returned by theonChangecallback is always a string and should be converted to a number before attempting to augment it.
You can see here most of the visual states of the component.
---
type: example
---
<Flex gap='medium' direction='column'>
<NumberInput
renderLabel='normal'
placeholder="placeholder"
/>
<NumberInput
interaction='disabled'
renderLabel='disabled'
placeholder="placeholder"
/>
<NumberInput
interaction='readonly'
renderLabel='readonly'
placeholder="placeholder"
/>
<NumberInput
renderLabel='with error message'
placeholder="placeholder"
messages={[{ text: 'This is an error.', type: 'error' }]}
/>
<NumberInput
renderLabel='with success message'
placeholder="placeholder"
messages={[{ text: 'Great success!', type: 'success' }]}
/>
<NumberInput
renderLabel='large size (default is "medium")'
placeholder="placeholder"
size='large'
/>
</Flex>---
type: embed
---
<Guidelines>
<Figure recommendation="yes" title="Do">
<Figure.Item>Use when you need increment/decrement functionality</Figure.Item>
<Figure.Item>Use labels at the top or to the left of the input field</Figure.Item>
</Figure>
<Figure recommendation="no" title="Don't">
<Figure.Item>Place labels or text strings to the right of the input field</Figure.Item>
</Figure>
</Guidelines>