Form composes together:
The provider from Formik. Additional props from Formik's Formik as well as the ones stated here.
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]
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.
Name | Type | Required | Default | Description |
---|---|---|---|---|
i18nScope | string | false | ||
item | string | true |
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.
All fields take these general props.
Name | Type | Required | Default | Description |
---|---|---|---|---|
as | ComponentWithFieldProp<Value, FormFieldValueComponentProps<Value>> | { create: ComponentWithFieldProp<Value, FormFieldValueComponentProps<Value>>; read: ComponentWithFieldProp<Value, FormFieldValueComponentProps<Value>>; update: ComponentWithFieldProp<Value, FormFieldValueComponentProps<Value>> } | false | 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 | |
children | ReactElement<BaseFieldProps<Value, FormFieldValueComponentProps<Value>>, string | JSXElementConstructor<any>> | ReactElement<BaseFieldProps<Value, FormFieldValueComponentProps<Value>>, string | JSXElementConstructor<any>>[] | false | ||
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. | |
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. | |
data-qa | string | false | ||
disabled | boolean | false | Specify disabled. Field disabled replaces the overall Form disabled state. | |
error | string | boolean | false | ||
label | string | false | ||
name | string | true | Key path in store. Accepts | |
required | boolean | false | The required asterisk if not using Yup | |
tooltip | any | false | ||
validate | FieldValidator | false | The validate function from Formik for single field validation.
| |
view | false | Determines show or edit state of field. |
Specific types of fields include:
Field props and Input props.
Field props and TextArea props.
Field props. A series of several checkboxes. Similiar to a multiselect.
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".
Field props and CurrenyInput props.
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
Field props and MultiSelect props.
Field props and GroupSelect props.
Field props and TieredSelect props.
Field props and NumberInput props.
Field props and PillSelect props.
Name | Type | Required | Default | Description |
---|---|---|---|---|
afterHide | ((e?: Event) => void) | false | ||
afterShow | (() => void) | false | ||
beforeHide | BeforeCallback | false | ||
beforeShow | BeforeCallback | false | ||
block | boolean | false | Make the Select button 100% width | |
disabled | boolean | false | ||
emptyMessage | string | false | When there are no children this will render | |
field | true | |||
footer | ReactNode | false | ||
hideDelay | number | false | ||
i18nScope | string | false | The i18n key to use for the select's configurable strings. Defaults to using the core library's default strings. | |
loading | boolean | false | If the select is loading | |
onClear | false | ((event: MouseEvent<HTMLButtonElement, MouseEvent>) => void) | false | ||
onScrollBottom | ((e: UIEvent<HTMLDivElement, UIEvent>) => void) | false | ||
onSelect | false | |||
optgroups | false | Array of available option groups | ||
options | false | Array of available options | ||
optionsRef | RefObject<HTMLDivElement> | false | ||
placeholder | string | false | Renders when the label is blank | |
placement | false | The placement of the overlay | ||
qa | Partial<QaTags> | false | ||
showDelay | number | false | ||
tabIndex | number | false | Configurable tabIndex for the select button | |
getColor | Function | false | A function returning the display color of an option's | |
getGroup | Function | false | A function returning group ID for a given option to define a relation to | |
getId | Function | false | A function returning ID of a given option
| |
getLabel | Function | false | A function returning label of an option
| |
getSuggested | Function | false | A function returning | |
groupGetId | Function | false | A function returning the ID of a group
| |
groupGetLabel | Function | false | A function returning label of a group
| |
onSearch | Function | false | Adds a search bar to the select. |
Field props. A series of several radio buttons. Similiar to a single select.
Name | Type | Required | Default | Description |
---|---|---|---|---|
className | string | false | Additional classNames | |
field | true | |||
options | Value[] | false | ||
style | CSSProperties | false | Additional CSS styles | |
getId | Function | false |
The id of an option{' '}
{'(option: OptionItem) => string | number'}
Default:
{'(option: OptionItem) => option.id'}
| |
getLabel | Function | false |
The display label of an option
{'(option: OptionItem) => string'}
Default:
{'(option: OptionItem) => option.label || option.name'}
| |
isDisabledOption | Function | false |
Field props and RichText props.
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.
Name | Type | Required | Default | Description |
---|---|---|---|---|
afterHide | ((e?: Event) => void) | false | ||
afterShow | (() => void) | false | ||
beforeHide | BeforeCallback | false | ||
beforeShow | BeforeCallback | false | ||
block | boolean | false | Make the Select button 100% width | |
disabled | boolean | false | ||
emptyMessage | string | false | When there are no children this will render | |
field | true | |||
footer | ReactNode | false | ||
hideDelay | number | false | ||
i18nScope | string | false | The i18n key to use for the select's configurable strings. Defaults to using the core library's default strings. | |
label | ReactNode | false | ||
loading | boolean | false | If the select is loading | |
onClear | false | ((event: MouseEvent<HTMLButtonElement, MouseEvent>) => void) | false | Callback for when cleared. Default enabled, has clear icon. | |
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. | |
onSelect | false | |||
optgroups | GroupItem[] | false | Array of available option groups | |
options | OptionItem[] | false | ||
optionsRef | RefObject<HTMLDivElement> | false | ||
placeholder | string | false | Renders when the label is blank | |
placement | false | The placement of the overlay | ||
qa | Partial<QaTags> | false | ||
showDelay | number | false | ||
tabIndex | number | false | Configurable tabIndex for the select button | |
getGroup | Function | false | Callback for each entry in | |
getId | Function | false |
The id of an option{' '}
{'(option: OptionItem) => string | number'}
Default:
{'(option: OptionItem) => option.id'}
| |
getLabel | Function | false |
The display label of an option
{'(option: OptionItem) => string'}
Default:
{'(option: OptionItem) => option.label || option.name'}
| |
groupGetId | Function | false | ||
groupGetLabel | Function | false |
The display label of a group
{'(group: GroupItem) => string'}
Default:
{'(group: GroupItem) => group.label || group.name'}
| |
groupHeaderRenderer | Function | false | Callback for rendering header for each entry in | |
isSuggestedOption | Function | false | If nothing is selected, suggest this option. From{' '}
| |
onBlur | Function | false | ||
optionRenderer | Function | false | Callback for rendering each | |
searchComparator | Function | false | Customize how search works
{'(query: string, value: string) => boolean'} |
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.
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.
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.
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.
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.
If using TypeScript, these components will need to extend a specific type from Core React, documented below.
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.)
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.
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.
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.
The children at the third level is a more typical React prop, like text, JSX, or a callback function that receives the field API.
Column spans and column placements should align or be confirmed with the Design System.
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.
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.
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.
Not recommended alternatives.
I suggest using validationSchema and Yup for validation. However, validate is a dependency-free, straightforward way to validate your forms. -- Formik
Resources:
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:
The error prop on a field will always be displayed. It is not linked to Formik validation and form submission.
A form can mount with errors based on the navigation experience or technical constraints.
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.
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.
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.
10.19.0