Skip to content

TextField

TextField wraps a text input with label, helper, and validation text. It is intended for text input types, such as text, number, email, password, tel, etc.
Figma logo
A basic TextField implementation using Formik and Yup to validate a required email text input.
A basic TextField implementation includes an input with a required label. Descriptive helperText can be added.

Disabled states should be used with caution.

Content can be prepended or appended
Mx.
Esquire
Mx.
Esquire

import React, { useState } from 'react'
import { Button, FormLayout, IconButton, TextField } from '@gusto/workbench'
import { Hide, Search, Show } from '@gusto/workbench-icons'
export const WithBeforeAfterContent = () => {
const [type, setType] = useState('password')
const [value, setValue] = useState('payroll')
return (
<FormLayout
fields={
<>
<TextField
label="Full name"
autoComplete="name"
name="name"
before="Mx."
/>
<TextField
label="Full name"
autoComplete="name"
name="name"
after="Esquire"
/>
<TextField
label="Full name"
autoComplete="name"
name="name"
before="Mx."
after="Esquire"
/>
<TextField
label="Password"
autoComplete="current-password"
name="password"
type={type}
after={
// Clickable or interactable elements must override the
// default `pointer-events: none;` by setting `pointer-events: auto;`.
// You may also want to remove the element from the tab order using `tabIndex={-1}`
<IconButton
aria-label={
type === 'password' ? `Show password` : `Hide password`
}
title={type === 'password' ? 'Show' : 'Hide'}
style={{ pointerEvents: 'auto' }}
edge="end"
onClick={() =>
setType(type === 'password' ? 'text' : 'password')
}
>
{type === 'password' ? <Show /> : <Hide />}
</IconButton>
}
defaultValue="password1!"
/>
<TextField
type="search"
name="search"
label="Search"
before={<Search />}
value={value}
onChange={e => setValue(e.target.value)}
after={
value ? (
<Button
onClick={() => setValue('')}
style={{ pointerEvents: 'auto' }}
variant="link"
>
clear
</Button>
) : null
}
/>
</>
}
/>
)
}
The CharacterCount component can be added to fields.
Any valid HTML attribute for the <input/> tag can be passed through TextField. In this example, we leverage various intrinsic HTML attributes to alter the behavior of the <input/>.
Setting the appropriate type on the input provides the browser with a hint as to which virtual keyboard would be most helpful when entering data in this field (relevant in mobile devices).The size and placeholder, min, and max are commonly used to set parameters for the data the field can accept.
Use the format prop to apply a visual mask to the input. As the user types, the mask will ensure the user is applying the correct format. Workbench supports a variety of alphanumeric masks, including phone and social security numbers.
Optional fields should be explicitly called out using the optional prop

It may sometimes be necessary to hide labels, but label text must still be made available for screen readers. This example uses the visuallyHideLabel prop to visually hide the label.

React props
NameTypeDefaultDescription
after  
ReactNode
-Content shown after the input value. By default this content is not interactive and will ignore pointer events. Any interactive elements must be styled with pointer-events: auto;
before  
ReactNode
-Content shown before the input value. By default this content is not interactive and will ignore pointer events. Any interactive elements must be styled with pointer-events: auto;
children  
ReactNode
-The content of the component. This content will be rendered as a sibling of the native input element.
component  
ReactNode
-Component override for the input element
counter  
ReactNode
-Indicator of a limit placed on a field, e.g. maximum character count or remaining hours.
fit  
contentcontainer
-Determines the point of reference for the width of the component. If set to content, the size will be set by the size of the input. If set to container, the size will expand to fit the containing element.
format  
string
-A combination of ‘static‘ and ‘replacement‘ characters to create a mask for the input value
  • * - allows any alphanumeric (e.g., format="***-***" will allow A1C-2B3)
  • 9 - allows any digit (e.g., format="999-999" will allow 123-456)
  • A - allows any letter (e.g., format="AAA-AAA" will allow ABC-DEF)
To use a literal character, escape it with a backslash (e.g., format="+\\9 (999)-999-9999" will allow +9 (123)-456-7890)
helperText  
ReactNode
-A brief description or hint for the input; can be used to link to an external resource.
id  
string
-The id of the input element
invalid  
boolean
-If true, the field will be shown in an invalid state
label  Required
string
-Label associated with the input
name  Required
string
-Name of the input. Submitted with the form as part of a name/value pair.
optional  
boolean
-Used to indicate the field is optional
securityMask  
boolean
-Add security masking to the input value. When true, the input value will be masked when not focused.
validationText  
ReactNode
-Validation message associated with the input
  • Always provide a non-empty label for the TextField
  • Avoid changing tabindex as this will affect the ability for keyboard users to navigate sequentially through the form

The following testing snippet(s) offer suggestions for testing the component using React Testing LibraryExternal link with occasional help from JestExternal link.

// BASIC TESTS
render(<TextField label="Label" helperText="Helper text" />);
const input = screen.getByLabelText('Label');
expect(input).toHaveAccessibleDescription('Helper text');
userEvent.type(input, 'Hello');
expect(input).toHaveDisplayValue('Hello');
// TESTING VALIDITY
render(<TextField label="Label" invalid validationText="Validation text" />);
const input = screen.getByLabelText('Label');
expect(input).toBeInvalid(); // or expect(input).not.toBeValid();
expect(input).toHaveAccessibleDescription('Validation text');
// TESTING MULTIPLE DESCRIPTIVE ELEMENTS
render(
<TextField
label="Label"
helperText="Helper text"
optional
validationText="Validation text"
/>,
);
const input = screen.getByLabelText('Label');
expect(input).toHaveAccessibleDescription(expect.stringContaining('(optional)'));
expect(input).toHaveAccessibleDescription(expect.stringContaining('Helper text'));
expect(input).toHaveAccessibleDescription(expect.stringContaining('Validation text'));
// or all at once:
expect(input).toHaveAccessibleDescription('(optional) Helper text Validation text');
// TESTING BEFORE AND AFTER
render(<TextField label="Label" before="Before" after="After" />);
const inputContainer = screen.getByLabelText('Label').parentElement as HTMLDivElement;
expect(container).toHaveTextContent('Before');
expect(container).toHaveTextContent('After');