# 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),
[LegacySelect](/components/LegacySelect), [Switch](../Switch/Switch.md),
[Checkbox](../Checkbox/Checkbox.md), and [Chips](/components/Chips). 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.](../product-vocabulary/product-vocabulary.md)

## Setup

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

## Accessibility

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

### Mobile

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `children` | `ReactNode` | Yes | — | Content to be passed into the form |
| `onSubmit` | `(data: T) => Promise<S>` | Yes | — | A callback function that handles the submission of form data. If an error occurs during submission, it should not be ... |
| `onSubmitError` | `(error: FormErrors) => void` | Yes | — | A callback function that handles any error that occurs during "onSubmit" |
| `onSubmitSuccess` | `(data: S) => void` | Yes | — | A callback function that handles a successful form submission from "onSubmit" |
| `bannerErrors` | `FormBannerErrors` | No | — | Network or user errors to be displayed as a banner at the top of the form |
| `bannerMessages` | `FormBannerMessage[]` | No | — | Status messages to be displayed as a banner at the top of the form |
| `disableKeyboardAwareScroll` | `boolean` | No | — | @internal Do not use this prop. It is a hack and will be removed. TODO: JOB-147156 This is a HACK for multiline input... |
| `formRef` | `RefObject<UseFormReturn<T> & { scrollViewRef?: RefObject<KeyboardAwareScrollViewRef>; saveButtonHeight?: number; messageBannerHeight?: number; }>` | No | — | ref object to access react hook form methods and state |
| `initialLoading` | `boolean` | No | — | Loading when the initial form data is being fetched |
| `initialValues` | `{ [x: string]: any; } | ((BrowserNativeObject | NestedValue) & FieldValues) | { [x: string]: any; }` | No | — | The initial values of the form inputs This should be available as soon as initialLoading is set to false |
| `localCacheExclude` | `string[]` | No | — | Forms field names that will not be considered for caching. Useful for omitting sensitive data. |
| `localCacheId` | `string | string[]` | No | — | A string or array of strings that can be used to identify the pre-filled data on the form. This can be used to suppor... |
| `localCacheKey` | `string` | No | — | Adding a key will save a local copy of the form data that will be used to recover values when the app is backgrounded... |
| `mode` | `"all" | "onBlur" | "onChange" | "onSubmit" | "onTouched"` | No | — | When the validation should happen. Possible values are "onBlur", "onChange", "onSubmit", "onTouched", and "all". The ... |
| `onBeforeSubmit` | `(data: T) => Promise<boolean>` | No | — | A callback function that is run before invoking onSubmit. Form submission is canceled if the promise resolves to false. |
| `renderFooter` | `ReactNode` | No | — | Renders a footer below the save button. |
| `renderStickySection` | `(onSubmit: () => void, label: string, isSubmitting: boolean) => ReactElement<unknown, string | JSXElementConstructor<any>>` | No | — | @deprecated use `secondaryAction` instead. Override default save button in the sticky section of the form with anothe... |
| `reValidateMode` | `"onBlur" | "onChange" | "onSubmit"` | No | — | When the validation after submission should happen. Possible values are "onBlur", "onChange", and "onSubmit". The def... |
| `saveButtonLabel` | `string` | No | — | Label to be displayed for the save button |
| `saveButtonOffset` | `number` | No | — | A number that will pull down the save button when the position is sticky. Useful when there's a footer or content bel... |
| `secondaryActions` | `SecondaryActionProp[]` | No | — | Secondary Action for ButtonGroup |
| `showStickySaveButton` | `boolean` | No | — | Forces to render the sticky save button instead of the inline. The sticky save button is default for iOS but not for ... |
| `UNSAFE_allowDiscardLocalCacheWhenOffline` | `boolean` | No | — | If true, the local cache will be removed when the user navigates away from the dirty form even when offline. By defau... |
