import { forwardRef, ReactElement, ReactNode } from 'react'; import InternalTextField from './TextField/InternalTextField'; import VRInternalTextField from './TextField/VRInternalTextField'; import useExperimentalTheme from './utils/useExperimentalTheme'; // deals with strings, but we only want numbers for this component. // So we parse what we get from InternalTextField and we stringify what we give it. type Handler = any; const parseHandlerValue = (handler?: Handler) => ({ event, value, }: | { event: React.ChangeEvent; value: string; } | { event: React.FocusEvent; value: string; } | { event: React.KeyboardEvent; value: string; }) => { const parsedValue = parseFloat(value); handler?.({ event, value: Number.isFinite(parsedValue) ? parsedValue : undefined, }); }; type Props = { /** * Indicate if autocomplete should be available on the input. */ autoComplete?: 'on' | 'off'; /** * Available for testing purposes, if needed. Consider [better queries](https://testing-library.com/docs/queries/about/#priority) before using this prop. */ dataTestId?: string; /** * Indicate if the input is disabled. */ disabled?: boolean; /** * For most use cases, pass a string with a helpful error message (be sure to localize!). In certain instances it can be useful to make some text clickable; to support this, you may instead pass a React.Node to wrap text in [Link](https://gestalt.pinterest.systems/web/link) or [TapArea](https://gestalt.pinterest.systems/web/taparea). */ errorMessage?: ReactNode; /** * More information for the user about how to complete the form field. */ helperText?: string; /** * A unique identifier for the input. */ id: string; /** * The label for the input. Be sure to localize the text. */ label?: string; /** * Whether the label should be visible or not. If `hidden`, the label is still available for screen reader users, but does not appear visually. */ labelDisplay?: 'visible' | 'hidden'; /** * The upper bound of valid input, inclusive. */ max?: number; /** * The lower bound of valid input, inclusive. */ min?: number; /** * Mobile only prop. Optionally specify the action label to present for the enter key on virtual keyboards. See the [enterKeyHint variant](https://gestalt.pinterest.systems/web/numberfield#EnterKeyHint) for more info. * */ mobileEnterKeyHint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'; /** * A unique name for the input. */ name?: string; /** * Callback triggered when the user blurs the input. */ onBlur?: (arg1: { event: React.FocusEvent; value: number | undefined }) => void; /** * Callback triggered when the value of the input changes, whether by keyboard entry or the input's arrows. */ onChange: (arg1: { event: React.ChangeEvent; value: number | undefined; }) => void; /** * Callback triggered when the user focuses the input. */ onFocus?: (arg1: { event: React.FocusEvent; value: number | undefined; }) => void; /** * Callback triggered when the user presses any key while the input is focused. */ onKeyDown?: (arg1: { event: React.KeyboardEvent; value: number | undefined; }) => void; /** * Placeholder text shown the the user has not yet input a value. */ placeholder?: string; /** * Indicate if the input is readOnly. See the [readOnly example](https://gestalt.pinterest.systems/web/textfield#Read-only) for more details. */ readOnly?: boolean; /** * Ref that is forwarded to the underlying input element. */ ref?: ReactElement; // eslint-disable-line react/no-unused-prop-types, /** * Defines the height of NumberField: sm: 32px, md: 40px (default), lg: 48px. See the [size variant](https://gestalt.pinterest.systems/web/NumberField#Size) for more details. */ size?: 'sm' | 'md' | 'lg'; /** * Indicates the amount the value will increase or decrease when using the input's arrows. */ step?: number; /** * The current value of the input. */ value?: number | undefined; }; /** * [NumberField](https://gestalt.pinterest.systems/web/numberfield) allows for numerical input. * * ![NumberField light mode](https://raw.githubusercontent.com/pinterest/gestalt/master/playwright/visual-test/NumberField.spec.ts-snapshots/NumberField-chromium-darwin.png) * ![NumberField dark mode](https://raw.githubusercontent.com/pinterest/gestalt/master/playwright/visual-test/NumberField-dark.spec.ts-snapshots/NumberField-dark-chromium-darwin.png) * */ const NumberFieldWithForwardRef = forwardRef(function NumberField( { autoComplete, dataTestId, disabled = false, mobileEnterKeyHint, errorMessage, helperText, id, label, labelDisplay = 'visible', max, min, name, onBlur, onChange, onFocus, onKeyDown, placeholder, readOnly = false, size = 'md', step, value, }: Props, ref, ) { const theme = useExperimentalTheme(); if (theme.MAIN) { return ( ); } return ( ); }); NumberFieldWithForwardRef.displayName = 'NumberField'; export default NumberFieldWithForwardRef;