Form

React

Form composes together:

  • formik state management
  • core-react Input, Select, etc. components
  • Show and edit views, discretely known as create, read, update
  • Two layout styles
  • Conditionally rendered components, noted by a third subcomponent Form.Field.Create pattern

Usage

Peer Dependency is Required
The dependency "formik" is required for this component, hook, and context to work.

Props

Form

The provider from Formik. Additional props from Formik's Formik as well as the ones stated here.

NameTypeRequiredDefaultDescription
children
ReactNode
true
since

10.19.0

disabled
boolean
false

Disable all fields at once.

default

false

enableConfirmNavigation
boolean
false

Enable a browser confirmation about losing of unsaved data when the form values are visually different from the initial values. Uses window.onbeforeunload and window.confirm for dialogs(Modal, Tearsheet). Requires onSubmit to return a promise to remove onbeforeunload.

a11y

To prevent accidental data loss, it is recommended to enable.

default

false

Accessibility notes:
To prevent accidental data loss, it is recommended to enable.
onSubmit
((values: Values, formikHelpers: FormikHelpers<Values>) => void | Promise<any>)
false
variantfalse

Toggle between modern and traditional form styles. Leave blank for the modern label-above layout.

viewfalse

Determines show or edit state of form.

default

'create'

Form.Form

Formik's Form component.

This component toggles between a form tag on edit views and a div on show views. When on read, the div will only apply className and style props.

More information about steps of submission.

Leveraging the HTML form element allows simple form submission. The Form.Form component will automatically receive the Form's onReset and onSubmit and apply it to the form tag. Leveraging the form tag, we can use a button with type='submit' for submission. Otherwise it will be required to call the useFormContext hook or use <Form> as a render prop function to access the submit handler.

A nicety of leveraging the HTML spec inside React like this:

An app can freely change all the children (React or DOM nodes) of a form, and as long as the Provider is still present in the React tree, all the values will appear in onSubmit when the button is triggered.

Rendering a form onto the document around input components is more semantically correct. Assistive technologies and browser plugins can discover form elements and implement special hooks to make them easier to use [source].

Note: It's strictly forbidden to nest a form inside another form. Nesting can cause forms to behave in an unpredictable manner based on the browser that is being used. [source]

Form.ErrorBanner

Contextual conditional error banner. Will only display when errors are present and contains predifined text for create and update views. Will not display on the read view.

NameTypeRequiredDefaultDescription
i18nScope
string
false
since

10.19.0

item
string
true
since

10.19.0

Form.Row

Each row is a CSS grid, based on a 12 column system. Set the top level Form variant='traditional' to change the style of the row and its fields.

Fields

All fields take these general props.

Form.Field

NameTypeRequiredDefaultDescription
asfalse

Customize the input component, either a single component for all views or an object with the keys of the views to render on that particular view. This input component will receive the field prop. If using TypeScript, these components will need to extend a specific type from Core React, read more about the{' '} props per view.

One of:
ReactComponentor { '{ read: ReactComponent, create: ReactComponent, update: ReactComponent }' }

since

10.19.0

children
ReactElement<BaseFieldProps<Value, FormFieldValueComponentProps<Value>>, string | JSXElementConstructor<any>> | ReactElement<BaseFieldProps<Value, FormFieldValueComponentProps<Value>>, string | JSXElementConstructor<any>>[]
false
since

10.19.0

colStart
10 | 8 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 9 | 11
false

Starting location of column. For traditional variant, this does not exist and is not necessary.

since

10.19.0

colWidth
10 | 8 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 9 | 11
false

Width of column. For traditional variant, will be either 6 or 12.

default

6

since

10.19.0

data-qa
string
false
since

10.19.0

description
ReactNode
false
since

11.25.0

disabled
boolean
false

Specify disabled. Field disabled replaces the overall Form disabled state.

since

10.19.0

error
string | boolean
false
since

10.19.0

label
string
false
since

10.19.0

name
string
true

Key path in store. Accepts bracket[notation] or{' '} dot.notation.

since

10.19.0

required
boolean
false

