Input
Used to collect form field information.
Import
import { Input } from '@pluralsight/react'
Anatomy
- Form Control
- Label
- Input
- Message
Usage
Valid Example
function ValidInputExample() {
const formId = 'email'
const [email, setEmail] = useState('')
function handleChange(e) {
setEmail(e.target.value)
}
return (
<FormControlProvider>
<Label htmlFor="email">Email</Label>
<Input
describedBy="email:help"
id="email"
name="email"
onChange={handleChange}
placeholder="email@example.com"
type="email"
value={email}
/>
<FieldMessage id="email:help">
We will never share your email.
</FieldMessage>
</FormControlProvider>
)
}
Invalid Example
For invalid form fields, add the invalid
prop to the FormControlProvider component and add an ErrorMessage component.
Since the FormControlProvider
manages the validity state of each form field, you don't have to worry about managing the child components as they will automatically update to reflect the current state.
function InvalidInputExample() {
const [email, setEmail] = useState('')
function handleChange(e) {
setEmail(e.target.value)
}
return (
<FormControlProvider required={true} invalid={!email}>
<Label htmlFor="email">Email</Label>
<Input
describedBy="email:help"
id="email"
name="email"
onChange={handleChange}
placeholder="email@example.com"
type="email"
value={email}
/>
<FieldMessage id="email:help">
We will never share your email.
</FieldMessage>
<ErrorMessage id="email:error">
Please enter an email address.
</ErrorMessage>
</FormControlProvider>
)
}
Date Picker
When you need to collect a known date, combine the Input with the useAutoFormatDate custom hook.
Calendar Pickers should only be used when the UX requires choosing a date that is unknown to the user.
Check out the Maybe You Don't Need a Date Picker article for more information.
What about type="date"
?
Contrary to the rest of the HTML element APIs, the type="date"
option for an input is not accessible. This is a known issue in the community and forces fully accessible applications to use the text
value instead.
function DateInput(props) {
const formatOptions = useAutoFormatDate()
return (
<FormControlProvider>
<Input
{...formatOptions}
describedBy="date:help"
id="date"
name="date"
startIcon={CalendarIcon}
type="text"
/>
</FormControlProvider>
)
}
Password Input
function PasswordInput() {
const [show, setShow] = useState(false)
const [password, setPassword] = useState('')
const fieldWrapper = {
position: 'relative',
}
const btnStyle = {
position: 'absolute',
right: '0.3rem',
top: '0',
zIndex: '100',
}
function handleChange(e) {
setPassword(e.target.value)
}
function handleToggleShow() {
setShow((prev) => !prev)
}
return (
<FormControlProvider required={true} invalid={!password}>
<Label htmlFor="password">Password</Label>
<div style={fieldWrapper}>
<Input
describedBy="password:error"
id="password"
name="password"
onChange={handleChange}
placeholder="Enter your password"
type={show ? 'text' : 'password'}
value={password}
/>
<Show when={password} fallback={null}>
<IconButton
ariaLabel={show ? 'Hide password' : 'Show password'}
icon={show ? EyeIcon : EyeOffIcon}
onClick={handleToggleShow}
style={btnStyle}
usage="text"
/>
</Show>
</div>
<ErrorMessage id="password:error">Please enter a password.</ErrorMessage>
</FormControlProvider>
)
}
Sizes
function InputSizes() {
return (
<div>
<FormControlProvider readOnly={true}>
<Input id="mExample" name="mExample" value="Medium input" />
</FormControlProvider>
<br />
<FormControlProvider readOnly={true}>
<Input id="lExample" name="lExample" value="Large input" />
</FormControlProvider>
</div>
)
}
Customizing
There are 3 ways to customize the Input component.
1. Unused Classes
Each component layer of the Input has a unused class name that can be utilized in your local CSS to customize the Input at any level.
pando-input
: The element of the Input.pando-input-wrapper
: The wrapper element of the Input.pando-input-start-icon
: The start icon element of the Input.pando-input-invalid-icon
: The invalid icon element of the Input.
2. Passing a className
prop
You can pass a className
prop to the Input component to customize the Input. This is useful if your project uses CSS Modules or a CSS-in-JS library like Emotion.
import customStyles from './customStyles.module.css'
function CustomInput(props) {
return <Input className={customStyles.custom} {...props} />
}
3. Ejected Input
For a low-level "ejected" approach, you can use the Headless-styles API to customize the Input however you prefer while keeping the accessibility behavior.
import {
getInputProps,
getStartIconProps,
getInvalidIconProps,
getInputWrapperProps,
} from '@pluralsight/headless-styles'
To learn more about the Headless-styles API, check out the Headless-styles documentation.
Behavior
Patterns
Do use the WarningTriangleFilled Icon for all invalid input fields.
Don't use any other Icon to represent an invalid input field.
Do stack form fields vertically when displaying a set.
Don't use more than one form field within the same row.
Usage
Do use the "(required)" text flag on all required form field labels.
Don't use a "*" to represent a required field; this pattern is not accessible.
API
Parameters
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
describedBy?: string
id: string
name: string
size?: 'm' | 'l'
startIcon?: ElementType
}
describedBy
- Theid
of the FieldMessage or ErrorMessage that describes the input.id
- Theid
of the input.name
- Thename
of the input.size
- The size of the input.startIcon
- The icon to display at the start of the input.
Size Mapping
{
m: 'Use in condensed areas where the default size is too large.',
l: 'Should be used in most, if not all cases.'
}
Accessibility
The Pando Input is fully accessible and screen-readable through the following features:
- The
aria-invalid
attribute is set totrue
when the input is invalid. - The
aria-describedby
attribute is set to theid
of the FieldMessage or ErrorMessage that describes the input. - The
aria-required
attribute is set totrue
when the input is required.