# Form

The Form component is a wrapper component that handles the submission and
validation of forms.

For more information about `validations` using any of the Input components, see
the [InputText](../InputText/InputText.md) documentation.

## Design & usages guidelines

The Form component has a lot of built-in features which rely on its internal
state. To take advantage of these features, do not bypass the Form's internal
state — the fields or inputs within the Form must have a `name` prop and **NOT**
have a `value` and `onChange` prop.

## Content guidelines

### Inputs

Form can accept various inputs and selection elements such as (but not limited
to) [InputText](../InputText/InputText.md), [Select](../Select/Select.md),
[Switch](../Switch/Switch.md), [Checkbox](../Checkbox/Checkbox.md), and
[Chips](../Chips/Chips.md). They should be placed [Cards](../Card/Card.md) to
indicate grouping when relevant, and groups of Cards can be spaced appropriately
using ContentSection.

### Save Button label

The `saveButtonLabel` property defaults to "Save", but should be made more
verbose to add context for the user. Use the format "Save {object}", such as
"Save Job". This helps clarify to the user that tapping the Save Button is not
saving the single input they are editing, but the entire object.

### Form errors

All error messaging should follow our
[Product Vocabulary.](/content/product-vocabulary)

## Setup (mobile)

Consuming apps using the mobile Form must wrap their app root with
`KeyboardProvider` from `react-native-keyboard-controller`.

## Accessibility (mobile)

The individual inputs are responsible for accessibility concerns such as the
labels, types, values, and error messages of each input.


## Platform considerations

#### iOS

On iOS, the save button will be fixed to the bottom of the viewport until the
keyboard is opened.

Once the keyboard is open, the save button will be inline beneath the Form's
inputs, unless the Form is so short that it does not scroll. In this case, the
save button will remain fixed above the keyboard.

This prevents the user from accidentally submitting the Form before they have
completed entering all the relevant information for their work, especially since
many data points in Jobber cannot be edited once saved.

#### Android

On Android, the save button will always be inline with the contents of the Form.

### Error handling

#### Server-side errors

Server-side error messages will be displayed in a banner at the top the Form
upon a failed submission attempt. These are errors where something has gone
wrong with the data either on the way to, or on the way back from, our servers.
If the user can address the errors, inform them how to do so in the banner.
Otherwise, a generic message informing the user that something went wrong is an
appropriate fallback.

Note: When a server-side error happens, inside the `onSubmit` function, an error
must be thrown. Throwing an error inside the `onSubmit` function will ensure
that your `onSubmitError` function is called instead of your `onSubmitSuccess`.

#### Client-side errors

Client-side errors are issues that we can catch and inform the user of before
they attempt to submit the Form, such as a required field left blank or an
incorrect email address format.

Error messages for individual inputs should appear inline on each impacted
input. Form will automatically scroll to the first invalid (has an error)
element and display the error message.

When an error occurs, either server-side or client-side, Form will announce the
message to screen-readers and set focus to the impacted portion of the Form.


## Props

### Web

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `onStateChange` | `(formState: { isDirty: boolean; isValid: boolean; }) => void` | No | — |  |
| `onSubmit` | `() => void` | No | — | Callback for when the form has been sucessfully submitted. |
| `ref` | `Ref<InputTextRef>` | No | — | Allows getting a ref to the component instance. Once the component unmounts, React will set `ref.current` to `null` (... |