The required asterisk if not using Yup required {<Form validationSchema={Yup.object().shape({ input_name: Yup.mixed().required(), })}>}

since

10.19.0

tooltip
any
false
since

10.19.0

validate
FieldValidator
false

The validate function from Formik for single field validation. {(value: Value = any) => undefined | string | Promise<Value>}

since

10.19.0

viewfalse

Determines show or edit state of field.

since

10.19.0

Specific types of fields include:

Form.Text

Field props and Input props.

Form.TextArea

Field props and TextArea props.

Form.Checkboxes

Field props. A series of several checkboxes. Similiar to a multiselect.

NameTypeRequiredDefaultDescription
checked
boolean
false

Checked state of the checkbox

since

10.19.0

children
ReactNode
false

The contents of the checkbox label

since

10.19.0

className
string
false

Additional classNames

since

10.19.0

disabled
boolean
false

Disabled state of the checkbox

since

10.19.0

error
boolean
false

Error state of the checkbox

since

10.19.0

fieldtrue
hasRequiredMark
boolean
false
since

11.25.0

i18nScope
string
false
since

10.19.0

indeterminate
boolean
false

Indeterminate state of the checkbox

since

10.19.0

inlineLabel
string
false

Next to checkbox label

since

11.25.0

requiredMark
boolean
false

Whether to show requiredMark

since

11.25.0

style
CSSProperties
false

Additional CSS styles

tooltip
any
false
since

11.25.0

Form.Checkbox

Field props and Checkbox props.

Form.Checkbox is about a single checkbox, while the HTML checkbox input type is designed for a series of checkboxes. Traditionally with checkboxes, [] is false while ['on'] is true, where 'on' is the value of each checked input. This pattern does not fit well with boolean backed data. The form state is oriented towards "yes/no" friendliness and is stored as a boolean not array. The value in store will be true or false and the value on the DOM will be "true" or "false".

Form.Currency

Field props and CurrenyInput props.

Form.DateSelect

Field props and DateSelect props.

Unlike DateSelect which only accepts date objects, Form.DateSelect can have a Form initialValue as a date object or string in the ISO format: yyyy-mm-ddThh:mm:ssZ. Like DateSelect, date changes will be a date oject or null. Be careful when providing string values, the string will be the argument to new Date().

The following examples were ran in California during DST

Format with / results in UTC midnight
Format with - results in local midnight
Format ISO results in local midnight
Format zoned time results in UTC midnight
Leading zeros on two digit entries are required

Form.MultiSelect

Field props and MultiSelect props.

Form.GroupSelect

Field props and GroupSelect props.

Form.TieredSelect

Field props and TieredSelect props.

Form.Number

Field props and NumberInput props.

Form.PillSelect

Field props and PillSelect props.

NameTypeRequiredDefaultDescription
afterHide
((e?: Event) => void)
false
afterShow
(() => void)
false
beforeHide
BeforeCallback
false
since

10.19.0

beforeShow
BeforeCallback
false
since

10.19.0

block
boolean
false

Make the Select button 100% width

since

10.19.0

container
HTMLElement
false
since

10.19.0

disabled
boolean
false
default

false

since

10.19.0

emptyMessage
string
false

When there are no children this will render

since

10.19.0

fieldtrue
footer
ReactNode
false
since

10.19.0

hideDelay
number
false
since

10.19.0

i18nScope
string
false

The i18n key to use for the select's configurable strings. Defaults to using the core library's default strings.

since

10.19.0

loading
boolean
false

If the select is loading

default

false

since

10.19.0

onClear
false | ((event: MouseEvent<HTMLButtonElement, MouseEvent>) => void)
false
since

10.19.0

onScrollBottom
((e: UIEvent<HTMLDivElement, UIEvent>) => void)
false
onSelect
((selection: Selection) => any)
false
optgroupsfalse

Array of available option groups

since

10.19.0

optionsfalse

Array of available options

since

10.19.0

optionsRef
RefObject<HTMLDivElement>
false
since

10.19.0

placeholder
string
false

Renders when the label is blank

since

10.19.0

placementfalse

The placement of the overlay

default

'bottom-left'

since

