use-a11y-form
TypeScript icon, indicating that this package has built-in type declarations

0.0.66 • Public • Published

use-a11y-form 🤖

React form hooks with a11y inclusive providers

  • 🚶 Super simple, accessible, multi-step + routed + tabbable forms friendly
  • 🤖 Compulsory accessibility. Aria labels and other good a11y practices are done by default
  • 💅 Headless. Integrates well with your existing components and stylings

use-a11y-form Logo

Installation

npm i use-a11y-form

Getting started

import { useForm, Form } from 'use-a11y-form'

const SignIn = () => {
  const form = useForm({
    values: { email: '', password: '' },
    validate: values => ({
      name: !values.name && 'Enter a name',
      password: !values.password && 'Enter a password',
    }),
    onSubmit: async (form, event) => {
      // ... form.values
      return 'Signed in successfully'
    },
  })

  return (
    <Form form={form}>
      <Form.Field name='name'>
        <Form.Label>Name</Form.Label>
        <Form.Input auto='name' />
        <Form.Error />
      </Form.Field>

      <Form.Field name='email'>
        <Form.Label>Email</Form.Label>
        <Form.Input type='email' auto='email' />
        <Form.Error />
      </Form.Field>

      <Form.Assertive />

      <Form.Submit>Continue</Form.Submit>
    </Form>
  )
}

Component behavior outside of form context

Some form components can be used outside of a <Form/> context. Wrapping your label and input field with no form context will automatically generate and assign ids.

const Field = ({ label, name, ...props }) => {
  return (
    <div className='...'>
      <Form.Field>
        {/* Will render label with correct id binding to your input */}
        <Form.Label>{label}</Form.Label>
        <Form.Input {...props} className={cx('...', props.className)} />

        {/* Will be ignored outside <Form/> */}
        <Form.Error />
      </Form.Field>
    </div>
  )
}
<div>
  <label for="XY"></label>
  <input id="XY"></label>
</div>

API Reference

useForm

Creates a new form api

useForm({
  values, // initial values
  validate, // values validations
  onSubmit, // submit handler
  translate, // error, assertive, ... translate fn
  disabled, // defines if entire form disabled
  valuesDependency, // defines if form should depend on changes do values object
  allowSubmitAttempt, // alternative form behavior, allows a first invalid submit attempt
  allowErrorSubmit, // allow submit when form still has errors
})

Form API

const form = useForm(...)

form.errors // form errors
form.values // form values
form.touched // touched fields
form.alert // assertive alert text

form.isSubmitting // defines if an async submit was called and is still unresolved
form.isValid // form is valid
form.isTouched // form was touched
form.isCompleted // form is completed

form.setValues({ name: value })
form.setValue("name", value)
form.setAssertive("Try again later.")
form.completed()

form.reset() // reset form state
form.reset({ ... }) // reset form state with values

Validation

The validate function recieves the current values and returns an object mapping fields to errors. Only strings are considered errors.

useForm({
  values: { name: '' },
  validate: values => ({ name: !name && '' }),
})
Validate values using vx

vx is a validation helper function that allows you to chain-compose multiple validations for a single value.

({ password }) => ({
  password: vx(
    !password && "Enter a name",
    password.length < 8 && "Password a longer password",
  )
}),

Submit

The submit function recieves the current form object and values

useForm({
  onSubmit: async ({ values, ...form }) => {
    const { error } = await signIn(values)
    if (error) return error // return assertive form error
    form.complete() // the complete method will disable the form
  },
})

Form providers

Form

Root form and context provider.

Parameters
  • form: form api object
  • asChild: use child as rendered element
  • validate: whether to reenable standard html form validation

All form properties are also passed as data attributes: data-use-a11y-form, data-submitting, data-disabled, data-submit-disabled, data-touched, data-completed, data-valid, data-invalid, data-showing-errors

<Form form={form}>
  {...}
</Form>

// or as child
<Form form={form} asChild>
  <form />
</Form>

Form.Context

Makes field context accessible through callback. Useful for custom or complex input components. Will throw an error if used outside <Form/>

<Form form={form}>
  <Form.Context>
    {form => {
      form.isCompleted
    }}
  </Form.Context>
</Form>

Form.FieldSet

Wraps fields in a accessible fieldset

<Form.FieldSet name="name">{...}</Form.FieldSet>

Form.Field

Wraps field components like Form.Input and Form.Label with field context

<Form.Field name="name">
  {...}
</Form.Field>

Form.FieldContext

Makes field context accessible through callback. Useful for custom or complex input components. Will throw an error if used outside <Form.Field/>

<Form.Field name='country'>
  <Form.FieldContext<string>>{field => <select value={field.value} />}</Form.FieldContext>
</Form.Field>

Form.Label

Create a label inside a field context

Parameters
  • asChild: use child as rendered element
<Form.Field name='name'>
  <Form.Label>Name</Form.Label>
</Form.Field>

Form.Input

Create an input inside a field context

Parameters
  • asChild: use child as rendered element
  • auto: pass a typed and accessible autocomplete

All input properties are also passed as data attributes: data-error, data-touched, data-has-hidden-error, data-disabled, data-field-context

<Form.Field name='name'>
  <Form.Input />
  {/* auto prop */}
  <Form.Input /> {/* autoComplete="off" */}
  <Form.Input auto /> {/* autoComplete="on" */}
  <Form.Input auto={false} /> {/* autoComplete="off" */}
  <Form.Input auto='given-name' /> {/* autoComplete="given-name" */}
</Form.Field>

Form.Error

Create an accessible input error inside a field context

Parameters
  • asChild: use child as rendered element
<Form.Field name='name'>
  <Form.Error />
</Form.Field>

Form.Assertive

Render assertive alert that may be returned from onSubmit

Parameters
  • asChild: use child as rendered element
<Form form={form}>
  <Form.Assertive />

  {/* if provided with children, the alert will always be shown */}
  <Form.Assertive>Something went wrong</Form.Assertive>
</Form>

Form.Submit

Render provided form submit

Parameters
  • asChild: use child as rendered element
<Form form={form}>
  <Form.Submit />
</Form>

vx

Validation helper

vx('Error A', 'Error B') // "Error A"
vx(false, 'Error B') // "Error B"
vx(false, false) // null

Context hooks

⚠️ Context hooks will throw an error if used outside their respective providers.

useFormContext

Access your form's context from your component

const form = useFormContext()

useFieldContext

Access your a field's context from your component

const form = useFieldContext<string>()
form.value // string

Adapters

createFieldAdapter

Creates a new field to component props adapter

const adaptRadio = createFieldAdapter((field) => ({ ..., checked: field.value }))

adaptSelect

Adapts a field context to work with select input

<Form.Field name='country'>
  <Form.FieldContext>
    {field => {
      field.value // null | "afghanistan" | ...
    }}
  </Form.FieldContext>

  <Form.FieldContext<Options>>
    {adaptSelect(field => {
      field.value // "" | "afghanistan" | ...
    })}
  </Form.FieldContext>
</Form.Field>

adaptCheckbox

Adapts a field context to work with a checkbox input

<Form.Field name='accept'>
  <Form.FieldContext<boolean>>
    {adaptCheckbox(props => (
      <input {...props} />
    ))}
  </Form.FieldContext>
</Form.Field>

Readme

Keywords

none

Package Sidebar

Install

npm i use-a11y-form

Weekly Downloads

7

Version

0.0.66

License

none

Unpacked Size

65.1 kB

Total Files

8

Last publish

Collaborators

  • weltmx