0.0.66 ā€¢ Published 24 days ago

use-a11y-form v0.0.66

Weekly downloads
-
License
-
Repository
-
Last release
24 days ago

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>
0.0.66

24 days ago

0.0.63

1 month ago

0.0.64

1 month ago

0.0.65

1 month ago

0.0.62

1 month ago

0.0.61

2 months ago

0.0.60

2 months ago

0.0.59

3 months ago

0.0.56

4 months ago

0.0.57

4 months ago

0.0.58

4 months ago

0.0.55

4 months ago

0.0.54

4 months ago

0.0.51

5 months ago

0.0.52

5 months ago

0.0.53

5 months ago

0.0.50

5 months ago

0.0.48

5 months ago

0.0.49

5 months ago

0.0.40

7 months ago

0.0.41

7 months ago

0.0.42

6 months ago

0.0.43

6 months ago

0.0.44

6 months ago

0.0.45

5 months ago

0.0.46

5 months ago

0.0.47

5 months ago

0.0.39

7 months ago

0.0.38

7 months ago

0.0.37

7 months ago

0.0.36

7 months ago

0.0.35

7 months ago

0.0.34

7 months ago

0.0.33

7 months ago

0.0.32

7 months ago

0.0.31

8 months ago

0.0.30

8 months ago

0.0.29

8 months ago

0.0.28

8 months ago

0.0.27

8 months ago

0.0.26

8 months ago

0.0.25

8 months ago

0.0.24

8 months ago

0.0.23

8 months ago

0.0.22

8 months ago

0.0.21

8 months ago

0.0.20

8 months ago

0.0.19

8 months ago

0.0.18

8 months ago

0.0.17

8 months ago

0.0.16

8 months ago

0.0.15

8 months ago

0.0.14

8 months ago

0.0.13

8 months ago

0.0.12

8 months ago

0.0.11

8 months ago

0.0.10

8 months ago

0.0.9

8 months ago

0.0.8

8 months ago

0.0.7

8 months ago

0.0.6

8 months ago

0.0.5

8 months ago

0.0.4

8 months ago

0.0.3

8 months ago

0.0.2

8 months ago

0.0.1

8 months ago