@iress-oss/ids-mcp-server 6.0.0-alpha.1-canary-20251204032753-fe18cab → 6.0.0-alpha.1-canary-20251204040305-3639454
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/generated/docs/components_components-alert-docs.md +1054 -0
- package/generated/docs/components_components-autocomplete-docs.md +3304 -0
- package/generated/docs/components_components-autocomplete-recipes-docs.md +98 -0
- package/generated/docs/components_components-badge-docs.md +312 -0
- package/generated/docs/components_components-button-docs.md +2339 -0
- package/generated/docs/components_components-buttongroup-docs.md +980 -0
- package/generated/docs/components_components-card-docs.md +1970 -0
- package/generated/docs/components_components-checkbox-docs.md +1083 -0
- package/generated/docs/components_components-checkboxgroup-docs.md +1597 -0
- package/generated/docs/components_components-checkboxgroup-recipes-docs.md +209 -0
- package/generated/docs/components_components-col-docs.md +755 -0
- package/generated/docs/components_components-container-docs.md +172 -0
- package/generated/docs/components_components-divider-docs.md +235 -0
- package/generated/docs/components_components-expander-docs.md +428 -0
- package/generated/docs/components_components-field-docs.md +3345 -0
- package/generated/docs/components_components-filter-docs.md +4091 -0
- package/generated/docs/components_components-hide-docs.md +450 -0
- package/generated/docs/components_components-icon-docs.md +1017 -0
- package/generated/docs/components_components-image-docs.md +168 -0
- package/generated/docs/components_components-inline-docs.md +1962 -0
- package/generated/docs/components_components-input-docs.md +1388 -0
- package/generated/docs/components_components-input-recipes-docs.md +349 -0
- package/generated/docs/components_components-inputcurrency-docs.md +636 -0
- package/generated/docs/components_components-inputcurrency-recipes-docs.md +208 -0
- package/generated/docs/components_components-introduction-docs.md +448 -0
- package/generated/docs/components_components-label-docs.md +229 -0
- package/generated/docs/components_components-link-docs.md +454 -0
- package/generated/docs/components_components-menu-docs.md +2219 -0
- package/generated/docs/components_components-menu-menuitem-docs.md +1455 -0
- package/generated/docs/components_components-modal-docs.md +2002 -0
- package/generated/docs/components_components-panel-docs.md +342 -0
- package/generated/docs/components_components-placeholder-docs.md +98 -0
- package/generated/docs/components_components-popover-docs.md +1631 -0
- package/generated/docs/components_components-popover-recipes-docs.md +537 -0
- package/generated/docs/components_components-progress-docs.md +128 -0
- package/generated/docs/components_components-provider-docs.md +123 -0
- package/generated/docs/components_components-radio-docs.md +499 -0
- package/generated/docs/components_components-radiogroup-docs.md +1573 -0
- package/generated/docs/components_components-readonly-docs.md +277 -0
- package/generated/docs/components_components-richselect-docs.md +6101 -0
- package/generated/docs/components_components-row-docs.md +2172 -0
- package/generated/docs/components_components-select-docs.md +1110 -0
- package/generated/docs/components_components-skeleton-docs.md +467 -0
- package/generated/docs/components_components-skeleton-recipes-docs.md +110 -0
- package/generated/docs/components_components-skiplink-docs.md +220 -0
- package/generated/docs/components_components-slideout-docs.md +1910 -0
- package/generated/docs/components_components-slider-docs.md +1284 -0
- package/generated/docs/components_components-spinner-docs.md +90 -0
- package/generated/docs/components_components-stack-docs.md +730 -0
- package/generated/docs/components_components-table-docs.md +4038 -0
- package/generated/docs/components_components-tabset-docs.md +955 -0
- package/generated/docs/components_components-tabset-tab-docs.md +342 -0
- package/generated/docs/components_components-tag-docs.md +410 -0
- package/generated/docs/components_components-text-docs.md +593 -0
- package/generated/docs/components_components-toaster-docs.md +451 -0
- package/generated/docs/components_components-toggle-docs.md +513 -0
- package/generated/docs/components_components-tooltip-docs.md +564 -0
- package/generated/docs/components_components-validationmessage-docs.md +608 -0
- package/generated/docs/components_contact-us-docs.md +9 -0
- package/generated/docs/components_foundations-accessibility-docs.md +33 -0
- package/generated/docs/components_foundations-consistency-docs.md +44 -0
- package/generated/docs/components_foundations-content-docs.md +18 -0
- package/generated/docs/components_foundations-introduction-docs.md +17 -0
- package/generated/docs/components_foundations-principles-docs.md +60 -0
- package/generated/docs/components_foundations-responsive-layout-docs.md +2692 -0
- package/generated/docs/components_foundations-user-experience-docs.md +53 -0
- package/generated/docs/components_foundations-visual-design-docs.md +39 -0
- package/generated/docs/components_foundations-z-index-stacking-docs.md +288 -0
- package/generated/docs/components_frequently-asked-questions-docs.md +44 -0
- package/generated/docs/components_get-started-develop-docs.md +47 -0
- package/generated/docs/components_get-started-using-storybook-docs.md +55 -0
- package/generated/docs/components_introduction-docs.md +85 -0
- package/generated/docs/components_patterns-form-docs.md +2469 -0
- package/generated/docs/components_patterns-form-recipes-docs.md +1597 -0
- package/generated/docs/components_patterns-introduction-docs.md +31 -0
- package/generated/docs/components_patterns-loading-docs.md +1908 -0
- package/generated/docs/components_patterns-shadow-docs.md +195 -0
- package/generated/docs/components_resources-code-katas-docs.md +25 -0
- package/generated/docs/components_resources-introduction-docs.md +28 -0
- package/generated/docs/components_resources-mcp-server-docs.md +23 -0
- package/generated/docs/components_resources-migration-guides-from-v4-to-v5-docs.md +142 -0
- package/generated/docs/components_styling-props-colour-docs.md +91 -0
- package/generated/docs/components_styling-props-elevation-docs.md +69 -0
- package/generated/docs/components_styling-props-radius-docs.md +63 -0
- package/generated/docs/components_styling-props-reference-docs.md +160 -0
- package/generated/docs/components_styling-props-screen-readers-docs.md +66 -0
- package/generated/docs/components_styling-props-sizing-docs.md +217 -0
- package/generated/docs/components_styling-props-spacing-docs.md +545 -0
- package/generated/docs/components_styling-props-typography-docs.md +66 -0
- package/generated/docs/components_versions-docs.md +14 -0
- package/generated/docs/guidelines.md +3083 -0
- package/generated/docs/introduction-docs.md +37 -0
- package/generated/docs/tokens_colour-docs.md +479 -0
- package/generated/docs/tokens_elevation-docs.md +39 -0
- package/generated/docs/tokens_introduction-docs.md +150 -0
- package/generated/docs/tokens_radius-docs.md +67 -0
- package/generated/docs/tokens_spacing-docs.md +87 -0
- package/generated/docs/tokens_typography-docs.md +305 -0
- package/package.json +2 -2
|
@@ -0,0 +1,2469 @@
|
|
|
1
|
+
Form
|
|
2
|
+
====
|
|
3
|
+
|
|
4
|
+
Overview
|
|
5
|
+
--------
|
|
6
|
+
|
|
7
|
+
Use the `IressForm` component when you want to request, validate and process data from the user.
|
|
8
|
+
|
|
9
|
+
[](./iframe.html?id=patterns-form--simple)
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
<ForwardedForm
|
|
14
|
+
actions\={<IressButton mode\="primary" type\="submit"\>Submit</IressButton\>}
|
|
15
|
+
pattern\="short"
|
|
16
|
+
\>
|
|
17
|
+
<IressText element\="p"\>
|
|
18
|
+
This form is a simple example of how to use{' '}
|
|
19
|
+
<code\>
|
|
20
|
+
IressFormField </code\>
|
|
21
|
+
{' '}with{' '}
|
|
22
|
+
<code\>
|
|
23
|
+
IressInput </code\>
|
|
24
|
+
. </IressText\>
|
|
25
|
+
<IressFormField
|
|
26
|
+
label\="Name"
|
|
27
|
+
name\="name"
|
|
28
|
+
render\={() \=> {}}
|
|
29
|
+
rules\={{
|
|
30
|
+
required: 'Name is required'
|
|
31
|
+
}}
|
|
32
|
+
/>
|
|
33
|
+
<IressFormField
|
|
34
|
+
label\="Email address"
|
|
35
|
+
name\="email"
|
|
36
|
+
render\={() \=> {}}
|
|
37
|
+
rules\={{
|
|
38
|
+
minLength: {
|
|
39
|
+
message: 'Use a longer email address',
|
|
40
|
+
value: 6
|
|
41
|
+
},
|
|
42
|
+
required: 'Email is required'
|
|
43
|
+
}}
|
|
44
|
+
/>
|
|
45
|
+
</ForwardedForm\>
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Props
|
|
50
|
+
-----
|
|
51
|
+
|
|
52
|
+
Below are the custom props you can access when using `IressForm`, which includes the props for the [`useForm` hook from React Hook Forms](https://react-hook-form.com/docs/useform). In addition to these, you have access to the original props of the underlying [`form` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form).
|
|
53
|
+
|
|
54
|
+
| Name | Description | Default | Control |
|
|
55
|
+
| --- | --- | --- | --- |
|
|
56
|
+
| actions |
|
|
57
|
+
object
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
| \- | Choose option...nonesubmitsave |
|
|
62
|
+
| alert |
|
|
63
|
+
|
|
64
|
+
\-
|
|
65
|
+
|
|
66
|
+
| \- | Choose option...nonebasicAlert |
|
|
67
|
+
| children |
|
|
68
|
+
|
|
69
|
+
array
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
| \- | Choose option...nonesimplesupportedControlsreadOnly |
|
|
74
|
+
| pattern |
|
|
75
|
+
|
|
76
|
+
string
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
| \- | short |
|
|
81
|
+
| Show React Hook Forms itemsReact Hook Forms | Show React Hook Forms items |
|
|
82
|
+
|
|
83
|
+
Key concepts
|
|
84
|
+
------------
|
|
85
|
+
|
|
86
|
+
As of version 5, `IressForm` is built on top of [React Hook Forms](https://react-hook-form.com/). This provides you the power of React Hook Forms with the convenience of IDS form components. However, there are some key differences to be aware of as it may affect how you build your forms.
|
|
87
|
+
|
|
88
|
+
### State management
|
|
89
|
+
|
|
90
|
+
`IressForm` manages the state of the form, including the form data and validation. This is done using the `useForm` hook from React Hook Forms. This hook provides a way to manage the form state, and provides methods to interact with the form. This was done to simplify the form components, make them more predictable (as the form becomes the single source of truth for all form related data) and improve performance by reducing re-renders (very important for large forms).
|
|
91
|
+
|
|
92
|
+
Due to this change, there are a few things you should consider during development:
|
|
93
|
+
|
|
94
|
+
* Avoid using the `useState` hook to manage form state. Instead, use the `IressForm.useForm` hook or the `ref` of the `IressForm` component to interact with the form state. The initial value of the form can be set using the `defaultValues` prop, but from then on you should be using either the hook or ref to interact with the form state.
|
|
95
|
+
* Avoid using `onChange` handlers on form fields to react to form values. Instead, use the `IressForm.useWatch` hook to watch the value of a field and conditionally render fields based on the value of another field.
|
|
96
|
+
|
|
97
|
+
See below an example comparing a version 4 and version 5 `IressForm` when managing form state.
|
|
98
|
+
|
|
99
|
+
[](./iframe.html?id=patterns-form--state-management-v-4-to-v-5)
|
|
100
|
+
|
|
101
|
+
Diff
|
|
102
|
+
|
|
103
|
+
Old
|
|
104
|
+
|
|
105
|
+
New
|
|
106
|
+
|
|
107
|
+
<table class="css-1n5o7vh-diff-container"><tbody><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text">import { IressForm, IressField, IressInput, IressCheckboxGroup, IressCheckbox } from '@iress/components';</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text">import { IressForm, IressFormField, IressInput, IressCheckboxGroup, IressCheckbox } from '@iress-oss/ids-components';</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-10regm7-empty-line"><pre></pre></td><td class="css-vl0irh-content css-10regm7-empty-line"><pre class="css-o1u8iu-content-text"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-10regm7-empty-line"><pre></pre></td><td class="css-vl0irh-content css-10regm7-empty-line"><pre class="css-o1u8iu-content-text"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text">export const App = () => {</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text">const ConditionalFields = () => {</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> // We need to create our own state to manage the visibility of the fields, </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> // Instead of creating our own state, we can now use the form state via the useWatch hook, </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> // which means we have two sources of truth potentially making our code harder to maintain</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> // meaning we still have a single source of truth</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> const [show, setShow] = useState(['name']);</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> const show = IressForm.useWatch({ name: 'show'});</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-10regm7-empty-line"><pre></pre></td><td class="css-vl0irh-content css-10regm7-empty-line"><pre class="css-o1u8iu-content-text"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text"> return (</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressForm></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> <></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressField label="Show fields"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> <IressFormField </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressCheckboxGroup value={show} onChange={(newValues) => setShow(newValues)}></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> label="Show fields" </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> name="show"</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> render={(controlledProps) => (</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> <IressCheckboxGroup {...controlledProps}></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text"> <IressCheckbox value="name">Name</IressCheckbox></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text"> <IressCheckbox value="email">Email</IressCheckbox></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text"> </IressCheckboxGroup></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> </IressField></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> )}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> {show.includes('name') && (</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> /></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressField label="Name"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> {show?.includes('name') && (</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressInput name="name" /></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> <IressFormField </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> </IressField></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> label="Name" </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> )}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> name="name"</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> {show.includes('email') && (</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> render={(controlledProps) => <IressInput {...controlledProps} />}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressField label="Email"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> /></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressInput name="email" type="email" /></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> )}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> </IressField></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> {show?.includes('email') && (</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> )}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> <IressFormField </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> </IressForm></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> label="Email" </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> name="email"</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> render={(controlledProps) => <IressInput {...controlledProps} type="email" />}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> /></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> )}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> </></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text"> );</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text">};</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-10regm7-empty-line css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-10regm7-empty-line css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text">export const App = () => (</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> <IressForm defaultValues={{ show: ['name'] }}></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> <ConditionalFields /> </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> </IressForm></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text">);</pre></td></tr></tbody></table>
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
{
|
|
112
|
+
render: () \=> <DiffViewer allowModeChange oldValue\={\`import { IressForm, IressField, IressInput, IressCheckboxGroup, IressCheckbox } from '@iress/components';
|
|
113
|
+
export const App = () => {
|
|
114
|
+
// We need to create our own state to manage the visibility of the fields, // which means we have two sources of truth potentially making our code harder to maintain const \[show, setShow\] = useState(\['name'\]);
|
|
115
|
+
return ( <IressForm> <IressField label="Show fields"> <IressCheckboxGroup value={show} onChange={(newValues) => setShow(newValues)}> <IressCheckbox value="name">Name</IressCheckbox> <IressCheckbox value="email">Email</IressCheckbox> </IressCheckboxGroup> </IressField> {show.includes('name') && ( <IressField label="Name"> <IressInput name="name" /> </IressField> )} {show.includes('email') && ( <IressField label="Email"> <IressInput name="email" type="email" /> </IressField> )} </IressForm> );
|
|
116
|
+
};\`} newValue\={\`import { IressForm, IressFormField, IressInput, IressCheckboxGroup, IressCheckbox } from '@iress-oss/ids-components';
|
|
117
|
+
const ConditionalFields = () => {
|
|
118
|
+
// Instead of creating our own state, we can now use the form state via the useWatch hook, // meaning we still have a single source of truth const show = IressForm.useWatch({ name: 'show'});
|
|
119
|
+
return ( <> <IressFormField label="Show fields" name="show" render={(controlledProps) => ( <IressCheckboxGroup {...controlledProps}> <IressCheckbox value="name">Name</IressCheckbox> <IressCheckbox value="email">Email</IressCheckbox> </IressCheckboxGroup> )} /> {show?.includes('name') && ( <IressFormField label="Name" name="name" render={(controlledProps) => <IressInput {...controlledProps} />} /> )} {show?.includes('email') && ( <IressFormField label="Email" name="email" render={(controlledProps) => <IressInput {...controlledProps} type="email" />} /> )} </> );
|
|
120
|
+
};
|
|
121
|
+
export const App = () => (
|
|
122
|
+
<IressForm defaultValues={{ show: \['name'\] }}> <ConditionalFields /> </IressForm>
|
|
123
|
+
);\`} /\>
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### Props
|
|
129
|
+
|
|
130
|
+
| Name | Description | Default | Control |
|
|
131
|
+
| --- | --- | --- | --- |
|
|
132
|
+
| actions |
|
|
133
|
+
object
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
| \- | Choose option...nonesubmitsave |
|
|
138
|
+
| alert |
|
|
139
|
+
|
|
140
|
+
\-
|
|
141
|
+
|
|
142
|
+
| \- | Choose option...nonebasicAlert |
|
|
143
|
+
| children |
|
|
144
|
+
|
|
145
|
+
array
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
| \- | Choose option...nonesimplesupportedControlsreadOnly |
|
|
150
|
+
| Show React Hook Forms itemsReact Hook Forms | Show React Hook Forms items |
|
|
151
|
+
|
|
152
|
+
### Validation
|
|
153
|
+
|
|
154
|
+
Validation is now done declaratively using the `rules` prop on the `IressFormField` component. This is based on the [rules available in React Hook Forms](https://www.react-hook-form.com/api/useform/register/#options). This change was made to allow for more complex validation rules to be implemented.
|
|
155
|
+
|
|
156
|
+
Due to this change, there are a few things you should consider during development:
|
|
157
|
+
|
|
158
|
+
* If you want validation messages to be shown on form controls, you need to use the `IressFormField` component to wrap around the form control and set the `rules` prop. This will allow the form to manage the validation state of the field.
|
|
159
|
+
* Although you can still use props such as `maxLength` on `IressInput`, these no longer propagate to the form validation. You need to use the `rules` prop to set these validation rules as well, and rely on `maxLength` for the input to stop the user from entering more characters than allowed (a user experience improvement that we definitely recommend).
|
|
160
|
+
* You can no longer override default error messages for the whole form. To override the default messages, you must specify them in the `rules` prop per `IressFormField`.
|
|
161
|
+
|
|
162
|
+
See below an example comparing a version 4 and version 5 `IressForm` when adding validation rules.
|
|
163
|
+
|
|
164
|
+
[](./iframe.html?id=patterns-form--validation-v-4-to-v-5)
|
|
165
|
+
|
|
166
|
+
Diff
|
|
167
|
+
|
|
168
|
+
Old
|
|
169
|
+
|
|
170
|
+
New
|
|
171
|
+
|
|
172
|
+
<table class="css-1n5o7vh-diff-container"><tbody><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text">import { IressForm, IressField, IressInput, IressButton } from '@iress/components';</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text">import { IressForm, IressFormField, IressInput, IressButton } from '@iress-oss/ids-components';</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-10regm7-empty-line"><pre></pre></td><td class="css-vl0irh-content css-10regm7-empty-line"><pre class="css-o1u8iu-content-text"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text">export const App = () => (</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressForm valueMissing="{{fieldName}} needs to be filled in!"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> <IressForm></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressField label="Name"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> <IressFormField </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressInput name="name" required /></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> label="Name"</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> </IressField></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> name="name"</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressField label="Email"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> render={(controlledProps) => <IressInput {...controlledProps} />}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressInput name="email" maxLength={10} /></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> rules={{ required: 'Name needs to be filled in!' }}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> </IressField></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> /></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> <IressFormField </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> label="Email"</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> name="email"</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> render={(controlledProps) => <IressInput {...controlledProps} type="email" maxLength={10} />}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> rules={{ maxLength: 10 }}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> /></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text"> <IressButton type="submit" mode="primary"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text"> Sign up</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text"> </IressButton></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text"> </IressForm></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text">);</pre></td></tr></tbody></table>
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
{
|
|
177
|
+
render: () \=> <DiffViewer allowModeChange oldValue\={\`import { IressForm, IressField, IressInput, IressButton } from '@iress/components';
|
|
178
|
+
export const App = () => (
|
|
179
|
+
<IressForm valueMissing="{{fieldName}} needs to be filled in!"> <IressField label="Name"> <IressInput name="name" required /> </IressField> <IressField label="Email"> <IressInput name="email" maxLength={10} /> </IressField> <IressButton type="submit" mode="primary"> Sign up </IressButton> </IressForm>
|
|
180
|
+
);\`} newValue\={\`import { IressForm, IressFormField, IressInput, IressButton } from '@iress-oss/ids-components';
|
|
181
|
+
export const App = () => (
|
|
182
|
+
<IressForm> <IressFormField label="Name" name="name" render={(controlledProps) => <IressInput {...controlledProps} />} rules={{ required: 'Name needs to be filled in!' }} /> <IressFormField label="Email" name="email" render={(controlledProps) => <IressInput {...controlledProps} type="email" maxLength={10} />} rules={{ maxLength: 10 }} /> <IressButton type="submit" mode="primary"> Sign up </IressButton> </IressForm>
|
|
183
|
+
);\`} />
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
#### Props
|
|
189
|
+
|
|
190
|
+
| Name | Description | Default | Control |
|
|
191
|
+
| --- | --- | --- | --- |
|
|
192
|
+
| actions |
|
|
193
|
+
object
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
| \- | Choose option...nonesubmitsave |
|
|
198
|
+
| alert |
|
|
199
|
+
|
|
200
|
+
\-
|
|
201
|
+
|
|
202
|
+
| \- | Choose option...nonebasicAlert |
|
|
203
|
+
| children |
|
|
204
|
+
|
|
205
|
+
array
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
| \- | Choose option...nonesimplesupportedControlsreadOnly |
|
|
210
|
+
| Show React Hook Forms itemsReact Hook Forms | Show React Hook Forms items |
|
|
211
|
+
|
|
212
|
+
### Syncing state
|
|
213
|
+
|
|
214
|
+
For most scenarios, you should use the `onSubmit` event to sync the form data with other state management systems (eg. server, browser storage or state management libraries such as Redux). This event is emitted when the form passes validation, and contains a map of the field names and the data entered by the user.
|
|
215
|
+
|
|
216
|
+
For more complex scenarios, you may need to sync a field value before the form is submitted. In this case, you can use the `IressForm.useWatch` hook to watch the value of a field and sync it with your state.
|
|
217
|
+
|
|
218
|
+
Consider the following for your development:
|
|
219
|
+
|
|
220
|
+
* Only use other state management systems to fill out the form at the initial render using `defaultValues`. After that, use the `onSubmit` event to sync the form data with your state.
|
|
221
|
+
* If you need to set form with data coming from an external system, use the `ref` of the form to `reset` the form values.
|
|
222
|
+
|
|
223
|
+
const ref \= useRef<FormRef | null\>(null);
|
|
224
|
+
const api \= useApi();
|
|
225
|
+
const handleSubmit \= async (data) \=> {
|
|
226
|
+
// Sync the form data with your state
|
|
227
|
+
const details \= await api.updateUser(data);
|
|
228
|
+
// Update the form with the new data
|
|
229
|
+
ref.current?.reset(details);
|
|
230
|
+
};
|
|
231
|
+
return (
|
|
232
|
+
<IressForm onSubmit\={handleSubmit} ref\={ref}\>
|
|
233
|
+
... </IressForm\>
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Usage
|
|
239
|
+
-----
|
|
240
|
+
|
|
241
|
+
### Patterns
|
|
242
|
+
|
|
243
|
+
The `IressForm` component supports different patterns to ensure consistency in how forms are displayed depending on the context of the form.
|
|
244
|
+
|
|
245
|
+
1. `long`: This pattern is used when a form has more than 8 fields. It has the following characteristics:
|
|
246
|
+
* The `heading` and `actions` are displayed at the top of the form and can be `sticky`, ensuring they are always visible to the user.
|
|
247
|
+
* The validation errors are displayed when the user blurs out of a field (ie. moves to the next field), ensuring that the user is informed of any errors before submitting the form.
|
|
248
|
+
2. `short`: This is the default pattern and should be used when a form has 8 or fewer fields, usually for familiar data such as the user's login details. It has the following characteristics:
|
|
249
|
+
* The `heading` is displayed at the top of the for and the `actions` are displayed at the bottom of the form.
|
|
250
|
+
* The validation errors are displayed when the user submits the form to ensure that the user is not overwhelmed with errors when filling out the form.
|
|
251
|
+
|
|
252
|
+
**Note:** It is recommended to use the patterns above for new applications, or those doing an overhaul, as they provide a consistent user experience across forms. For older products, please follow the existing patterns in your application to ensure consistency with the rest of the product.
|
|
253
|
+
|
|
254
|
+
### Fields
|
|
255
|
+
|
|
256
|
+
Use the `IressFormField` component to create form fields. This component is a layout component that wraps around form controls such as `IressInput`. It provides a consistent layout for form fields, and hooks into the `IressForm` component to provide validation and error handling.
|
|
257
|
+
|
|
258
|
+
It has three required props:
|
|
259
|
+
|
|
260
|
+
* `name`: The name of the field, which will be used to identify the field in the form data.
|
|
261
|
+
* `label`: The label for the field.
|
|
262
|
+
* `render`: A render prop that renders the form control. It is passed an object containing the props to be spread onto the form control to allow it to be controlled by the form.
|
|
263
|
+
|
|
264
|
+
<IressFormField
|
|
265
|
+
name\="email"
|
|
266
|
+
label\="Email"
|
|
267
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} type\="email" />}
|
|
268
|
+
/>
|
|
269
|
+
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
#### Supported form controls
|
|
273
|
+
|
|
274
|
+
Here are some examples of how to use `IressFormField` with different form controls. If you are using a form control that has multiple inputs inside (for example, `IressCheckboxGroup`), you can use `IressFormFieldset`, which changes the HTML structure to use a `fieldset` and `legend` element to group the inputs.
|
|
275
|
+
|
|
276
|
+
[](./iframe.html?id=patterns-form--fields)
|
|
277
|
+
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
<ForwardedForm
|
|
281
|
+
actions\={<IressButton mode\="primary" type\="submit"\>Submit</IressButton\>}
|
|
282
|
+
heading\="All supported form fields"
|
|
283
|
+
pattern\="long"
|
|
284
|
+
\>
|
|
285
|
+
<IressText element\="p"\>
|
|
286
|
+
This form showcases all the different components you can use within{' '}
|
|
287
|
+
<code\>
|
|
288
|
+
IressFormField </code\>
|
|
289
|
+
. </IressText\>
|
|
290
|
+
<IressFormField
|
|
291
|
+
label\="Name"
|
|
292
|
+
name\="name"
|
|
293
|
+
render\={() \=> {}}
|
|
294
|
+
rules\={{
|
|
295
|
+
required: true
|
|
296
|
+
}}
|
|
297
|
+
/>
|
|
298
|
+
<IressFormField
|
|
299
|
+
label\="Date"
|
|
300
|
+
name\="date"
|
|
301
|
+
render\={() \=> {}}
|
|
302
|
+
rules\={{
|
|
303
|
+
required: true
|
|
304
|
+
}}
|
|
305
|
+
/>
|
|
306
|
+
<IressFormField
|
|
307
|
+
label\="Gender"
|
|
308
|
+
name\="gender"
|
|
309
|
+
render\={() \=> {}}
|
|
310
|
+
rules\={{
|
|
311
|
+
required: true
|
|
312
|
+
}}
|
|
313
|
+
/>
|
|
314
|
+
<IressFormField
|
|
315
|
+
label\="Gender with icons"
|
|
316
|
+
name\="gender-with-icons"
|
|
317
|
+
render\={() \=> {}}
|
|
318
|
+
rules\={{
|
|
319
|
+
required: true
|
|
320
|
+
}}
|
|
321
|
+
/>
|
|
322
|
+
<IressFormFieldset
|
|
323
|
+
label\="Hobbies"
|
|
324
|
+
name\="hobbies"
|
|
325
|
+
render\={() \=> {}}
|
|
326
|
+
rules\={{
|
|
327
|
+
required: 'Select at least one hobby'
|
|
328
|
+
}}
|
|
329
|
+
/>
|
|
330
|
+
<IressFormFieldset
|
|
331
|
+
label\="Food of preference"
|
|
332
|
+
name\="food"
|
|
333
|
+
render\={() \=> {}}
|
|
334
|
+
rules\={{
|
|
335
|
+
required: 'Select at least one food of preference'
|
|
336
|
+
}}
|
|
337
|
+
/>
|
|
338
|
+
<IressFormField
|
|
339
|
+
label\="Terms and privacy policy"
|
|
340
|
+
name\="terms"
|
|
341
|
+
render\={() \=> {}}
|
|
342
|
+
rules\={{
|
|
343
|
+
required: 'You need to agree to the terms and conditions before proceeding'
|
|
344
|
+
}}
|
|
345
|
+
/>
|
|
346
|
+
<IressFormField
|
|
347
|
+
hint\="Type to copy an existing character's name"
|
|
348
|
+
label\="Your star wars name"
|
|
349
|
+
name\="star\_wars\_name"
|
|
350
|
+
render\={() \=> {}}
|
|
351
|
+
rules\={{
|
|
352
|
+
required: true
|
|
353
|
+
}}
|
|
354
|
+
/>
|
|
355
|
+
<IressFormField
|
|
356
|
+
label\="What's your pain level?"
|
|
357
|
+
name\="pain\_level"
|
|
358
|
+
render\={() \=> {}}
|
|
359
|
+
rules\={{
|
|
360
|
+
required: true
|
|
361
|
+
}}
|
|
362
|
+
/>
|
|
363
|
+
<IressFormField
|
|
364
|
+
label\="Favourite email domains"
|
|
365
|
+
name\="email\_domains"
|
|
366
|
+
render\={() \=> {}}
|
|
367
|
+
rules\={{
|
|
368
|
+
required: true
|
|
369
|
+
}}
|
|
370
|
+
/>
|
|
371
|
+
</ForwardedForm\>
|
|
372
|
+
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
#### IressFormField props
|
|
376
|
+
|
|
377
|
+
| Name | Description | Default |
|
|
378
|
+
| --- | --- | --- |
|
|
379
|
+
| actions |
|
|
380
|
+
object
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
| \- |
|
|
385
|
+
| alert |
|
|
386
|
+
|
|
387
|
+
\-
|
|
388
|
+
|
|
389
|
+
| \- |
|
|
390
|
+
| children |
|
|
391
|
+
|
|
392
|
+
array
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
| \- |
|
|
397
|
+
| heading |
|
|
398
|
+
|
|
399
|
+
string
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
| \- |
|
|
404
|
+
| pattern |
|
|
405
|
+
|
|
406
|
+
string
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
| \- |
|
|
411
|
+
| Show React Hook Forms itemsReact Hook Forms | Show React Hook Forms items |
|
|
412
|
+
|
|
413
|
+
### Rules
|
|
414
|
+
|
|
415
|
+
Use the `rules` prop on the `IressFormField` component to add validation rules. These are based on the [rules available in React Hook Forms](https://www.react-hook-form.com/api/useform/register/#options). The following rules are supported.
|
|
416
|
+
|
|
417
|
+
**Note:** In version 5, you can no longer override default error messages for the whole form. To override the default messages, you must specify them in the `rules` prop per `IressFormField`.
|
|
418
|
+
|
|
419
|
+
#### `required`
|
|
420
|
+
|
|
421
|
+
A boolean which, if `true`, indicates that the input must have a value before the form can be submitted. You can assign a string to return a custom error message.
|
|
422
|
+
|
|
423
|
+
[](./iframe.html?id=patterns-form-rules--required)
|
|
424
|
+
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
<ForwardedForm\>
|
|
428
|
+
<IressFormField
|
|
429
|
+
hint\=""
|
|
430
|
+
label\="Default message"
|
|
431
|
+
name\="IressInput-default"
|
|
432
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
433
|
+
rules\={{
|
|
434
|
+
required: true
|
|
435
|
+
}}
|
|
436
|
+
/>
|
|
437
|
+
<IressFormField
|
|
438
|
+
hint\=""
|
|
439
|
+
label\="Custom message"
|
|
440
|
+
name\="IressInput-custom"
|
|
441
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
442
|
+
rules\={{
|
|
443
|
+
required: 'Please check this field'
|
|
444
|
+
}}
|
|
445
|
+
/>
|
|
446
|
+
<IressButton
|
|
447
|
+
mode\="primary"
|
|
448
|
+
type\="submit"
|
|
449
|
+
\>
|
|
450
|
+
Validate </IressButton\>
|
|
451
|
+
</ForwardedForm\>
|
|
452
|
+
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
#### Adjust
|
|
456
|
+
|
|
457
|
+
| Name | Description | Default | Control |
|
|
458
|
+
| --- | --- | --- | --- |
|
|
459
|
+
| Show Storybook only itemsStorybook only | Show Storybook only items |
|
|
460
|
+
|
|
461
|
+
#### `maxLength`
|
|
462
|
+
|
|
463
|
+
The maximum character length of the value to accept for this input.
|
|
464
|
+
|
|
465
|
+
**Notes**
|
|
466
|
+
|
|
467
|
+
* For `IressInput`, you should also set the `maxLength` to stop the user from entering more characters than allowed.
|
|
468
|
+
* Only applies to: `IressAutocomplete`, `IressInput`, `IressRadioGroup` and `IressSelect`.
|
|
469
|
+
|
|
470
|
+
[](./iframe.html?id=patterns-form-rules--max-length)
|
|
471
|
+
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
<ForwardedForm\>
|
|
475
|
+
<IressFormField
|
|
476
|
+
hint\="Enter a maximum length of 5 characters"
|
|
477
|
+
label\="Default message"
|
|
478
|
+
name\="IressInput-default"
|
|
479
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
480
|
+
rules\={{
|
|
481
|
+
maxLength: 5
|
|
482
|
+
}}
|
|
483
|
+
/>
|
|
484
|
+
<IressFormField
|
|
485
|
+
hint\="Enter a maximum length of 5 characters"
|
|
486
|
+
label\="Custom message"
|
|
487
|
+
name\="IressInput-custom"
|
|
488
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
489
|
+
rules\={{
|
|
490
|
+
maxLength: {
|
|
491
|
+
message: 'Please enter a max length of 5 characters!',
|
|
492
|
+
value: 5
|
|
493
|
+
}
|
|
494
|
+
}}
|
|
495
|
+
/>
|
|
496
|
+
<IressButton
|
|
497
|
+
mode\="primary"
|
|
498
|
+
type\="submit"
|
|
499
|
+
\>
|
|
500
|
+
Validate </IressButton\>
|
|
501
|
+
</ForwardedForm\>
|
|
502
|
+
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
#### Adjust
|
|
506
|
+
|
|
507
|
+
| Name | Description | Default | Control |
|
|
508
|
+
| --- | --- | --- | --- |
|
|
509
|
+
| Show Storybook only itemsStorybook only | Show Storybook only items |
|
|
510
|
+
|
|
511
|
+
#### `minLength`
|
|
512
|
+
|
|
513
|
+
The minimum character length of the value to accept for this input.
|
|
514
|
+
|
|
515
|
+
**Notes**
|
|
516
|
+
|
|
517
|
+
* For `IressInput`, you should also set the `minLength` to stop the user from entering more characters than allowed.
|
|
518
|
+
* Only applies to: `IressAutocomplete`, `IressInput`, `IressRadioGroup` and `IressSelect`.
|
|
519
|
+
|
|
520
|
+
[](./iframe.html?id=patterns-form-rules--min-length)
|
|
521
|
+
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
<ForwardedForm\>
|
|
525
|
+
<IressFormField
|
|
526
|
+
hint\="Enter a minimum length of 7 characters"
|
|
527
|
+
label\="Default message"
|
|
528
|
+
name\="IressInput-default"
|
|
529
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
530
|
+
rules\={{
|
|
531
|
+
minLength: 7
|
|
532
|
+
}}
|
|
533
|
+
/>
|
|
534
|
+
<IressFormField
|
|
535
|
+
hint\="Enter a minimum length of 7 characters"
|
|
536
|
+
label\="Custom message"
|
|
537
|
+
name\="IressInput-custom"
|
|
538
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
539
|
+
rules\={{
|
|
540
|
+
minLength: {
|
|
541
|
+
message: 'Please enter a min length of 7 characters!',
|
|
542
|
+
value: 7
|
|
543
|
+
}
|
|
544
|
+
}}
|
|
545
|
+
/>
|
|
546
|
+
<IressButton
|
|
547
|
+
mode\="primary"
|
|
548
|
+
type\="submit"
|
|
549
|
+
\>
|
|
550
|
+
Validate </IressButton\>
|
|
551
|
+
</ForwardedForm\>
|
|
552
|
+
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
#### Adjust
|
|
556
|
+
|
|
557
|
+
| Name | Description | Default | Control |
|
|
558
|
+
| --- | --- | --- | --- |
|
|
559
|
+
| Show Storybook only itemsStorybook only | Show Storybook only items |
|
|
560
|
+
|
|
561
|
+
#### `max`
|
|
562
|
+
|
|
563
|
+
The maximum number to accept for this input.
|
|
564
|
+
|
|
565
|
+
**Notes**
|
|
566
|
+
|
|
567
|
+
* Only applies to: `IressAutocomplete`, `IressInput`, `IressRadioGroup` and `IressSelect`.
|
|
568
|
+
|
|
569
|
+
[](./iframe.html?id=patterns-form-rules--max)
|
|
570
|
+
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
<ForwardedForm\>
|
|
574
|
+
<IressFormField
|
|
575
|
+
hint\="Select a maximum of 2"
|
|
576
|
+
label\="Default message"
|
|
577
|
+
name\="IressInput-default"
|
|
578
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
579
|
+
rules\={{
|
|
580
|
+
max: 2
|
|
581
|
+
}}
|
|
582
|
+
/>
|
|
583
|
+
<IressFormField
|
|
584
|
+
hint\="Select a maximum of 2"
|
|
585
|
+
label\="Custom message"
|
|
586
|
+
name\="IressInput-custom"
|
|
587
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
588
|
+
rules\={{
|
|
589
|
+
max: {
|
|
590
|
+
message: 'Please enter a max of 2!',
|
|
591
|
+
value: 2
|
|
592
|
+
}
|
|
593
|
+
}}
|
|
594
|
+
/>
|
|
595
|
+
<IressButton
|
|
596
|
+
mode\="primary"
|
|
597
|
+
type\="submit"
|
|
598
|
+
\>
|
|
599
|
+
Validate </IressButton\>
|
|
600
|
+
</ForwardedForm\>
|
|
601
|
+
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
#### Adjust
|
|
605
|
+
|
|
606
|
+
| Name | Description | Default | Control |
|
|
607
|
+
| --- | --- | --- | --- |
|
|
608
|
+
| Show Storybook only itemsStorybook only | Show Storybook only items |
|
|
609
|
+
|
|
610
|
+
#### `min`
|
|
611
|
+
|
|
612
|
+
The minimum number to accept for this input.
|
|
613
|
+
|
|
614
|
+
**Notes**
|
|
615
|
+
|
|
616
|
+
* Only applies to: `IressAutocomplete`, `IressInput`, `IressRadioGroup` and `IressSelect`.
|
|
617
|
+
|
|
618
|
+
[](./iframe.html?id=patterns-form-rules--min)
|
|
619
|
+
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
<ForwardedForm\>
|
|
623
|
+
<IressFormField
|
|
624
|
+
hint\="Select a minimum of 20"
|
|
625
|
+
label\="Default message"
|
|
626
|
+
name\="IressInput-default"
|
|
627
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
628
|
+
rules\={{
|
|
629
|
+
min: 20
|
|
630
|
+
}}
|
|
631
|
+
/>
|
|
632
|
+
<IressFormField
|
|
633
|
+
hint\="Select a minimum of 20"
|
|
634
|
+
label\="Custom message"
|
|
635
|
+
name\="IressInput-custom"
|
|
636
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
637
|
+
rules\={{
|
|
638
|
+
min: {
|
|
639
|
+
message: 'Please enter a min of 20!',
|
|
640
|
+
value: 20
|
|
641
|
+
}
|
|
642
|
+
}}
|
|
643
|
+
/>
|
|
644
|
+
<IressButton
|
|
645
|
+
mode\="primary"
|
|
646
|
+
type\="submit"
|
|
647
|
+
\>
|
|
648
|
+
Validate </IressButton\>
|
|
649
|
+
</ForwardedForm\>
|
|
650
|
+
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
#### Adjust
|
|
654
|
+
|
|
655
|
+
| Name | Description | Default | Control |
|
|
656
|
+
| --- | --- | --- | --- |
|
|
657
|
+
| Show Storybook only itemsStorybook only | Show Storybook only items |
|
|
658
|
+
|
|
659
|
+
#### `pattern`
|
|
660
|
+
|
|
661
|
+
The accepted regex pattern for the input.
|
|
662
|
+
|
|
663
|
+
**Notes**
|
|
664
|
+
|
|
665
|
+
* Only applies to: `IressAutocomplete`, `IressInput`, `IressRadioGroup` and `IressSelect`.
|
|
666
|
+
|
|
667
|
+
[](./iframe.html?id=patterns-form-rules--pattern)
|
|
668
|
+
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
<ForwardedForm\>
|
|
672
|
+
<IressFormField
|
|
673
|
+
hint\="Enter a valid email address"
|
|
674
|
+
label\="Default message"
|
|
675
|
+
name\="IressInput-default"
|
|
676
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
677
|
+
rules\={{
|
|
678
|
+
pattern: /^\[a-zA-Z0-9.!#$%&'\*+/=?^\_\`{|}~-\]+@\[a-zA-Z0-9\](?:\[a-zA-Z0-9-\]{0,61}\[a-zA-Z0-9\])?(?:\\.\[a-zA-Z0-9\](?:\[a-zA-Z0-9-\]{0,61}\[a-zA-Z0-9\])?)\*$/
|
|
679
|
+
}}
|
|
680
|
+
/>
|
|
681
|
+
<IressFormField hint="Enter a valid email address" label="Custom message" name="IressInput-custom" render={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
682
|
+
rules={{
|
|
683
|
+
pattern: {
|
|
684
|
+
message: 'Please enter a valid email address!',
|
|
685
|
+
value: /^\[a-zA-Z0-9.!#$%&'\*+/=?^\_\`{|}~-\]+@\[a-zA-Z0-9\](?:\[a-zA-Z0-9-\]{0,61}\[a-zA-Z0-9\])?(?:\\.\[a-zA-Z0-9\](?:\[a-zA-Z0-9-\]{0,61}\[a-zA-Z0-9\])?)\*$/
|
|
686
|
+
}
|
|
687
|
+
}}
|
|
688
|
+
/> <IressButton
|
|
689
|
+
mode\="primary"
|
|
690
|
+
type\="submit"
|
|
691
|
+
\>
|
|
692
|
+
Validate </IressButton\>
|
|
693
|
+
</ForwardedForm\>
|
|
694
|
+
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
#### Adjust
|
|
698
|
+
|
|
699
|
+
| Name | Description | Default | Control |
|
|
700
|
+
| --- | --- | --- | --- |
|
|
701
|
+
| Show Storybook only itemsStorybook only | Show Storybook only items |
|
|
702
|
+
|
|
703
|
+
#### `minDate`
|
|
704
|
+
|
|
705
|
+
The minimum date to accept for this input.
|
|
706
|
+
|
|
707
|
+
**Note:** This is a custom rule created for `IressForm` and its sub-components. It will translate the rule into a `validate` rule for react-hook-forms. It will not work with a `validate` function, only if you set the `validate` prop to an `object` of functions.
|
|
708
|
+
|
|
709
|
+
[](./iframe.html?id=patterns-form-rules--min-date)
|
|
710
|
+
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
{
|
|
714
|
+
...Default,
|
|
715
|
+
args: {
|
|
716
|
+
element: 'IressInputDate',
|
|
717
|
+
field: {
|
|
718
|
+
hint: 'Enter a date after today'
|
|
719
|
+
},
|
|
720
|
+
rules: {
|
|
721
|
+
minDate: new Date()
|
|
722
|
+
},
|
|
723
|
+
customRules: {
|
|
724
|
+
minDate: {
|
|
725
|
+
value: new Date(),
|
|
726
|
+
message: 'Please enter a date after today!'
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
#### Props
|
|
735
|
+
|
|
736
|
+
| Name | Description | Default | Control |
|
|
737
|
+
| --- | --- | --- | --- |
|
|
738
|
+
| children |
|
|
739
|
+
\-
|
|
740
|
+
|
|
741
|
+
| \- | \- |
|
|
742
|
+
| Show Storybook only itemsStorybook only | Show Storybook only items |
|
|
743
|
+
|
|
744
|
+
#### `maxDate`
|
|
745
|
+
|
|
746
|
+
The maximum date to accept for this input.
|
|
747
|
+
|
|
748
|
+
**Note:** This is a custom rule created for `IressForm` and its sub-components. It will translate the rule into a `validate` rule for react-hook-forms. It will not work with a `validate` function, only if you set the `validate` prop to an `object` of functions.
|
|
749
|
+
|
|
750
|
+
[](./iframe.html?id=patterns-form-rules--max-date)
|
|
751
|
+
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
{
|
|
755
|
+
...Default,
|
|
756
|
+
args: {
|
|
757
|
+
element: 'IressInputDate',
|
|
758
|
+
field: {
|
|
759
|
+
hint: 'Enter a date before today'
|
|
760
|
+
},
|
|
761
|
+
rules: {
|
|
762
|
+
maxDate: new Date()
|
|
763
|
+
},
|
|
764
|
+
customRules: {
|
|
765
|
+
maxDate: {
|
|
766
|
+
value: new Date(),
|
|
767
|
+
message: 'Please enter a date before today!'
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
#### Props
|
|
776
|
+
|
|
777
|
+
| Name | Description | Default | Control |
|
|
778
|
+
| --- | --- | --- | --- |
|
|
779
|
+
| children |
|
|
780
|
+
\-
|
|
781
|
+
|
|
782
|
+
| \- | \- |
|
|
783
|
+
| Show Storybook only itemsStorybook only | Show Storybook only items |
|
|
784
|
+
|
|
785
|
+
#### `email`
|
|
786
|
+
|
|
787
|
+
Ensures the input is a valid email address.
|
|
788
|
+
|
|
789
|
+
**Note:** This is a custom rule created for `IressForm` and its sub-components. It will translate the rule into a `validate` rule for react-hook-forms. It will not work with a `validate` function, only if you set the `validate` prop to an `object` of functions.
|
|
790
|
+
|
|
791
|
+
[](./iframe.html?id=patterns-form-rules--email)
|
|
792
|
+
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
<ForwardedForm\>
|
|
796
|
+
<IressFormField
|
|
797
|
+
hint\="Enter an email address"
|
|
798
|
+
label\="Default message"
|
|
799
|
+
name\="IressInput-default"
|
|
800
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
801
|
+
rules\={{
|
|
802
|
+
email: true
|
|
803
|
+
}}
|
|
804
|
+
/>
|
|
805
|
+
<IressFormField
|
|
806
|
+
hint\="Enter an email address"
|
|
807
|
+
label\="Custom message"
|
|
808
|
+
name\="IressInput-custom"
|
|
809
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
810
|
+
rules\={{
|
|
811
|
+
email: 'Please enter a valid email address!'
|
|
812
|
+
}}
|
|
813
|
+
/>
|
|
814
|
+
<IressButton
|
|
815
|
+
mode\="primary"
|
|
816
|
+
type\="submit"
|
|
817
|
+
\>
|
|
818
|
+
Validate </IressButton\>
|
|
819
|
+
</ForwardedForm\>
|
|
820
|
+
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
#### Props
|
|
824
|
+
|
|
825
|
+
| Name | Description | Default | Control |
|
|
826
|
+
| --- | --- | --- | --- |
|
|
827
|
+
| children |
|
|
828
|
+
\-
|
|
829
|
+
|
|
830
|
+
| \- | \- |
|
|
831
|
+
| Show Storybook only itemsStorybook only | Show Storybook only items |
|
|
832
|
+
|
|
833
|
+
#### `validate`
|
|
834
|
+
|
|
835
|
+
You can pass a callback function as the argument to validate, or you can pass an object of callback functions to validate against all of them. This function will be executed on its own without depending on other validation rules included.
|
|
836
|
+
|
|
837
|
+
**Notes**
|
|
838
|
+
|
|
839
|
+
* for `object` or `array` input data, it's recommended to use the validate function for validation as the other rules mostly apply to `string`, `string[]`, `number` and `boolean` data types.
|
|
840
|
+
|
|
841
|
+
[](./iframe.html?id=patterns-form-rules--validate)
|
|
842
|
+
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
<ForwardedForm\>
|
|
846
|
+
<IressFormFieldset
|
|
847
|
+
hint\="If checkbox, click two items to pass. Anything else, make sure it is Google."
|
|
848
|
+
label\="Default message"
|
|
849
|
+
name\="IressCheckboxGroup-default"
|
|
850
|
+
render\={(controlledProps) \=> (
|
|
851
|
+
<IressCheckboxGroup {...controlledProps}\>
|
|
852
|
+
<IressCheckbox value\="reading"\>Reading</IressCheckbox\>
|
|
853
|
+
<IressCheckbox value\="writing"\>Writing</IressCheckbox\>
|
|
854
|
+
</IressCheckboxGroup\>
|
|
855
|
+
)}
|
|
856
|
+
rules\={{
|
|
857
|
+
validate: {
|
|
858
|
+
atLeastTwoItems: (value) \=> Array.isArray(value) ? value.length \>= 2 : true,
|
|
859
|
+
isGoogle: (value) \=> Array.isArray(value) ? true : value \=== "Google"
|
|
860
|
+
}
|
|
861
|
+
}}
|
|
862
|
+
/>
|
|
863
|
+
<IressFormFieldset
|
|
864
|
+
hint\="If checkbox, click two items to pass. Anything else, make sure it is Google."
|
|
865
|
+
label\="Custom message"
|
|
866
|
+
name\="IressCheckboxGroup-custom"
|
|
867
|
+
render\={(controlledProps) \=> (
|
|
868
|
+
<IressCheckboxGroup {...controlledProps}\>
|
|
869
|
+
<IressCheckbox value\="reading"\>Reading</IressCheckbox\>
|
|
870
|
+
<IressCheckbox value\="writing"\>Writing</IressCheckbox\>
|
|
871
|
+
</IressCheckboxGroup\>
|
|
872
|
+
)}
|
|
873
|
+
rules\={{
|
|
874
|
+
validate: {
|
|
875
|
+
atLeastTwoItems: (value) \=> (Array.isArray(value) ? value.length \>= 2 : true) || "Please select at least two items!",
|
|
876
|
+
isGoogle: (value) \=> (Array.isArray(value) ? true : value \=== "Google") || "I guess you are not evil!"
|
|
877
|
+
}
|
|
878
|
+
}}
|
|
879
|
+
/>
|
|
880
|
+
<IressButton
|
|
881
|
+
mode\="primary"
|
|
882
|
+
type\="submit"
|
|
883
|
+
\>
|
|
884
|
+
Validate </IressButton\>
|
|
885
|
+
</ForwardedForm\>
|
|
886
|
+
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
#### Props
|
|
890
|
+
|
|
891
|
+
| Name | Description | Default | Control |
|
|
892
|
+
| --- | --- | --- | --- |
|
|
893
|
+
| children |
|
|
894
|
+
\-
|
|
895
|
+
|
|
896
|
+
| \- | \- |
|
|
897
|
+
| Show Storybook only itemsStorybook only | Show Storybook only items |
|
|
898
|
+
|
|
899
|
+
### Handling submission
|
|
900
|
+
|
|
901
|
+
When the form passes validation (if not disabled), the `onSubmit` event is emitted. Its event details contain a map of the field names and the data entered by the user.
|
|
902
|
+
|
|
903
|
+
[](./iframe.html?id=patterns-form--handling-submission)
|
|
904
|
+
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
import {
|
|
908
|
+
IressTable,
|
|
909
|
+
type IressFormProps,
|
|
910
|
+
IressForm,
|
|
911
|
+
IressModal,
|
|
912
|
+
IressFormField,
|
|
913
|
+
IressInput,
|
|
914
|
+
IressButton,
|
|
915
|
+
} from '@iress-oss/ids-components';
|
|
916
|
+
import { useState } from 'react';
|
|
917
|
+
interface FieldValues {
|
|
918
|
+
name?: string;
|
|
919
|
+
email?: string;
|
|
920
|
+
}
|
|
921
|
+
export const FormSubmission \= () \=> {
|
|
922
|
+
const \[showModal, setShowModal\] \= useState(false);
|
|
923
|
+
const \[submitted, setSubmitted\] \= useState<FieldValues | undefined\>(
|
|
924
|
+
undefined,
|
|
925
|
+
);
|
|
926
|
+
return (
|
|
927
|
+
<IressForm
|
|
928
|
+
{...{
|
|
929
|
+
pattern: 'short',
|
|
930
|
+
}}
|
|
931
|
+
onSubmit\={(data) \=> {
|
|
932
|
+
setSubmitted(data);
|
|
933
|
+
setShowModal(true);
|
|
934
|
+
}}
|
|
935
|
+
\>
|
|
936
|
+
<IressFormField
|
|
937
|
+
label\="Name"
|
|
938
|
+
name\="name"
|
|
939
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
940
|
+
rules\={{
|
|
941
|
+
required: 'Name is required',
|
|
942
|
+
}}
|
|
943
|
+
/>
|
|
944
|
+
<IressFormField
|
|
945
|
+
label\="Email address"
|
|
946
|
+
name\="email"
|
|
947
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
948
|
+
rules\={{
|
|
949
|
+
minLength: {
|
|
950
|
+
message: 'Use a longer email address',
|
|
951
|
+
value: 6,
|
|
952
|
+
},
|
|
953
|
+
required: 'Email is required',
|
|
954
|
+
}}
|
|
955
|
+
/>
|
|
956
|
+
<IressButton mode\="primary" type\="submit"\>
|
|
957
|
+
Sign up </IressButton\>
|
|
958
|
+
<IressModal
|
|
959
|
+
show\={showModal}
|
|
960
|
+
onShowChange\={setShowModal}
|
|
961
|
+
onExited\={() \=> setSubmitted(undefined)}
|
|
962
|
+
\>
|
|
963
|
+
<IressTable
|
|
964
|
+
caption\="Submitted details"
|
|
965
|
+
rows\={Object.entries(submitted ?? {}).map((entry) \=> ({
|
|
966
|
+
name: entry\[0\],
|
|
967
|
+
value: JSON.stringify(entry\[1\], null, 2),
|
|
968
|
+
}))}
|
|
969
|
+
/>
|
|
970
|
+
</IressModal\>
|
|
971
|
+
</IressForm\>
|
|
972
|
+
);
|
|
973
|
+
};
|
|
974
|
+
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
#### Props
|
|
978
|
+
|
|
979
|
+
| Name | Description | Default | Control |
|
|
980
|
+
| --- | --- | --- | --- |
|
|
981
|
+
| actions |
|
|
982
|
+
object
|
|
983
|
+
|
|
984
|
+
|
|
985
|
+
|
|
986
|
+
| \- | Choose option...nonesubmitsave |
|
|
987
|
+
| alert |
|
|
988
|
+
|
|
989
|
+
\-
|
|
990
|
+
|
|
991
|
+
| \- | Choose option...nonebasicAlert |
|
|
992
|
+
| children |
|
|
993
|
+
|
|
994
|
+
array
|
|
995
|
+
|
|
996
|
+
|
|
997
|
+
|
|
998
|
+
| \- | \- |
|
|
999
|
+
| onSubmit |
|
|
1000
|
+
|
|
1001
|
+
\-
|
|
1002
|
+
|
|
1003
|
+
| \- | \- |
|
|
1004
|
+
| pattern |
|
|
1005
|
+
|
|
1006
|
+
string
|
|
1007
|
+
|
|
1008
|
+
|
|
1009
|
+
|
|
1010
|
+
| \- | short |
|
|
1011
|
+
| Show React Hook Forms itemsReact Hook Forms | Show React Hook Forms items |
|
|
1012
|
+
|
|
1013
|
+
Behaviour
|
|
1014
|
+
---------
|
|
1015
|
+
|
|
1016
|
+
* Initial form validation is done when the user first submits the form. This allows them to focus on entering data without being overwhelmed by validation errors.
|
|
1017
|
+
* If there are validation errors on submission, they will be shown at the form level as a summary, as well as per field. Only the first failing error will be displayed per field.
|
|
1018
|
+
* After the first submission, fields are validated on change, to provide users instant feedback as they are now at the validation phase.
|
|
1019
|
+
|
|
1020
|
+
**Note:** The default user experience regarding validation is different to previous versions of IDS. This change was done to align IDS with the typical user experience found in other applications. If you would like to change the behaviour to be more consistent with the original IDS, set the `mode` prop of the form to `onBlur`.
|
|
1021
|
+
|
|
1022
|
+
Examples
|
|
1023
|
+
--------
|
|
1024
|
+
|
|
1025
|
+
### Pre-fill the form
|
|
1026
|
+
|
|
1027
|
+
You can set the `defaultValues` prop to pre-fill the form values.
|
|
1028
|
+
|
|
1029
|
+
[](./iframe.html?id=patterns-form--default-values)
|
|
1030
|
+
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
<ForwardedForm
|
|
1034
|
+
actions\={<IressButton mode\="primary" type\="submit"\>Submit</IressButton\>}
|
|
1035
|
+
defaultValues\={{
|
|
1036
|
+
email: 'luke.skywalker@iress.com',
|
|
1037
|
+
name: 'Luke Skywalker'
|
|
1038
|
+
}}
|
|
1039
|
+
pattern\="short"
|
|
1040
|
+
\>
|
|
1041
|
+
<IressText element\="p"\>
|
|
1042
|
+
This form is a simple example of how to use{' '}
|
|
1043
|
+
<code\>
|
|
1044
|
+
IressFormField </code\>
|
|
1045
|
+
{' '}with{' '}
|
|
1046
|
+
<code\>
|
|
1047
|
+
IressInput </code\>
|
|
1048
|
+
. </IressText\>
|
|
1049
|
+
<IressFormField
|
|
1050
|
+
label\="Name"
|
|
1051
|
+
name\="name"
|
|
1052
|
+
render\={() \=> {}}
|
|
1053
|
+
rules\={{
|
|
1054
|
+
required: 'Name is required'
|
|
1055
|
+
}}
|
|
1056
|
+
/>
|
|
1057
|
+
<IressFormField
|
|
1058
|
+
label\="Email address"
|
|
1059
|
+
name\="email"
|
|
1060
|
+
render\={() \=> {}}
|
|
1061
|
+
rules\={{
|
|
1062
|
+
minLength: {
|
|
1063
|
+
message: 'Use a longer email address',
|
|
1064
|
+
value: 6
|
|
1065
|
+
},
|
|
1066
|
+
required: 'Email is required'
|
|
1067
|
+
}}
|
|
1068
|
+
/>
|
|
1069
|
+
</ForwardedForm\>
|
|
1070
|
+
|
|
1071
|
+
```
|
|
1072
|
+
|
|
1073
|
+
#### Props
|
|
1074
|
+
|
|
1075
|
+
| Name | Description | Default | Control |
|
|
1076
|
+
| --- | --- | --- | --- |
|
|
1077
|
+
| actions |
|
|
1078
|
+
object
|
|
1079
|
+
|
|
1080
|
+
|
|
1081
|
+
|
|
1082
|
+
| \- | Choose option...nonesubmitsave |
|
|
1083
|
+
| alert |
|
|
1084
|
+
|
|
1085
|
+
\-
|
|
1086
|
+
|
|
1087
|
+
| \- | Choose option...nonebasicAlert |
|
|
1088
|
+
| children |
|
|
1089
|
+
|
|
1090
|
+
array
|
|
1091
|
+
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
| \- | Choose option...nonesimplesupportedControlsreadOnly |
|
|
1095
|
+
| pattern |
|
|
1096
|
+
|
|
1097
|
+
string
|
|
1098
|
+
|
|
1099
|
+
|
|
1100
|
+
|
|
1101
|
+
| \- | short |
|
|
1102
|
+
| Show React Hook Forms itemsReact Hook Forms | Show React Hook Forms items |
|
|
1103
|
+
|
|
1104
|
+
### Custom error handling
|
|
1105
|
+
|
|
1106
|
+
The `onError` prop allows you to listen to any field errors. It takes two arguments. The first is a map of the field name and an object containing the first error message and type. The second is a ref to the original element that caused the error (the ref of the underlying input).
|
|
1107
|
+
|
|
1108
|
+
One use case for this prop is to create your own visible error summary at the top of the form, or to log errors to an external service.
|
|
1109
|
+
|
|
1110
|
+
[](./iframe.html?id=patterns-form--custom-error-handling)
|
|
1111
|
+
|
|
1112
|
+
```
|
|
1113
|
+
|
|
1114
|
+
import {
|
|
1115
|
+
IressTable,
|
|
1116
|
+
type IressFormProps,
|
|
1117
|
+
IressForm,
|
|
1118
|
+
IressModal,
|
|
1119
|
+
IressStack,
|
|
1120
|
+
IressFormField,
|
|
1121
|
+
IressInput,
|
|
1122
|
+
IressButton,
|
|
1123
|
+
IressText,
|
|
1124
|
+
} from '@iress-oss/ids-components';
|
|
1125
|
+
import { useState } from 'react';
|
|
1126
|
+
import { type FieldErrors } from 'react-hook-form';
|
|
1127
|
+
interface FieldValues {
|
|
1128
|
+
name?: string;
|
|
1129
|
+
email?: string;
|
|
1130
|
+
}
|
|
1131
|
+
export const CustomErrorHandlingForm \= () \=> {
|
|
1132
|
+
const \[errors, setErrors\] \= useState<FieldErrors<FieldValues\> | undefined\>(
|
|
1133
|
+
undefined,
|
|
1134
|
+
);
|
|
1135
|
+
return (
|
|
1136
|
+
<IressForm {...{
|
|
1137
|
+
pattern: 'short',
|
|
1138
|
+
}} onError\={(data) \=> setErrors(data)}\>
|
|
1139
|
+
<IressText mb\="md"\>
|
|
1140
|
+
<h2\>Custom error handling</h2\>
|
|
1141
|
+
<p\>
|
|
1142
|
+
Demonstrates usage of the <code\>onError</code\> prop to show a modal when there are issues with the form. </p\>
|
|
1143
|
+
</IressText\>
|
|
1144
|
+
<IressFormField
|
|
1145
|
+
label\="Name"
|
|
1146
|
+
name\="name"
|
|
1147
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
1148
|
+
rules\={{
|
|
1149
|
+
required: 'Name is required',
|
|
1150
|
+
}}
|
|
1151
|
+
/>
|
|
1152
|
+
<IressFormField
|
|
1153
|
+
label\="Email address"
|
|
1154
|
+
name\="email"
|
|
1155
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
1156
|
+
rules\={{
|
|
1157
|
+
minLength: {
|
|
1158
|
+
message: 'Use a longer email address',
|
|
1159
|
+
value: 6,
|
|
1160
|
+
},
|
|
1161
|
+
required: 'Email is required',
|
|
1162
|
+
}}
|
|
1163
|
+
/>
|
|
1164
|
+
<IressButton mode\="primary" type\="submit"\>
|
|
1165
|
+
Sign up </IressButton\>
|
|
1166
|
+
<IressModal
|
|
1167
|
+
show\={!!errors}
|
|
1168
|
+
onShowChange\={(show) \=> !show && setErrors(undefined)}
|
|
1169
|
+
\>
|
|
1170
|
+
<IressTable
|
|
1171
|
+
caption\="Errors"
|
|
1172
|
+
rows\={Object.entries(errors ?? {}).map((\[name, errorDetails\]) \=> ({
|
|
1173
|
+
name,
|
|
1174
|
+
errorDetails: (
|
|
1175
|
+
<IressStack gap\="sm"\>
|
|
1176
|
+
<ul\>
|
|
1177
|
+
<li\>Error type: {String(errorDetails?.type)}</li\>
|
|
1178
|
+
<li\>Error message: {String(errorDetails?.message)}</li\>
|
|
1179
|
+
</ul\>
|
|
1180
|
+
</IressStack\>
|
|
1181
|
+
),
|
|
1182
|
+
}))}
|
|
1183
|
+
/>
|
|
1184
|
+
</IressModal\>
|
|
1185
|
+
</IressForm\>
|
|
1186
|
+
);
|
|
1187
|
+
};
|
|
1188
|
+
|
|
1189
|
+
```
|
|
1190
|
+
|
|
1191
|
+
#### Props
|
|
1192
|
+
|
|
1193
|
+
| Name | Description | Default | Control |
|
|
1194
|
+
| --- | --- | --- | --- |
|
|
1195
|
+
| actions |
|
|
1196
|
+
object
|
|
1197
|
+
|
|
1198
|
+
|
|
1199
|
+
|
|
1200
|
+
| \- | Choose option...nonesubmitsave |
|
|
1201
|
+
| alert |
|
|
1202
|
+
|
|
1203
|
+
\-
|
|
1204
|
+
|
|
1205
|
+
| \- | Choose option...nonebasicAlert |
|
|
1206
|
+
| children |
|
|
1207
|
+
|
|
1208
|
+
array
|
|
1209
|
+
|
|
1210
|
+
|
|
1211
|
+
|
|
1212
|
+
| \- | \- |
|
|
1213
|
+
| onError |
|
|
1214
|
+
|
|
1215
|
+
\-
|
|
1216
|
+
|
|
1217
|
+
| \- | \- |
|
|
1218
|
+
| onSubmit |
|
|
1219
|
+
|
|
1220
|
+
\-
|
|
1221
|
+
|
|
1222
|
+
| \- | \- |
|
|
1223
|
+
| pattern |
|
|
1224
|
+
|
|
1225
|
+
string
|
|
1226
|
+
|
|
1227
|
+
|
|
1228
|
+
|
|
1229
|
+
| \- | short |
|
|
1230
|
+
| Show React Hook Forms itemsReact Hook Forms | Show React Hook Forms items |
|
|
1231
|
+
|
|
1232
|
+
### `values`
|
|
1233
|
+
|
|
1234
|
+
If you would like more control over each value of the form, you should use the `values` prop. This will make the form controlled, meaning it will rely completely on the `values` state to render the value of each field. You will need to use the `onSubmit` prop to sync the form value with your state.
|
|
1235
|
+
|
|
1236
|
+
Use cases where you may need the `values` prop:
|
|
1237
|
+
|
|
1238
|
+
* Syncing with a server once the values have been processed
|
|
1239
|
+
* Syncing the value with browser storage
|
|
1240
|
+
|
|
1241
|
+
**Note:** `values` takes precedence over `defaultValues`. To ensure your form state is predictable, it is best to only use one prop to manage form values.
|
|
1242
|
+
|
|
1243
|
+
[](./iframe.html?id=patterns-form--values)
|
|
1244
|
+
|
|
1245
|
+
```
|
|
1246
|
+
|
|
1247
|
+
interface FieldValues {
|
|
1248
|
+
name?: string;
|
|
1249
|
+
email?: string;
|
|
1250
|
+
}
|
|
1251
|
+
export const ControlledForm \= (args: IressFormProps<FieldValues\>) \=> {
|
|
1252
|
+
const \[values, setValues\] \= useState<FieldValues\>({
|
|
1253
|
+
name: 'Leia Skywalker',
|
|
1254
|
+
email: 'leia.skywalker@iress.com',
|
|
1255
|
+
});
|
|
1256
|
+
const \[preview, setPreview\] \= useState(false);
|
|
1257
|
+
return (
|
|
1258
|
+
<\>
|
|
1259
|
+
<IressForm
|
|
1260
|
+
{...args}
|
|
1261
|
+
onSubmit\={(data) \=> {
|
|
1262
|
+
setValues(data);
|
|
1263
|
+
setPreview(true);
|
|
1264
|
+
}}
|
|
1265
|
+
values\={values}
|
|
1266
|
+
mode\="onChange"
|
|
1267
|
+
\>
|
|
1268
|
+
<IressFormField
|
|
1269
|
+
label\="Name"
|
|
1270
|
+
name\="name"
|
|
1271
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
1272
|
+
rules\={{
|
|
1273
|
+
required: 'Name is required',
|
|
1274
|
+
}}
|
|
1275
|
+
/>
|
|
1276
|
+
<IressFormField
|
|
1277
|
+
label\="Email address"
|
|
1278
|
+
name\="email"
|
|
1279
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
1280
|
+
rules\={{
|
|
1281
|
+
minLength: {
|
|
1282
|
+
message: 'Use a longer email address',
|
|
1283
|
+
value: 6,
|
|
1284
|
+
},
|
|
1285
|
+
required: 'Email is required',
|
|
1286
|
+
}}
|
|
1287
|
+
/>
|
|
1288
|
+
</IressForm\>
|
|
1289
|
+
<IressDivider my\="md" />
|
|
1290
|
+
<IressInline gap\="sm"\>
|
|
1291
|
+
<IressButton onClick\={() \=> setPreview(true)}\>Last update</IressButton\>
|
|
1292
|
+
<IressButton
|
|
1293
|
+
onClick\={() \=>
|
|
1294
|
+
setValues({
|
|
1295
|
+
name: '',
|
|
1296
|
+
email: '',
|
|
1297
|
+
})
|
|
1298
|
+
}
|
|
1299
|
+
\>
|
|
1300
|
+
Clear </IressButton\>
|
|
1301
|
+
</IressInline\>
|
|
1302
|
+
<IressModal show\={!!preview} onShowChange\={(show) \=> setPreview(show)}\>
|
|
1303
|
+
<IressTable
|
|
1304
|
+
caption\="Last update"
|
|
1305
|
+
rows\={Object.entries(values).map((entry) \=> ({
|
|
1306
|
+
name: entry\[0\],
|
|
1307
|
+
value: JSON.stringify(entry\[1\], null, 2),
|
|
1308
|
+
}))}
|
|
1309
|
+
/>
|
|
1310
|
+
</IressModal\>
|
|
1311
|
+
</\>
|
|
1312
|
+
);
|
|
1313
|
+
};
|
|
1314
|
+
|
|
1315
|
+
```
|
|
1316
|
+
|
|
1317
|
+
#### Props
|
|
1318
|
+
|
|
1319
|
+
| Name | Description | Default | Control |
|
|
1320
|
+
| --- | --- | --- | --- |
|
|
1321
|
+
| actions |
|
|
1322
|
+
object
|
|
1323
|
+
|
|
1324
|
+
|
|
1325
|
+
|
|
1326
|
+
| \- | Choose option...nonesubmitsave |
|
|
1327
|
+
| alert |
|
|
1328
|
+
|
|
1329
|
+
\-
|
|
1330
|
+
|
|
1331
|
+
| \- | Choose option...nonebasicAlert |
|
|
1332
|
+
| children |
|
|
1333
|
+
|
|
1334
|
+
array
|
|
1335
|
+
|
|
1336
|
+
|
|
1337
|
+
|
|
1338
|
+
| \- | Choose option...nonesimplesupportedControlsreadOnly |
|
|
1339
|
+
| onSubmit |
|
|
1340
|
+
|
|
1341
|
+
\-
|
|
1342
|
+
|
|
1343
|
+
| \- | \- |
|
|
1344
|
+
| pattern |
|
|
1345
|
+
|
|
1346
|
+
string
|
|
1347
|
+
|
|
1348
|
+
|
|
1349
|
+
|
|
1350
|
+
| \- | short |
|
|
1351
|
+
| Show React Hook Forms itemsReact Hook Forms | Show React Hook Forms items |
|
|
1352
|
+
|
|
1353
|
+
### Disable validation
|
|
1354
|
+
|
|
1355
|
+
Disabling validation is not possible with the `IressForm` component. In cases where you do need to disable validation, please consider the following:
|
|
1356
|
+
|
|
1357
|
+
1. Use a non-submitting button to save a draft (eg. `<IressButton type="button">Save as draft</IressButton>`). Then you can use the `ref` of the form to get the form data.
|
|
1358
|
+
2. Use a native `form` element, and customise the error handling.
|
|
1359
|
+
|
|
1360
|
+
Here we have an example showcasing option one.
|
|
1361
|
+
|
|
1362
|
+
[](./iframe.html?id=patterns-form--disable-validation)
|
|
1363
|
+
|
|
1364
|
+
```
|
|
1365
|
+
|
|
1366
|
+
import {
|
|
1367
|
+
type FormRef,
|
|
1368
|
+
IressButton,
|
|
1369
|
+
IressDivider,
|
|
1370
|
+
IressForm,
|
|
1371
|
+
IressFormField,
|
|
1372
|
+
type IressFormProps,
|
|
1373
|
+
IressInput,
|
|
1374
|
+
IressToasterProvider,
|
|
1375
|
+
useToaster,
|
|
1376
|
+
} from '@iress-oss/ids-components';
|
|
1377
|
+
import { useRef } from 'react';
|
|
1378
|
+
interface FieldValues {
|
|
1379
|
+
name?: string;
|
|
1380
|
+
email?: string;
|
|
1381
|
+
}
|
|
1382
|
+
const Form \= () \=> {
|
|
1383
|
+
const { success, error } \= useToaster();
|
|
1384
|
+
const formRef \= useRef<FormRef<FieldValues\>>(null);
|
|
1385
|
+
return (
|
|
1386
|
+
<\>
|
|
1387
|
+
<IressForm {...{
|
|
1388
|
+
actions: {
|
|
1389
|
+
type: {
|
|
1390
|
+
\_\_docgenInfo: {
|
|
1391
|
+
description: '',
|
|
1392
|
+
methods: \[\],
|
|
1393
|
+
displayName: 'IressButton',
|
|
1394
|
+
props: {
|
|
1395
|
+
value: {
|
|
1396
|
+
required: false,
|
|
1397
|
+
tsType: {
|
|
1398
|
+
name: 'FormControlValue',
|
|
1399
|
+
},
|
|
1400
|
+
description: 'The value of the button, when used in \`IressButtonGroup\`.',
|
|
1401
|
+
},
|
|
1402
|
+
active: {
|
|
1403
|
+
required: false,
|
|
1404
|
+
tsType: {
|
|
1405
|
+
name: 'boolean',
|
|
1406
|
+
},
|
|
1407
|
+
description: 'Sets the active state of the button, usually used to indicate the button has activated a modal, popover or slideout.',
|
|
1408
|
+
},
|
|
1409
|
+
append: {
|
|
1410
|
+
required: false,
|
|
1411
|
+
tsType: {
|
|
1412
|
+
name: 'ReactNode',
|
|
1413
|
+
},
|
|
1414
|
+
description: 'Content for the append slot.',
|
|
1415
|
+
},
|
|
1416
|
+
children: {
|
|
1417
|
+
required: false,
|
|
1418
|
+
tsType: {
|
|
1419
|
+
name: 'ReactNode',
|
|
1420
|
+
},
|
|
1421
|
+
description: 'Content is placed between prepend and append if provided. Used to describe the expected action of this button.',
|
|
1422
|
+
},
|
|
1423
|
+
element: {
|
|
1424
|
+
required: false,
|
|
1425
|
+
tsType: {
|
|
1426
|
+
name: 'C',
|
|
1427
|
+
},
|
|
1428
|
+
description: 'Change the component that will be rendered as the button, used for third-party libraries that require a specific element type.\\nBy default, it will render a button or an anchor tag based on the \`href\` prop.',
|
|
1429
|
+
},
|
|
1430
|
+
fluid: {
|
|
1431
|
+
required: false,
|
|
1432
|
+
tsType: {
|
|
1433
|
+
name: 'union',
|
|
1434
|
+
raw: 'true | Breakpoints',
|
|
1435
|
+
elements: \[
|
|
1436
|
+
{
|
|
1437
|
+
name: 'literal',
|
|
1438
|
+
value: 'true',
|
|
1439
|
+
},
|
|
1440
|
+
{
|
|
1441
|
+
name: 'Breakpoints',
|
|
1442
|
+
},
|
|
1443
|
+
\],
|
|
1444
|
+
},
|
|
1445
|
+
description: "If \`true\`, the button will stretch to fill it's container. The prop is responsive, so you can set the breakpoint(s) at which the button will be fluid.\\n\\nAll breakpoints: \`fluid={true}\`\\nUp to a specific breakpoint: \`fluid={{ base: true, md: false }}\`",
|
|
1446
|
+
},
|
|
1447
|
+
href: {
|
|
1448
|
+
required: false,
|
|
1449
|
+
tsType: {
|
|
1450
|
+
name: 'THref',
|
|
1451
|
+
},
|
|
1452
|
+
description: 'Contains a URL or a URL fragment that the hyperlink points to.\\nIf this property is set, an anchor tag will be rendered.',
|
|
1453
|
+
},
|
|
1454
|
+
loading: {
|
|
1455
|
+
required: false,
|
|
1456
|
+
tsType: {
|
|
1457
|
+
name: 'union',
|
|
1458
|
+
raw: 'boolean | string',
|
|
1459
|
+
elements: \[
|
|
1460
|
+
{
|
|
1461
|
+
name: 'boolean',
|
|
1462
|
+
},
|
|
1463
|
+
{
|
|
1464
|
+
name: 'string',
|
|
1465
|
+
},
|
|
1466
|
+
\],
|
|
1467
|
+
},
|
|
1468
|
+
description: 'When true, button is in loading state. If provided a string, will be used as the loading text for screen readers.',
|
|
1469
|
+
defaultValue: {
|
|
1470
|
+
value: 'false',
|
|
1471
|
+
computed: false,
|
|
1472
|
+
},
|
|
1473
|
+
},
|
|
1474
|
+
mode: {
|
|
1475
|
+
required: false,
|
|
1476
|
+
tsType: {
|
|
1477
|
+
name: 'union',
|
|
1478
|
+
raw: "'primary' | 'secondary' | 'tertiary'",
|
|
1479
|
+
elements: \[
|
|
1480
|
+
{
|
|
1481
|
+
name: 'literal',
|
|
1482
|
+
value: "'primary'",
|
|
1483
|
+
},
|
|
1484
|
+
{
|
|
1485
|
+
name: 'literal',
|
|
1486
|
+
value: "'secondary'",
|
|
1487
|
+
},
|
|
1488
|
+
{
|
|
1489
|
+
name: 'literal',
|
|
1490
|
+
value: "'tertiary'",
|
|
1491
|
+
},
|
|
1492
|
+
\],
|
|
1493
|
+
},
|
|
1494
|
+
description: 'Style of the button.\\n- Primary: Used for the main action on a page. Usually only used once per screen.\\n- Secondary: Used for secondary actions on a page, often an action on multiple \`IressPanel\`s. Can used multiple times per screen.\\n- Tertiary: Used for tertiary actions on a page, often the secondary action on multiple \`IressPanel\`s. Can used multiple times per screen.\\n\\n\*\*Migrating to version 6\*\*\\n- \`link\` mode has been removed. If it is an action, use the \`tertiary\` mode. If it is a link inside a paragraph, use the new \`IressLink\` component instead.\\n- \`danger\` has been removed. Please use the \`status\` prop instead.\\n- \`positive\` and \`success\` have been removed. Please use the \`status\` prop instead.',
|
|
1495
|
+
defaultValue: {
|
|
1496
|
+
value: "'secondary'",
|
|
1497
|
+
computed: false,
|
|
1498
|
+
},
|
|
1499
|
+
},
|
|
1500
|
+
onClick: {
|
|
1501
|
+
required: false,
|
|
1502
|
+
tsType: {
|
|
1503
|
+
name: 'MouseEventHandler',
|
|
1504
|
+
elements: \[
|
|
1505
|
+
{
|
|
1506
|
+
name: 'Exclude',
|
|
1507
|
+
elements: \[
|
|
1508
|
+
{
|
|
1509
|
+
name: 'Parameters\[0\]',
|
|
1510
|
+
raw: 'Parameters<Exclude<ButtonRef<C, THref>, undefined>>\[0\]',
|
|
1511
|
+
},
|
|
1512
|
+
{
|
|
1513
|
+
name: 'null',
|
|
1514
|
+
},
|
|
1515
|
+
\],
|
|
1516
|
+
raw: 'Exclude<Parameters<Exclude<ButtonRef<C, THref>, undefined>>\[0\], null>',
|
|
1517
|
+
},
|
|
1518
|
+
\],
|
|
1519
|
+
raw: 'MouseEventHandler<ButtonInstance<C, THref>>',
|
|
1520
|
+
},
|
|
1521
|
+
description: 'Emitted when the menu item is clicked.',
|
|
1522
|
+
},
|
|
1523
|
+
prepend: {
|
|
1524
|
+
required: false,
|
|
1525
|
+
tsType: {
|
|
1526
|
+
name: 'ReactNode',
|
|
1527
|
+
},
|
|
1528
|
+
description: 'Content for the prepend slot.',
|
|
1529
|
+
},
|
|
1530
|
+
noWrap: {
|
|
1531
|
+
required: false,
|
|
1532
|
+
tsType: {
|
|
1533
|
+
name: 'boolean',
|
|
1534
|
+
},
|
|
1535
|
+
description: 'Prevents text wrapping if set to true.',
|
|
1536
|
+
defaultValue: {
|
|
1537
|
+
value: 'false',
|
|
1538
|
+
computed: false,
|
|
1539
|
+
},
|
|
1540
|
+
},
|
|
1541
|
+
status: {
|
|
1542
|
+
required: false,
|
|
1543
|
+
tsType: {
|
|
1544
|
+
name: 'union',
|
|
1545
|
+
raw: "'success' | 'danger'",
|
|
1546
|
+
elements: \[
|
|
1547
|
+
{
|
|
1548
|
+
name: 'literal',
|
|
1549
|
+
value: "'success'",
|
|
1550
|
+
},
|
|
1551
|
+
{
|
|
1552
|
+
name: 'literal',
|
|
1553
|
+
value: "'danger'",
|
|
1554
|
+
},
|
|
1555
|
+
\],
|
|
1556
|
+
},
|
|
1557
|
+
description: 'An optional status to assign to the button.\\n- \`success\`: Indicates a successful or positive action.\\n- \`danger\`: Indicates a dangerous or potentially negative action.',
|
|
1558
|
+
},
|
|
1559
|
+
},
|
|
1560
|
+
composes: \[
|
|
1561
|
+
'IressCSSProps',
|
|
1562
|
+
'IressTestProps',
|
|
1563
|
+
\],
|
|
1564
|
+
},
|
|
1565
|
+
},
|
|
1566
|
+
key: null,
|
|
1567
|
+
props: {
|
|
1568
|
+
type: 'submit',
|
|
1569
|
+
mode: 'primary',
|
|
1570
|
+
children: 'Submit',
|
|
1571
|
+
},
|
|
1572
|
+
\_owner: null,
|
|
1573
|
+
\_store: {},
|
|
1574
|
+
},
|
|
1575
|
+
pattern: 'short',
|
|
1576
|
+
}}
|
|
1577
|
+
onSubmit={() \=>
|
|
1578
|
+
success({
|
|
1579
|
+
heading: 'Passed validation',
|
|
1580
|
+
content: JSON.stringify(formRef.current?.api.getValues(), null, 2),
|
|
1581
|
+
})
|
|
1582
|
+
}
|
|
1583
|
+
onError={() \=>
|
|
1584
|
+
error({
|
|
1585
|
+
heading: 'Failed validation',
|
|
1586
|
+
content: JSON.stringify(formRef.current?.api.getValues(), null, 2),
|
|
1587
|
+
})
|
|
1588
|
+
}
|
|
1589
|
+
ref={formRef}
|
|
1590
|
+
> <IressFormField
|
|
1591
|
+
label\="Name"
|
|
1592
|
+
name\="name"
|
|
1593
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
1594
|
+
rules\={{
|
|
1595
|
+
required: 'Name is required',
|
|
1596
|
+
}}
|
|
1597
|
+
/>
|
|
1598
|
+
<IressFormField
|
|
1599
|
+
label\="Email address"
|
|
1600
|
+
name\="email"
|
|
1601
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
1602
|
+
rules\={{
|
|
1603
|
+
minLength: {
|
|
1604
|
+
message: 'Use a longer email address',
|
|
1605
|
+
value: 6,
|
|
1606
|
+
},
|
|
1607
|
+
required: 'Email is required',
|
|
1608
|
+
}}
|
|
1609
|
+
/>
|
|
1610
|
+
</IressForm\>
|
|
1611
|
+
<IressDivider my\="md" />
|
|
1612
|
+
<IressButton
|
|
1613
|
+
onClick\={() \=> {
|
|
1614
|
+
success({
|
|
1615
|
+
heading: 'Saved as draft (no validation)',
|
|
1616
|
+
content: JSON.stringify(formRef.current?.api.getValues(), null, 2),
|
|
1617
|
+
});
|
|
1618
|
+
}}
|
|
1619
|
+
\>
|
|
1620
|
+
Save as draft </IressButton\>
|
|
1621
|
+
</\>
|
|
1622
|
+
);
|
|
1623
|
+
};
|
|
1624
|
+
export const DisableValidationForm \= () \=> (
|
|
1625
|
+
<IressToasterProvider\>
|
|
1626
|
+
<Form {...{
|
|
1627
|
+
actions: {
|
|
1628
|
+
type: {
|
|
1629
|
+
\_\_docgenInfo: {
|
|
1630
|
+
description: '',
|
|
1631
|
+
methods: \[\],
|
|
1632
|
+
displayName: 'IressButton',
|
|
1633
|
+
props: {
|
|
1634
|
+
value: {
|
|
1635
|
+
required: false,
|
|
1636
|
+
tsType: {
|
|
1637
|
+
name: 'FormControlValue',
|
|
1638
|
+
},
|
|
1639
|
+
description: 'The value of the button, when used in \`IressButtonGroup\`.',
|
|
1640
|
+
},
|
|
1641
|
+
active: {
|
|
1642
|
+
required: false,
|
|
1643
|
+
tsType: {
|
|
1644
|
+
name: 'boolean',
|
|
1645
|
+
},
|
|
1646
|
+
description: 'Sets the active state of the button, usually used to indicate the button has activated a modal, popover or slideout.',
|
|
1647
|
+
},
|
|
1648
|
+
append: {
|
|
1649
|
+
required: false,
|
|
1650
|
+
tsType: {
|
|
1651
|
+
name: 'ReactNode',
|
|
1652
|
+
},
|
|
1653
|
+
description: 'Content for the append slot.',
|
|
1654
|
+
},
|
|
1655
|
+
children: {
|
|
1656
|
+
required: false,
|
|
1657
|
+
tsType: {
|
|
1658
|
+
name: 'ReactNode',
|
|
1659
|
+
},
|
|
1660
|
+
description: 'Content is placed between prepend and append if provided. Used to describe the expected action of this button.',
|
|
1661
|
+
},
|
|
1662
|
+
element: {
|
|
1663
|
+
required: false,
|
|
1664
|
+
tsType: {
|
|
1665
|
+
name: 'C',
|
|
1666
|
+
},
|
|
1667
|
+
description: 'Change the component that will be rendered as the button, used for third-party libraries that require a specific element type.\\nBy default, it will render a button or an anchor tag based on the \`href\` prop.',
|
|
1668
|
+
},
|
|
1669
|
+
fluid: {
|
|
1670
|
+
required: false,
|
|
1671
|
+
tsType: {
|
|
1672
|
+
name: 'union',
|
|
1673
|
+
raw: 'true | Breakpoints',
|
|
1674
|
+
elements: \[
|
|
1675
|
+
{
|
|
1676
|
+
name: 'literal',
|
|
1677
|
+
value: 'true',
|
|
1678
|
+
},
|
|
1679
|
+
{
|
|
1680
|
+
name: 'Breakpoints',
|
|
1681
|
+
},
|
|
1682
|
+
\],
|
|
1683
|
+
},
|
|
1684
|
+
description: "If \`true\`, the button will stretch to fill it's container. The prop is responsive, so you can set the breakpoint(s) at which the button will be fluid.\\n\\nAll breakpoints: \`fluid={true}\`\\nUp to a specific breakpoint: \`fluid={{ base: true, md: false }}\`",
|
|
1685
|
+
},
|
|
1686
|
+
href: {
|
|
1687
|
+
required: false,
|
|
1688
|
+
tsType: {
|
|
1689
|
+
name: 'THref',
|
|
1690
|
+
},
|
|
1691
|
+
description: 'Contains a URL or a URL fragment that the hyperlink points to.\\nIf this property is set, an anchor tag will be rendered.',
|
|
1692
|
+
},
|
|
1693
|
+
loading: {
|
|
1694
|
+
required: false,
|
|
1695
|
+
tsType: {
|
|
1696
|
+
name: 'union',
|
|
1697
|
+
raw: 'boolean | string',
|
|
1698
|
+
elements: \[
|
|
1699
|
+
{
|
|
1700
|
+
name: 'boolean',
|
|
1701
|
+
},
|
|
1702
|
+
{
|
|
1703
|
+
name: 'string',
|
|
1704
|
+
},
|
|
1705
|
+
\],
|
|
1706
|
+
},
|
|
1707
|
+
description: 'When true, button is in loading state. If provided a string, will be used as the loading text for screen readers.',
|
|
1708
|
+
defaultValue: {
|
|
1709
|
+
value: 'false',
|
|
1710
|
+
computed: false,
|
|
1711
|
+
},
|
|
1712
|
+
},
|
|
1713
|
+
mode: {
|
|
1714
|
+
required: false,
|
|
1715
|
+
tsType: {
|
|
1716
|
+
name: 'union',
|
|
1717
|
+
raw: "'primary' | 'secondary' | 'tertiary'",
|
|
1718
|
+
elements: \[
|
|
1719
|
+
{
|
|
1720
|
+
name: 'literal',
|
|
1721
|
+
value: "'primary'",
|
|
1722
|
+
},
|
|
1723
|
+
{
|
|
1724
|
+
name: 'literal',
|
|
1725
|
+
value: "'secondary'",
|
|
1726
|
+
},
|
|
1727
|
+
{
|
|
1728
|
+
name: 'literal',
|
|
1729
|
+
value: "'tertiary'",
|
|
1730
|
+
},
|
|
1731
|
+
\],
|
|
1732
|
+
},
|
|
1733
|
+
description: 'Style of the button.\\n- Primary: Used for the main action on a page. Usually only used once per screen.\\n- Secondary: Used for secondary actions on a page, often an action on multiple \`IressPanel\`s. Can used multiple times per screen.\\n- Tertiary: Used for tertiary actions on a page, often the secondary action on multiple \`IressPanel\`s. Can used multiple times per screen.\\n\\n\*\*Migrating to version 6\*\*\\n- \`link\` mode has been removed. If it is an action, use the \`tertiary\` mode. If it is a link inside a paragraph, use the new \`IressLink\` component instead.\\n- \`danger\` has been removed. Please use the \`status\` prop instead.\\n- \`positive\` and \`success\` have been removed. Please use the \`status\` prop instead.',
|
|
1734
|
+
defaultValue: {
|
|
1735
|
+
value: "'secondary'",
|
|
1736
|
+
computed: false,
|
|
1737
|
+
},
|
|
1738
|
+
},
|
|
1739
|
+
onClick: {
|
|
1740
|
+
required: false,
|
|
1741
|
+
tsType: {
|
|
1742
|
+
name: 'MouseEventHandler',
|
|
1743
|
+
elements: \[
|
|
1744
|
+
{
|
|
1745
|
+
name: 'Exclude',
|
|
1746
|
+
elements: \[
|
|
1747
|
+
{
|
|
1748
|
+
name: 'Parameters\[0\]',
|
|
1749
|
+
raw: 'Parameters<Exclude<ButtonRef<C, THref>, undefined>>\[0\]',
|
|
1750
|
+
},
|
|
1751
|
+
{
|
|
1752
|
+
name: 'null',
|
|
1753
|
+
},
|
|
1754
|
+
\],
|
|
1755
|
+
raw: 'Exclude<Parameters<Exclude<ButtonRef<C, THref>, undefined>>\[0\], null>',
|
|
1756
|
+
},
|
|
1757
|
+
\],
|
|
1758
|
+
raw: 'MouseEventHandler<ButtonInstance<C, THref>>',
|
|
1759
|
+
},
|
|
1760
|
+
description: 'Emitted when the menu item is clicked.',
|
|
1761
|
+
},
|
|
1762
|
+
prepend: {
|
|
1763
|
+
required: false,
|
|
1764
|
+
tsType: {
|
|
1765
|
+
name: 'ReactNode',
|
|
1766
|
+
},
|
|
1767
|
+
description: 'Content for the prepend slot.',
|
|
1768
|
+
},
|
|
1769
|
+
noWrap: {
|
|
1770
|
+
required: false,
|
|
1771
|
+
tsType: {
|
|
1772
|
+
name: 'boolean',
|
|
1773
|
+
},
|
|
1774
|
+
description: 'Prevents text wrapping if set to true.',
|
|
1775
|
+
defaultValue: {
|
|
1776
|
+
value: 'false',
|
|
1777
|
+
computed: false,
|
|
1778
|
+
},
|
|
1779
|
+
},
|
|
1780
|
+
status: {
|
|
1781
|
+
required: false,
|
|
1782
|
+
tsType: {
|
|
1783
|
+
name: 'union',
|
|
1784
|
+
raw: "'success' | 'danger'",
|
|
1785
|
+
elements: \[
|
|
1786
|
+
{
|
|
1787
|
+
name: 'literal',
|
|
1788
|
+
value: "'success'",
|
|
1789
|
+
},
|
|
1790
|
+
{
|
|
1791
|
+
name: 'literal',
|
|
1792
|
+
value: "'danger'",
|
|
1793
|
+
},
|
|
1794
|
+
\],
|
|
1795
|
+
},
|
|
1796
|
+
description: 'An optional status to assign to the button.\\n- \`success\`: Indicates a successful or positive action.\\n- \`danger\`: Indicates a dangerous or potentially negative action.',
|
|
1797
|
+
},
|
|
1798
|
+
},
|
|
1799
|
+
composes: \[
|
|
1800
|
+
'IressCSSProps',
|
|
1801
|
+
'IressTestProps',
|
|
1802
|
+
\],
|
|
1803
|
+
},
|
|
1804
|
+
},
|
|
1805
|
+
key: null,
|
|
1806
|
+
props: {
|
|
1807
|
+
type: 'submit',
|
|
1808
|
+
mode: 'primary',
|
|
1809
|
+
children: 'Submit',
|
|
1810
|
+
},
|
|
1811
|
+
\_owner: null,
|
|
1812
|
+
\_store: {},
|
|
1813
|
+
},
|
|
1814
|
+
pattern: 'short',
|
|
1815
|
+
}} /> </IressToasterProvider\>
|
|
1816
|
+
);
|
|
1817
|
+
|
|
1818
|
+
```
|
|
1819
|
+
|
|
1820
|
+
#### Props
|
|
1821
|
+
|
|
1822
|
+
| Name | Description | Default | Control |
|
|
1823
|
+
| --- | --- | --- | --- |
|
|
1824
|
+
| actions |
|
|
1825
|
+
object
|
|
1826
|
+
|
|
1827
|
+
|
|
1828
|
+
|
|
1829
|
+
| \- | Choose option...nonesubmitsave |
|
|
1830
|
+
| alert |
|
|
1831
|
+
|
|
1832
|
+
\-
|
|
1833
|
+
|
|
1834
|
+
| \- | Choose option...nonebasicAlert |
|
|
1835
|
+
| children |
|
|
1836
|
+
|
|
1837
|
+
array
|
|
1838
|
+
|
|
1839
|
+
|
|
1840
|
+
|
|
1841
|
+
| \- | \- |
|
|
1842
|
+
| onError |
|
|
1843
|
+
|
|
1844
|
+
\-
|
|
1845
|
+
|
|
1846
|
+
| \- | \- |
|
|
1847
|
+
| onSubmit |
|
|
1848
|
+
|
|
1849
|
+
\-
|
|
1850
|
+
|
|
1851
|
+
| \- | \- |
|
|
1852
|
+
| pattern |
|
|
1853
|
+
|
|
1854
|
+
string
|
|
1855
|
+
|
|
1856
|
+
|
|
1857
|
+
|
|
1858
|
+
| \- | short |
|
|
1859
|
+
| Show React Hook Forms itemsReact Hook Forms | Show React Hook Forms items |
|
|
1860
|
+
|
|
1861
|
+
### Resetting the form
|
|
1862
|
+
|
|
1863
|
+
You can reset the form using the `ref` of the form. You must provide a `defaultValues` prop that contains all the fields in the form to ensure it resets properly.
|
|
1864
|
+
|
|
1865
|
+
**Note:** `<button type="reset" />` does not work with `IressForm`. You need to add an `onClick` prop to the button and use the `ref.reset` method to reset the form.
|
|
1866
|
+
|
|
1867
|
+
[](./iframe.html?id=patterns-form--reset-form)
|
|
1868
|
+
|
|
1869
|
+
```
|
|
1870
|
+
|
|
1871
|
+
import {
|
|
1872
|
+
type IressFormProps,
|
|
1873
|
+
IressForm,
|
|
1874
|
+
type FormRef,
|
|
1875
|
+
IressDivider,
|
|
1876
|
+
IressButton,
|
|
1877
|
+
IressFormField,
|
|
1878
|
+
IressInput,
|
|
1879
|
+
} from '@iress-oss/ids-components';
|
|
1880
|
+
import { useRef } from 'react';
|
|
1881
|
+
interface FieldValues {
|
|
1882
|
+
name?: string;
|
|
1883
|
+
email?: string;
|
|
1884
|
+
}
|
|
1885
|
+
export const FormReset \= () \=> {
|
|
1886
|
+
const ref \= useRef<FormRef<FieldValues\>>(null);
|
|
1887
|
+
return (
|
|
1888
|
+
<IressForm {...{
|
|
1889
|
+
pattern: 'short',
|
|
1890
|
+
defaultValues: {
|
|
1891
|
+
name: '',
|
|
1892
|
+
email: '',
|
|
1893
|
+
},
|
|
1894
|
+
}} ref\={ref}\>
|
|
1895
|
+
<IressFormField
|
|
1896
|
+
label\="Name"
|
|
1897
|
+
name\="name"
|
|
1898
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
1899
|
+
rules\={{
|
|
1900
|
+
required: 'Name is required',
|
|
1901
|
+
}}
|
|
1902
|
+
/>
|
|
1903
|
+
<IressFormField
|
|
1904
|
+
label\="Email address"
|
|
1905
|
+
name\="email"
|
|
1906
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
1907
|
+
rules\={{
|
|
1908
|
+
minLength: {
|
|
1909
|
+
message: 'Use a longer email address',
|
|
1910
|
+
value: 6,
|
|
1911
|
+
},
|
|
1912
|
+
required: 'Email is required',
|
|
1913
|
+
}}
|
|
1914
|
+
/>
|
|
1915
|
+
<IressButton mode\="primary" type\="submit"\>
|
|
1916
|
+
Sign up </IressButton\>
|
|
1917
|
+
<IressDivider my\="md" />
|
|
1918
|
+
<IressButton type\="reset" onClick\={() \=> ref.current?.reset()}\>
|
|
1919
|
+
Reset </IressButton\>
|
|
1920
|
+
</IressForm\>
|
|
1921
|
+
);
|
|
1922
|
+
};
|
|
1923
|
+
|
|
1924
|
+
```
|
|
1925
|
+
|
|
1926
|
+
#### Props
|
|
1927
|
+
|
|
1928
|
+
| Name | Description | Default | Control |
|
|
1929
|
+
| --- | --- | --- | --- |
|
|
1930
|
+
| actions |
|
|
1931
|
+
object
|
|
1932
|
+
|
|
1933
|
+
|
|
1934
|
+
|
|
1935
|
+
| \- | Choose option...nonesubmitsave |
|
|
1936
|
+
| alert |
|
|
1937
|
+
|
|
1938
|
+
\-
|
|
1939
|
+
|
|
1940
|
+
| \- | Choose option...nonebasicAlert |
|
|
1941
|
+
| children |
|
|
1942
|
+
|
|
1943
|
+
array
|
|
1944
|
+
|
|
1945
|
+
|
|
1946
|
+
|
|
1947
|
+
| \- | \- |
|
|
1948
|
+
| pattern |
|
|
1949
|
+
|
|
1950
|
+
string
|
|
1951
|
+
|
|
1952
|
+
|
|
1953
|
+
|
|
1954
|
+
| \- | short |
|
|
1955
|
+
| Show React Hook Forms itemsReact Hook Forms | Show React Hook Forms items |
|
|
1956
|
+
|
|
1957
|
+
### Read only
|
|
1958
|
+
|
|
1959
|
+
You can pass the `readOnly` prop to remove the asterisk symbol (\*) even when the field is `required`, and that field will be exempted from validation.
|
|
1960
|
+
|
|
1961
|
+
[](./iframe.html?id=patterns-form--exclude-read-only-validation)
|
|
1962
|
+
|
|
1963
|
+
```
|
|
1964
|
+
|
|
1965
|
+
<IressStack gap\="md"\>
|
|
1966
|
+
<IressText\>
|
|
1967
|
+
<h2\>
|
|
1968
|
+
Excludes read-only validation </h2\>
|
|
1969
|
+
<p\>
|
|
1970
|
+
Try hitting submit to see that only email is being validated </p\>
|
|
1971
|
+
</IressText\>
|
|
1972
|
+
<ForwardedForm
|
|
1973
|
+
actions\={<IressButton mode\="primary" type\="submit"\>Submit</IressButton\>}
|
|
1974
|
+
pattern\="short"
|
|
1975
|
+
\>
|
|
1976
|
+
<IressFormField
|
|
1977
|
+
label\="Name"
|
|
1978
|
+
name\="name"
|
|
1979
|
+
readOnly
|
|
1980
|
+
render\={() \=> {}}
|
|
1981
|
+
rules\={{
|
|
1982
|
+
required: 'Name is required'
|
|
1983
|
+
}}
|
|
1984
|
+
/>
|
|
1985
|
+
<IressFormField
|
|
1986
|
+
label\="Email address"
|
|
1987
|
+
name\="email"
|
|
1988
|
+
render\={() \=> {}}
|
|
1989
|
+
rules\={{
|
|
1990
|
+
minLength: {
|
|
1991
|
+
message: 'Use a longer email address',
|
|
1992
|
+
value: 6
|
|
1993
|
+
},
|
|
1994
|
+
required: 'Email is required'
|
|
1995
|
+
}}
|
|
1996
|
+
/>
|
|
1997
|
+
</ForwardedForm\>
|
|
1998
|
+
</IressStack\>
|
|
1999
|
+
|
|
2000
|
+
```
|
|
2001
|
+
|
|
2002
|
+
#### Props
|
|
2003
|
+
|
|
2004
|
+
| Name | Description | Default | Control |
|
|
2005
|
+
| --- | --- | --- | --- |
|
|
2006
|
+
| actions |
|
|
2007
|
+
object
|
|
2008
|
+
|
|
2009
|
+
|
|
2010
|
+
|
|
2011
|
+
| \- | Choose option...nonesubmitsave |
|
|
2012
|
+
| alert |
|
|
2013
|
+
|
|
2014
|
+
\-
|
|
2015
|
+
|
|
2016
|
+
| \- | Choose option...nonebasicAlert |
|
|
2017
|
+
| children |
|
|
2018
|
+
|
|
2019
|
+
array
|
|
2020
|
+
|
|
2021
|
+
|
|
2022
|
+
|
|
2023
|
+
| \- | Choose option...nonesimplesupportedControlsreadOnly |
|
|
2024
|
+
| pattern |
|
|
2025
|
+
|
|
2026
|
+
string
|
|
2027
|
+
|
|
2028
|
+
|
|
2029
|
+
|
|
2030
|
+
| \- | short |
|
|
2031
|
+
| Show React Hook Forms itemsReact Hook Forms | Show React Hook Forms items |
|
|
2032
|
+
|
|
2033
|
+
`IressHookForm`
|
|
2034
|
+
---------------
|
|
2035
|
+
|
|
2036
|
+
`IressHookForm` is the underlying component that `IressForm` is built upon. It has a single required prop, `form`, which expects the return value of the `useForm` hook from React Hook Forms.
|
|
2037
|
+
|
|
2038
|
+
It has been exposed to consumers to allow you to have complete control of your React Hook Forms instance whilst still taking advantage of the IDS form components.
|
|
2039
|
+
|
|
2040
|
+
Some use cases:
|
|
2041
|
+
|
|
2042
|
+
1. You may need to use the `useForm` hook in a parent component to share the form state with multiple child components.
|
|
2043
|
+
2. You would like to use the return value of the `useForm` hook without having to use a ref to access the `react-hook-form` api.
|
|
2044
|
+
|
|
2045
|
+
[](./iframe.html?id=patterns-form--hook-form)
|
|
2046
|
+
|
|
2047
|
+
```
|
|
2048
|
+
|
|
2049
|
+
interface FieldValues {
|
|
2050
|
+
firstName: string;
|
|
2051
|
+
lastName: string;
|
|
2052
|
+
insuredAtPolicyLevel?: boolean;
|
|
2053
|
+
sumInsured?: number;
|
|
2054
|
+
sumInsured\_na?: string;
|
|
2055
|
+
}
|
|
2056
|
+
export const HookFormExample \= () \=> {
|
|
2057
|
+
const initialInsuredAtPolicyLevel \= false;
|
|
2058
|
+
const initialSumInsured \= 5000;
|
|
2059
|
+
const form \= IressHookForm.useForm<FieldValues\>();
|
|
2060
|
+
const { watch, control } \= form;
|
|
2061
|
+
const firstName \= watch('firstName');
|
|
2062
|
+
const lastName \= watch('lastName');
|
|
2063
|
+
const insuredAtPolicyLevel \= watch('insuredAtPolicyLevel');
|
|
2064
|
+
return (
|
|
2065
|
+
<IressContainer\>
|
|
2066
|
+
<IressText\>
|
|
2067
|
+
<h2\>Hook Form Example</h2\>
|
|
2068
|
+
<p\>
|
|
2069
|
+
This example demonstrates how to use the <code\>IressHookForm</code\>{' '}
|
|
2070
|
+
component to create a form with controlled fields and conditional rendering based on form values. </p\>
|
|
2071
|
+
<IressHookForm form\={form}\>
|
|
2072
|
+
{firstName && lastName && (
|
|
2073
|
+
<IressPanel mb\="md" bg\="alt"\>
|
|
2074
|
+
Name: {firstName} {lastName}
|
|
2075
|
+
</IressPanel\>
|
|
2076
|
+
)}
|
|
2077
|
+
<IressFormField
|
|
2078
|
+
name\="firstName"
|
|
2079
|
+
label\="First Name"
|
|
2080
|
+
render\={(controlledProps) \=> <IressInput {...controlledProps} />}
|
|
2081
|
+
rules\={{ required: true }}
|
|
2082
|
+
/>
|
|
2083
|
+
<IressFormField
|
|
2084
|
+
name\="lastName"
|
|
2085
|
+
label\="Last Name"
|
|
2086
|
+
render\={(controlledProps) \=> (
|
|
2087
|
+
<IressInput {...controlledProps} type\="email" />
|
|
2088
|
+
)}
|
|
2089
|
+
rules\={{ required: true }}
|
|
2090
|
+
/>
|
|
2091
|
+
<IressDivider mt\="lg" mb\="md" />
|
|
2092
|
+
<IressFormField
|
|
2093
|
+
name\="insuredAtPolicyLevel"
|
|
2094
|
+
defaultChecked\={initialInsuredAtPolicyLevel}
|
|
2095
|
+
label\="Insurance options"
|
|
2096
|
+
control\={control}
|
|
2097
|
+
render\={(controlledProps) \=> (
|
|
2098
|
+
<IressCheckbox {...controlledProps}\>
|
|
2099
|
+
Insured at policy level </IressCheckbox\>
|
|
2100
|
+
)}
|
|
2101
|
+
/>
|
|
2102
|
+
{insuredAtPolicyLevel && (
|
|
2103
|
+
<IressFormField
|
|
2104
|
+
name\="sumInsured"
|
|
2105
|
+
defaultValue\={initialSumInsured}
|
|
2106
|
+
label\="Sum insured"
|
|
2107
|
+
control\={control}
|
|
2108
|
+
render\={(controlledProps) \=> (
|
|
2109
|
+
<IressInputCurrency {...controlledProps} currencyCode\="GBP" />
|
|
2110
|
+
)}
|
|
2111
|
+
/>
|
|
2112
|
+
)}
|
|
2113
|
+
{!insuredAtPolicyLevel && (
|
|
2114
|
+
<IressFormField
|
|
2115
|
+
name\="sumInsured\_na"
|
|
2116
|
+
defaultValue\="N/A"
|
|
2117
|
+
label\="Sum insured"
|
|
2118
|
+
control\={control}
|
|
2119
|
+
render\={(properties) \=> <IressInput {...properties} readOnly />}
|
|
2120
|
+
/>
|
|
2121
|
+
)}
|
|
2122
|
+
<IressButton type\="submit" mode\="primary"\>
|
|
2123
|
+
Submit </IressButton\>
|
|
2124
|
+
</IressHookForm\>
|
|
2125
|
+
</IressText\>
|
|
2126
|
+
</IressContainer\>
|
|
2127
|
+
);
|
|
2128
|
+
};
|
|
2129
|
+
|
|
2130
|
+
```
|
|
2131
|
+
|
|
2132
|
+
#### Props
|
|
2133
|
+
|
|
2134
|
+
| Name | Description | Default | Control |
|
|
2135
|
+
| --- | --- | --- | --- |
|
|
2136
|
+
| alert |
|
|
2137
|
+
The content of the alert section.
|
|
2138
|
+
|
|
2139
|
+
ReactNode
|
|
2140
|
+
|
|
2141
|
+
|
|
2142
|
+
|
|
2143
|
+
| \- | Set object |
|
|
2144
|
+
| children |
|
|
2145
|
+
|
|
2146
|
+
The content of the form, usually multiple `IressFormField` or `IressFormFieldset` components.
|
|
2147
|
+
|
|
2148
|
+
ReactNode
|
|
2149
|
+
|
|
2150
|
+
|
|
2151
|
+
|
|
2152
|
+
| \- | Set object |
|
|
2153
|
+
| form\* |
|
|
2154
|
+
|
|
2155
|
+
React hook form instance.
|
|
2156
|
+
|
|
2157
|
+
UseFormReturn
|
|
2158
|
+
|
|
2159
|
+
|
|
2160
|
+
|
|
2161
|
+
| \- | Set object |
|
|
2162
|
+
| onError |
|
|
2163
|
+
|
|
2164
|
+
Emitted when any field has an error. Called after the first submit if any errors are recorded, and from then on when any value changes.
|
|
2165
|
+
|
|
2166
|
+
SubmitErrorHandler
|
|
2167
|
+
|
|
2168
|
+
|
|
2169
|
+
|
|
2170
|
+
| \- | Set object |
|
|
2171
|
+
| onSubmit |
|
|
2172
|
+
|
|
2173
|
+
Handler for when the submit method on the form is called after validation is passed.
|
|
2174
|
+
|
|
2175
|
+
(data: T) => void
|
|
2176
|
+
|
|
2177
|
+
| \- | \- |
|
|
2178
|
+
| onValidChange |
|
|
2179
|
+
|
|
2180
|
+
Emitted when the form state is valid.
|
|
2181
|
+
|
|
2182
|
+
(isValid: boolean) => void
|
|
2183
|
+
|
|
2184
|
+
| \- | \- |
|
|
2185
|
+
| updateErrorSummaryOnSubmit |
|
|
2186
|
+
|
|
2187
|
+
If set to `true`, the summary will only update when the form is submitted, not on every field change. This is useful for performance reasons, especially in large forms.
|
|
2188
|
+
|
|
2189
|
+
boolean
|
|
2190
|
+
|
|
2191
|
+
|
|
2192
|
+
|
|
2193
|
+
| \- | Set boolean |
|
|
2194
|
+
|
|
2195
|
+
`IressFormValidationSummary`
|
|
2196
|
+
----------------------------
|
|
2197
|
+
|
|
2198
|
+
`IressFormValidationSummary` is the error summary component that is added to the top of the form for screen readers to announce validation errors. It is automatically added to the form when there are validation errors, but you can also use it independently to create your own error summary, usually used if you want a visible error summary at the top of the form.
|
|
2199
|
+
|
|
2200
|
+
[](./iframe.html?id=patterns-form--validation-summary)
|
|
2201
|
+
|
|
2202
|
+
```
|
|
2203
|
+
|
|
2204
|
+
<ForwardedForm
|
|
2205
|
+
actions\={<IressButton mode\="primary" type\="submit"\>Submit</IressButton\>}
|
|
2206
|
+
alert\={<IressFormValidationSummary />}
|
|
2207
|
+
heading\="Try hitting submit to see the validation summary"
|
|
2208
|
+
pattern\="long"
|
|
2209
|
+
\>
|
|
2210
|
+
<IressText element\="p"\>
|
|
2211
|
+
This form showcases all the different components you can use within{' '}
|
|
2212
|
+
<code\>
|
|
2213
|
+
IressFormField </code\>
|
|
2214
|
+
. </IressText\>
|
|
2215
|
+
<IressFormField
|
|
2216
|
+
label\="Name"
|
|
2217
|
+
name\="name"
|
|
2218
|
+
render\={() \=> {}}
|
|
2219
|
+
rules\={{
|
|
2220
|
+
required: true
|
|
2221
|
+
}}
|
|
2222
|
+
/>
|
|
2223
|
+
<IressFormField
|
|
2224
|
+
label\="Date"
|
|
2225
|
+
name\="date"
|
|
2226
|
+
render\={() \=> {}}
|
|
2227
|
+
rules\={{
|
|
2228
|
+
required: true
|
|
2229
|
+
}}
|
|
2230
|
+
/>
|
|
2231
|
+
<IressFormField
|
|
2232
|
+
label\="Gender"
|
|
2233
|
+
name\="gender"
|
|
2234
|
+
render\={() \=> {}}
|
|
2235
|
+
rules\={{
|
|
2236
|
+
required: true
|
|
2237
|
+
}}
|
|
2238
|
+
/>
|
|
2239
|
+
<IressFormField
|
|
2240
|
+
label\="Gender with icons"
|
|
2241
|
+
name\="gender-with-icons"
|
|
2242
|
+
render\={() \=> {}}
|
|
2243
|
+
rules\={{
|
|
2244
|
+
required: true
|
|
2245
|
+
}}
|
|
2246
|
+
/>
|
|
2247
|
+
<IressFormFieldset
|
|
2248
|
+
label\="Hobbies"
|
|
2249
|
+
name\="hobbies"
|
|
2250
|
+
render\={() \=> {}}
|
|
2251
|
+
rules\={{
|
|
2252
|
+
required: 'Select at least one hobby'
|
|
2253
|
+
}}
|
|
2254
|
+
/>
|
|
2255
|
+
<IressFormFieldset
|
|
2256
|
+
label\="Food of preference"
|
|
2257
|
+
name\="food"
|
|
2258
|
+
render\={() \=> {}}
|
|
2259
|
+
rules\={{
|
|
2260
|
+
required: 'Select at least one food of preference'
|
|
2261
|
+
}}
|
|
2262
|
+
/>
|
|
2263
|
+
<IressFormField
|
|
2264
|
+
label\="Terms and privacy policy"
|
|
2265
|
+
name\="terms"
|
|
2266
|
+
render\={() \=> {}}
|
|
2267
|
+
rules\={{
|
|
2268
|
+
required: 'You need to agree to the terms and conditions before proceeding'
|
|
2269
|
+
}}
|
|
2270
|
+
/>
|
|
2271
|
+
<IressFormField
|
|
2272
|
+
hint\="Type to copy an existing character's name"
|
|
2273
|
+
label\="Your star wars name"
|
|
2274
|
+
name\="star\_wars\_name"
|
|
2275
|
+
render\={() \=> {}}
|
|
2276
|
+
rules\={{
|
|
2277
|
+
required: true
|
|
2278
|
+
}}
|
|
2279
|
+
/>
|
|
2280
|
+
<IressFormField
|
|
2281
|
+
label\="What's your pain level?"
|
|
2282
|
+
name\="pain\_level"
|
|
2283
|
+
render\={() \=> {}}
|
|
2284
|
+
rules\={{
|
|
2285
|
+
required: true
|
|
2286
|
+
}}
|
|
2287
|
+
/>
|
|
2288
|
+
<IressFormField
|
|
2289
|
+
label\="Favourite email domains"
|
|
2290
|
+
name\="email\_domains"
|
|
2291
|
+
render\={() \=> {}}
|
|
2292
|
+
rules\={{
|
|
2293
|
+
required: true
|
|
2294
|
+
}}
|
|
2295
|
+
/>
|
|
2296
|
+
</ForwardedForm\>
|
|
2297
|
+
|
|
2298
|
+
```
|
|
2299
|
+
|
|
2300
|
+
#### Props
|
|
2301
|
+
|
|
2302
|
+
| Name | Description | Default | Control |
|
|
2303
|
+
| --- | --- | --- | --- |
|
|
2304
|
+
| actions |
|
|
2305
|
+
object
|
|
2306
|
+
|
|
2307
|
+
|
|
2308
|
+
|
|
2309
|
+
| \- | Choose option...nonesubmitsave |
|
|
2310
|
+
| alert |
|
|
2311
|
+
|
|
2312
|
+
object
|
|
2313
|
+
|
|
2314
|
+
|
|
2315
|
+
|
|
2316
|
+
| \- | Choose option...nonebasicAlert |
|
|
2317
|
+
| children |
|
|
2318
|
+
|
|
2319
|
+
array
|
|
2320
|
+
|
|
2321
|
+
|
|
2322
|
+
|
|
2323
|
+
| \- | Choose option...nonesimplesupportedControlsreadOnly |
|
|
2324
|
+
| heading |
|
|
2325
|
+
|
|
2326
|
+
string
|
|
2327
|
+
|
|
2328
|
+
|
|
2329
|
+
|
|
2330
|
+
| \- | Try hitting submit to see the validation summary |
|
|
2331
|
+
| pattern |
|
|
2332
|
+
|
|
2333
|
+
string
|
|
2334
|
+
|
|
2335
|
+
|
|
2336
|
+
|
|
2337
|
+
| \- | long |
|
|
2338
|
+
| Show React Hook Forms itemsReact Hook Forms | Show React Hook Forms items |
|
|
2339
|
+
|
|
2340
|
+
Migration to version 5 and beyond
|
|
2341
|
+
---------------------------------
|
|
2342
|
+
|
|
2343
|
+
The previous form components contained a lot of logic to translate the HTML5 validation API to a format that matched the design system’s guidelines. This allowed users to use the default props of input such as `pattern` and `required`, and be assured that the `IressField` would display errors accordingly.
|
|
2344
|
+
|
|
2345
|
+
Although this worked for simple forms, it did not work for forms which had complex business requirements. This was due to the logic inside the form components being hard to override. Additionally, it was seemingly impossible to implement the business requirements using the HTML5 validation API, which itself is very restricted.
|
|
2346
|
+
|
|
2347
|
+
In version 5 we have decided to provide two alternative methods of using form components to better accommodate our consumer’s needs.
|
|
2348
|
+
|
|
2349
|
+
The validation logic has been stripped from all of the existing form components. They are now closer to their native implementation, with a few customisations to match the IDS guidelines. IressField has transformed into a layout component to allow you to lay out form fields consistent with IDS guidelines, using your own validation tools.
|
|
2350
|
+
|
|
2351
|
+
Automated validation is now solely contained in `IressForm` and `IressFormField`, using [React Hook Form](https://react-hook-form.com/docs/useform) under the hood to simplify maintenance.
|
|
2352
|
+
|
|
2353
|
+
Testing
|
|
2354
|
+
-------
|
|
2355
|
+
|
|
2356
|
+
Unfortunately due to the asynchronous nature of React Hook Form validation, `IressForm` still needs to be tested using `screen.findBy` queries (at least in the first query after render). If `findBy` is not used, you will start to see the dreaded `act warnings`. For more information on testing IressForm, please refer to the (React Hook Form testing documentation)\[[https://react-hook-form.com/advanced-usage#TestingForm](https://react-hook-form.com/advanced-usage#TestingForm)\]
|
|
2357
|
+
|
|
2358
|
+
Here is an example of testing a form submission.
|
|
2359
|
+
|
|
2360
|
+
render(
|
|
2361
|
+
<IressForm\>
|
|
2362
|
+
<IressFormField
|
|
2363
|
+
label\="Email"
|
|
2364
|
+
name\="email"
|
|
2365
|
+
rules\={{ required: true }}
|
|
2366
|
+
render\={(controlledProps) \=> (
|
|
2367
|
+
<IressInput {...controlledProps} type\="email" />
|
|
2368
|
+
)}
|
|
2369
|
+
/>
|
|
2370
|
+
<IressButton type\="submit"\>Submit</IressButton\>
|
|
2371
|
+
</IressForm\>,
|
|
2372
|
+
);
|
|
2373
|
+
// May be needed sometimes to get over the act warning
|
|
2374
|
+
await screen.getByRole('form');
|
|
2375
|
+
const emailInput \= screen.getByRole('textbox');
|
|
2376
|
+
const submitButton \= screen.getByRole('button', { name: 'Submit' });
|
|
2377
|
+
await userEvent.click(submitButton);
|
|
2378
|
+
// Errors are asynchronous, so we need to wait for them to appear
|
|
2379
|
+
const summaryError \= await screen.findByText(
|
|
2380
|
+
'There was a problem submitting this form',
|
|
2381
|
+
);
|
|
2382
|
+
expect(summaryError).toBeInTheDocument();
|
|
2383
|
+
|
|
2384
|
+
```
|
|
2385
|
+
|
|
2386
|
+
Caveat
|
|
2387
|
+
------
|
|
2388
|
+
|
|
2389
|
+
### Using `IressSelect` with non-string values in `IressFormField`
|
|
2390
|
+
|
|
2391
|
+
When using form controls that support non-string values (like `IressSelect` with number values), you might encounter type conflicts with React Hook Form's default event handling. By default, React Hook Form expects string values from form controls.
|
|
2392
|
+
|
|
2393
|
+
**Problem:**
|
|
2394
|
+
|
|
2395
|
+
<IressFormField
|
|
2396
|
+
name\="dependents"
|
|
2397
|
+
label\="Dependents"
|
|
2398
|
+
render\={(controlledProps) \=> (
|
|
2399
|
+
<IressSelect {...controlledProps}\>
|
|
2400
|
+
<option key\="0" value\={0}\>
|
|
2401
|
+
0 </option\>
|
|
2402
|
+
<option key\="1" value\={1}\>
|
|
2403
|
+
1 </option\>
|
|
2404
|
+
<option key\="2" value\={2}\>
|
|
2405
|
+
2 </option\>
|
|
2406
|
+
</IressSelect\>
|
|
2407
|
+
)}
|
|
2408
|
+
/>
|
|
2409
|
+
|
|
2410
|
+
```
|
|
2411
|
+
|
|
2412
|
+
In this case, React Hook Form will try to convert the value to string, which might cause type issues.
|
|
2413
|
+
|
|
2414
|
+
**Solution:** Override the `onChange` handler to pass the actual value as a second parameter:
|
|
2415
|
+
|
|
2416
|
+
<IressFormField
|
|
2417
|
+
name\="dependents"
|
|
2418
|
+
label\="Dependents"
|
|
2419
|
+
render\={(controlledProps) \=> (
|
|
2420
|
+
<IressSelect
|
|
2421
|
+
{...controlledProps}
|
|
2422
|
+
onChange\={(\_e, value) \=> controlledProps.onChange(value)}
|
|
2423
|
+
\>
|
|
2424
|
+
<option key\="0" value\={0}\>
|
|
2425
|
+
0 </option\>
|
|
2426
|
+
<option key\="1" value\={1}\>
|
|
2427
|
+
1 </option\>
|
|
2428
|
+
<option key\="2" value\={2}\>
|
|
2429
|
+
2 </option\>
|
|
2430
|
+
</IressSelect\>
|
|
2431
|
+
)}
|
|
2432
|
+
/>
|
|
2433
|
+
|
|
2434
|
+
```
|
|
2435
|
+
|
|
2436
|
+
This ensures the original value type is preserved when passed to React Hook Form. You can find out more about the code example in the [Switch Edit Readonly Form Documentation](?path=/docs/components-form-recipes--docs#switching-between-readonly-and-edit-modes).
|
|
2437
|
+
|
|
2438
|
+
### Properly resetting fields
|
|
2439
|
+
|
|
2440
|
+
When resetting fields that accept non-string values (like `IressRichSelect`), you should reset them to `null` or `undefined` instead of an empty string. This is because the underlying component is strictly typed and expects a specific value type.
|
|
2441
|
+
|
|
2442
|
+
**Problem:**
|
|
2443
|
+
|
|
2444
|
+
const { resetField } \= IressForm.useFormContext();
|
|
2445
|
+
resetField('rich-select', {
|
|
2446
|
+
label: '',
|
|
2447
|
+
value: '',
|
|
2448
|
+
});
|
|
2449
|
+
|
|
2450
|
+
```
|
|
2451
|
+
|
|
2452
|
+
In this case, it will look like it cleared the field, but actually it has not. This is obvious with a `placeholder` set, as it will not show the placeholder.
|
|
2453
|
+
|
|
2454
|
+
**Solution:** Override the `onChange` handler to pass the actual value as a second parameter:
|
|
2455
|
+
|
|
2456
|
+
const { resetField } \= IressForm.useFormContext();
|
|
2457
|
+
resetField('rich-select', null); // or undefined
|
|
2458
|
+
|
|
2459
|
+
```
|
|
2460
|
+
|
|
2461
|
+
This will properly reset the field to null and clear the field value.
|
|
2462
|
+
|
|
2463
|
+
### Conflicting versions of React Hook Forms
|
|
2464
|
+
|
|
2465
|
+
If you are using React Hook Forms in your application, please ensure it is the same version IDS uses, otherwise there will be conflicts.
|
|
2466
|
+
|
|
2467
|
+
The version we are using in IDS is:
|
|
2468
|
+
|
|
2469
|
+
yarn add react-hook-form@7.55.0
|