10.19.0

qa
Partial<QaTags>
false
since

10.19.0

showDelay
number
false
since

10.19.0

tabIndex
number
false

Configurable tabIndex for the select button

default

0

since

10.19.0

getColor
Function
false

A function returning the display color of an option's Pill (option: unknown) => PillColor

defaultvalue

(option) => (option as PillOption).color

since

10.19.0

getGroup
Function
false

A function returning group ID for a given option to define a relation to optgroups (option: unknown) => string | number

defaultvalue

(option) => (option as PillOption).groupId

since

10.19.0

getId
Function
false

A function returning ID of a given option (option: unknown) => string | number

defaultvalue

(option) => (option as PillOption).id

since

10.19.0

getLabel
Function
false

A function returning label of an option (option: unknown) => string

defaultvalue

(option) => (option as PillOption).label

since

10.19.0

getSuggested
Function
false

A function returning boolean value to suggest this option if nothing is selected. (option: unknown) => boolean

defaultvalue

(option) => (option as PillOption).suggested

since

10.19.0

groupGetId
Function
false

A function returning the ID of a group (group: unknown) => string | number

defaultvalue

(group) => (group as PillOptgroup).id

since

10.19.0

groupGetLabel
Function
false

A function returning label of a group (group: unknown) => string

defaultvalue

(group) => (group as PillOptgroup).label

since

10.19.0

onSearch
Function
false

Adds a search bar to the select.

since

10.19.0

Form.RadioButtons

Field props. A series of several radio buttons. Similiar to a single select.

NameTypeRequiredDefaultDescription
className
string
false

Additional classNames

fieldtrue
options
Value[]
false
since

10.19.0

style
CSSProperties
false

Additional CSS styles

getId
Function
false

The id of an option

since

10.19.0

default

(option: OptionItem) => option.id

getLabel
Function
false
The display label of an option {'(option: OptionItem) => string'}

Default: {'(option: OptionItem) => option.label || option.name'}
since

10.19.0

isDisabledOption
Function
false
since

10.19.0

Form.RichText

Field props and RichText props.

Form.Select

Field props and some Select props. Form.Select has an API closer to MultiSelect than Select. It uses getters and an array of options. Automatic searching of options and the clear icon are enabled by default.

NameTypeRequiredDefaultDescription
afterHide
((e?: Event) => void)
false
afterShow
(() => void)
false
beforeHide
BeforeCallback
false
since

10.19.0

beforeShow
BeforeCallback
false
since

10.19.0

block
boolean
false

Make the Select button 100% width

since

10.19.0

container
HTMLElement
false
since

10.19.0

disabled
boolean
false
default

false

since

10.19.0

emptyMessage
string
false

When there are no children this will render

since

10.19.0

fieldtrue
footer
ReactNode
false
since

10.19.0

hideDelay
number
false
since

10.19.0

i18nScope
string
false

The i18n key to use for the select's configurable strings. Defaults to using the core library's default strings.

since

10.19.0

label
ReactNode
false
since

10.19.0

loading
boolean
false

If the select is loading

default

false

since

10.19.0

onClear
false | ((event: MouseEvent<HTMLButtonElement, MouseEvent>) => void)
false

Callback for when cleared. Default enabled, has clear icon.

since

10.19.0

onScrollBottom
((e: UIEvent<HTMLDivElement, UIEvent>) => void)
false
onSearch
false | ((event: ChangeEvent<HTMLInputElement>) => void)
false

Callback for when searching. Default enabled, has seach bar in menu.

since

10.19.0

onSelect
((selection: Selection) => any)
false
optgroups
GroupItem[]
false

Array of available option groups

since

10.19.0

options
OptionItem[]
false
since

10.19.0

optionsRef
RefObject<HTMLDivElement>
false
since

10.19.0

placeholder
string
false

Renders when the label is blank

since

10.19.0

placementfalse

The placement of the overlay

default

'bottom-left'

since

10.19.0

qa
Partial<QaTags>
false
since

10.19.0

showDelay
number
false
since

10.19.0

tabIndex
number
false

Configurable tabIndex for the select button

default

0

