import type * as React from 'react'; import LabelMask from './LabelMask'; import Mask from './Mask'; import TextInput from './TextInput'; import classNames from 'classnames'; import cleanFieldProps from '../utilities/cleanFieldProps'; import describeField from '../utilities/describeField'; import useId from '../utilities/useId'; import { Label } from '../Label'; import { useLabelProps, UseLabelPropsProps } from '../Label/useLabelProps'; import { useHint, UseHintProps } from '../Hint/useHint'; import { useInlineError, UseInlineErrorProps } from '../InlineError/useInlineError'; export type TextFieldDefaultValue = string | number; export type TextFieldMask = 'currency' | 'phone' | 'ssn' | 'zip'; export type TextFieldRows = number | string; export type TextFieldSize = 'small' | 'medium'; export type TextFieldValue = string | number; interface BaseTextFieldProps { /** * Apply an `aria-label` to the text field to provide additional * context to assistive devices. */ ariaLabel?: string; /* * Sets the focus on the select during the first mount */ autoFocus?: boolean; /** * Additional classes to be added to the root element. */ className?: string; /** * Sets the initial value. Use this for an uncontrolled component; otherwise, * use the `value` property. */ defaultValue?: TextFieldDefaultValue; disabled?: boolean; fieldClassName?: string; /** * A unique `id` to be used on the text field. */ id?: string; /** * Access a reference to the `input` or `textarea` element */ inputRef?: React.Ref | React.MutableRefObject; /** * Set to `true` to apply the "inverse" color scheme */ inversed?: boolean; /** * Applies date format masking to the input value entered and renders to a text field * above the input. Passing `true` to `valueOnly` will return just the formatted value * entered. */ labelMask?: (rawInput: string, valueOnly?: boolean) => string; /** * Apply formatting to the field that's unique to the value you expect to be entered. * Depending on the mask, the field's appearance and functionality may be affected. */ mask?: TextFieldMask; /** * Whether or not the text field is a multiline text field */ multiline?: boolean; name: string; /** * Sets `inputMode`, `type`, and `pattern` to improve accessibility and consistency for number fields. Use this prop instead of `type="number"`, see [here](https://technology.blog.gov.uk/2020/02/24/why-the-gov-uk-design-system-team-changed-the-input-type-for-numbers/) for more information. */ numeric?: boolean; onBlur?: (...args: any[]) => any; onChange?: (...args: any[]) => any; /** * @hide-prop HTML `input` [pattern](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefpattern). */ pattern?: string; /** * Optionally specify the number of visible text lines for the field. Only * applicable if this is a multiline field. */ rows?: TextFieldRows; /** * Set the max-width of the input either to `'small'` or `'medium'`. */ size?: TextFieldSize; /** * HTML `input` [type](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#_types) attribute. If you are using `type=number` please use the numeric prop instead. */ type?: string; /** * Sets the input's `value`. Use this in combination with `onChange` * for a controlled component; otherwise, set `defaultValue`. */ value?: TextFieldValue; } export type TextFieldProps = BaseTextFieldProps & Omit, keyof BaseTextFieldProps> & Omit; /** * For information about how and when to use this component, * [refer to its full documentation page](https://design.cms.gov/components/text-field/). */ export const TextField = (props: TextFieldProps) => { const { id: originalId, mask, labelMask, className, ...remainingProps } = props; const id = useId('text-field--', originalId); if (process.env.NODE_ENV !== 'production') { if (props.type === 'number') { console.warn( `Please use the 'numeric' prop instead of 'type="number"' unless your user research suggests otherwise.` ); } if (mask && labelMask) { console.warn( "The 'mask' and 'labelMask' fields are mutually exclusive. Please use either one or the other." ); } } const { errorId, topError, bottomError, invalid } = useInlineError({ ...props, id }); const { hintId, hintElement } = useHint({ ...props, id }); const labelProps = useLabelProps({ ...props, id }); let input = ( ); // See if we need to wrap the input based on some props if (mask) { input = {input}; } else if (labelMask) { input = {input}; } return (
); }; TextField.defaultProps = { type: 'text', }; export default TextField;