Skip to main content

Input

Used to collect form field information.

Import

import { Input } from '@pluralsight/react'

Anatomy

  1. Form Control
  2. Label
  3. Input
  4. 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.

  1. pando-input: The element of the Input.
  2. pando-input-wrapper: The wrapper element of the Input.
  3. pando-input-start-icon: The start icon element of the Input.
  4. 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
}
  1. describedBy - The id of the FieldMessage or ErrorMessage that describes the input.
  2. id - The id of the input.
  3. name - The name of the input.
  4. size - The size of the input.
  5. 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:

  1. The aria-invalid attribute is set to true when the input is invalid.
  2. The aria-describedby attribute is set to the id of the FieldMessage or ErrorMessage that describes the input.
  3. The aria-required attribute is set to true when the input is required.