since

10.19.0

getGroup
Function
false

Callback for each entry in options to define relation to group in optgroups {'(option: OptionItem) => string | number'}

since

10.19.0

getId
Function
false

The id of an option

since

10.19.0

default

(option: OptionItem) => option.id

getLabel
Function
false
The display label of an option {'(option: OptionItem) => string'}

Default: {'(option: OptionItem) => option.label || option.name'}
since

10.19.0

groupGetId
Function
false
since

10.19.0

groupGetLabel
Function
false
The display label of a group {'(group: GroupItem) => string'}

Default: {'(group: GroupItem) => group.label || group.name'}
since

10.19.0

groupHeaderRenderer
Function
false

Callback for rendering header for each entry in optgroups {'(group: GroupItem) => React.ReactNode'}

since

10.19.0

isSuggestedOption
Function
false

If nothing is selected, suggest this option. From{' '} {'Select.Option suggested'}. {'(option: OptionItem) => boolean'}

since

10.19.0

onBlur
Function
false
since

10.19.0

optionRenderer
Function
false

Callback for rendering each option {'(option: OptionItem) => React.ReactNode'}

since

10.19.0

searchComparator
Function
false
Customize how search works
{'(query: string, value: string) => boolean'}
since

10.19.0

Example

Create and Update

There is currently no visual difference between create and update. In the future that could change anywhere within the Form, like displaying meta data only on update views. Today they exist for flexibility of a field in a workflow. More about customizing between the "edit" views can be read in the props per view.

Read

Traditional Example

Create and Update

To achieve the traditional layout, majority of the props are identical. Two props differ, colWidth can only be 6 or 12 and colStart is not necessary. Width will default to 6 and is only necessary to specify 12 if the field should take the entire row.

Read

Props Per View

Using a self-closing tag with a field, e.g. <Form.Select />, it will have uniformity across create, read, and update views, i.e. this field has all the same properties like label and disabled across views. When a field needs to have different props per view, we can customize with the third subcomponent pattern.

Form.Field.Create Form.Field.Read Form.Field.Update

Conditionally rendered subcomponents based on view, a way to differenciate a field based on form state. The same rules apply for Form.Field apply to Form.Select, Form.Text, etc. Using children of Form.Field we can customize the props per view. However, the form will make no assumptions about the number of child and the field on create, read, update view, you need to explicitly place Form.Field.Create, Form.Field.Read, and Form.Field.Update if you want all views once using the Form.Field children API. By placing common props on the parent and specific props on the child, each view can be customized. (Behind the scenes, Form.Field switched from rendering HTML and is now a context provider for shared props).

What is the difference between .Create and .Update? That is for the client to decide! Both .Ceate and .Update use the same layout and input components. Depending on the UX and context of the field in a workflow, aspects might change with the field. If the create and update views are always identical, it is possible to stick to one view variant for both "edit" views.

The third tier subcomponent props are nearly identical to the second tier, Form.Field, except for children. Anything on this level like disabled, view, label will trump props on Form.Field or Form. For example, when the global state is update or disabled, a Form.Select.Update could overwrite it with view='read' or disabled={false}, while all other fields in the form are in their respective .Update view.

Form.Field is capable of letting the client customize the "input component" for a field while keeping the layout closed. The key thing at play is the field API.

field API

Type FormFieldAPI

input

Based on Formik input. Useful when working with native HTML form elements and accessing the name or value. Core React does not supply the checkbox properties from Formik.

helpers

Directly from Formik helpers. Setter methods tied to the field.

meta

Mostly Formik meta with some Core React customization. The error property is when to show the error, it is currently a combination of Formik's error and touched. The pending error message will be under message.error. Added properties like view, disabled, and required.

messages

From Core React, informational messages about the field. Today it only contains error. It is possible to have a value for messages.error but meta.error be false. This is because the meta is when to notify the user of an error.

Form.Field as prop with a Component

If using TypeScript, these components will need to extend a specific type from Core React, documented below.

Form.Field as prop with a object of Components

It is possible to create custom components for the input and output of a value. Leveraging the as prop as an object with keys of each of the view states, create, read, update. It is recommended to define this object outside of render, as it should be a static object we can keep the same memory reference for React context performance gains. (Outside of render in a functional component means top level in a file, outside of the function scope. The entire function is the render! Moving the object above the return but inside the functional component does not keep the same memory reference each render.)

TypeScript and as prop components

The input and output components for as inside the layout are typed to extend and receive the field prop with the field API. It will be necessary to import and extends the type in an interface. The type of props.field.input.value will default to any. To supply the value's type, pass the type as a generic arg.

Form.Field children prop

The children of a field, e.g. Form.Number, should be another subcomponent of the same field, e.g. Form.Number.Read, to customize props on a particular view.

Form.Field.X as prop

It only makes sense here for as to be a Component. Form.Field.Create will ever only pick the create key from the object.

If you are separating a form into distinct files like Create.jsx, Read.jsx, Update.jsx and hard setting a constant Form view state, each of those files would use a consistent subcomponent.

Form.Field.X children prop

The children at the third level is a more typical React prop, like text, JSX, or a callback function that receives the field API.

Width Examples

Column spans and column placements should align or be confirmed with the Design System.

  • colStart 1 colWidth 6 with colStart 7 colWidth 6
  • colStart 1 colWidth 12

Validation

Validation runs each submit. Failed validation will prevent form submission and update error messages.

The recommended validation method is Form validationSchema with a Yup schema. They are extremely expressive and allow modeling complex, interdependent validations, or value transformations. Validation on the Form level opposed to Form.Field guarantees fields are validated even if they are unmounted.

Yup Validation

From Formik documentation:

We use Yup for object schema validation. It has an API that's pretty similar to Joi and React PropTypes but is small enough for the browser and fast enough for runtime usage. Because we ❤️ Yup sooo much, Formik has a special config option / prop for Yup object schemas called validationSchema which will automatically transform Yup's validation errors into a pretty object whose keys match values and touched. This symmetry makes it easy to manage business logic around error messages. -- Formik, The Palmer Group

The following examples show some cases of Form validationSchema={yup}, more information can be found from the Yup documentation.

Required Yup Validation

Using a Yup required schema, yup.mixed().required(), will result with a required marker automatically include on the field. The auto include marker feature may not work with complex Yup schemas.

Inputs that can clear a value with an x button will result with a null value. To assure the error message is correct and using your translated requried text when null, add .nullable() to the object's schema.

Conditional Yup Validation

Async Yup Validation

validate Props

Not recommended alternatives.

Form validate

I suggest using validationSchema and Yup for validation. However, validate is a dependency-free, straightforward way to validate your forms. -- Formik

Resources:

Form.Field validate

You can run independent field-level validations by passing a function to the validate prop.

Note: The components' validate function will only be executed on mounted fields. That is to say, if any of your fields unmount during the flow of your form, those fields will not be validated during form validation/submission. -- Formik

This cannot be used in collapsible sections.

Resources:

error Prop

The error prop on a field will always be displayed. It is not linked to Formik validation and form submission.

Initial Errors

A form can mount with errors based on the navigation experience or technical constraints.

Submission

A button type='submit' inside the form tag (Form.Form component) will trigger submission. If any validations fail, the submission will not continue.

It is recommended to use the promise path for submission. Promise submission is required when using enableConfirmNavigation to 'clean up' after submission. More about Formik submission.

Confirm Navigation

Setting the prop enableConfirmNavigation to true will have Form apply a window.onbeforeunload listener to prevent losing unsaved changes, and after a successful submission it will remove the listener. The Form must be notified after successful submission by calling the promise success callback in the onSubmit function. The browser warning of unsaved data when leaving a page will not work in Safari.

Review Tearsheet docs to learn how this prop works in tandem with a Modal or Tearsheet trying to close.

onSubmit returns Promise

Submission promise will provide a success and an error callback. State changes like toggling isSubmitting and removing the window.onbeforeunload event will be updated automatically in the component.

If the onSubmit function is async and it returns undefined, it will always be considered a success.

If the onSubmit function is synchronous and it returns undefined, it will never reset certain states.