@iress-oss/ids-mcp-server 5.15.0 → 6.0.0-alpha.1

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.
Files changed (91) hide show
  1. package/package.json +28 -46
  2. package/generated/docs/components-alert-docs.md +0 -130
  3. package/generated/docs/components-autocomplete-docs.md +0 -754
  4. package/generated/docs/components-autocomplete-recipes-docs.md +0 -104
  5. package/generated/docs/components-badge-docs.md +0 -148
  6. package/generated/docs/components-button-docs.md +0 -362
  7. package/generated/docs/components-button-recipes-docs.md +0 -76
  8. package/generated/docs/components-buttongroup-docs.md +0 -310
  9. package/generated/docs/components-card-docs.md +0 -494
  10. package/generated/docs/components-card-recipes-docs.md +0 -89
  11. package/generated/docs/components-checkbox-docs.md +0 -193
  12. package/generated/docs/components-checkboxgroup-docs.md +0 -692
  13. package/generated/docs/components-checkboxgroup-recipes-docs.md +0 -119
  14. package/generated/docs/components-col-docs.md +0 -466
  15. package/generated/docs/components-combobox-docs.md +0 -1016
  16. package/generated/docs/components-container-docs.md +0 -91
  17. package/generated/docs/components-divider-docs.md +0 -176
  18. package/generated/docs/components-expander-docs.md +0 -215
  19. package/generated/docs/components-field-docs.md +0 -675
  20. package/generated/docs/components-filter-docs.md +0 -1109
  21. package/generated/docs/components-form-docs.md +0 -2410
  22. package/generated/docs/components-form-recipes-docs.md +0 -886
  23. package/generated/docs/components-hide-docs.md +0 -265
  24. package/generated/docs/components-icon-docs.md +0 -553
  25. package/generated/docs/components-inline-docs.md +0 -868
  26. package/generated/docs/components-input-docs.md +0 -335
  27. package/generated/docs/components-input-recipes-docs.md +0 -140
  28. package/generated/docs/components-inputcurrency-docs.md +0 -157
  29. package/generated/docs/components-inputcurrency-recipes-docs.md +0 -116
  30. package/generated/docs/components-label-docs.md +0 -135
  31. package/generated/docs/components-menu-docs.md +0 -704
  32. package/generated/docs/components-menu-menuitem-docs.md +0 -193
  33. package/generated/docs/components-modal-docs.md +0 -587
  34. package/generated/docs/components-navbar-docs.md +0 -291
  35. package/generated/docs/components-navbar-recipes-docs.md +0 -413
  36. package/generated/docs/components-panel-docs.md +0 -380
  37. package/generated/docs/components-placeholder-docs.md +0 -27
  38. package/generated/docs/components-popover-docs.md +0 -464
  39. package/generated/docs/components-popover-recipes-docs.md +0 -245
  40. package/generated/docs/components-progress-docs.md +0 -104
  41. package/generated/docs/components-provider-docs.md +0 -105
  42. package/generated/docs/components-radio-docs.md +0 -107
  43. package/generated/docs/components-radiogroup-docs.md +0 -683
  44. package/generated/docs/components-readonly-docs.md +0 -89
  45. package/generated/docs/components-richselect-docs.md +0 -2433
  46. package/generated/docs/components-row-docs.md +0 -877
  47. package/generated/docs/components-select-docs.md +0 -456
  48. package/generated/docs/components-skeleton-docs.md +0 -214
  49. package/generated/docs/components-skeleton-recipes-docs.md +0 -76
  50. package/generated/docs/components-skiplink-docs.md +0 -66
  51. package/generated/docs/components-slideout-docs.md +0 -538
  52. package/generated/docs/components-slider-docs.md +0 -346
  53. package/generated/docs/components-spinner-docs.md +0 -59
  54. package/generated/docs/components-stack-docs.md +0 -265
  55. package/generated/docs/components-table-ag-grid-docs.md +0 -1074
  56. package/generated/docs/components-table-docs.md +0 -1305
  57. package/generated/docs/components-tabset-docs.md +0 -341
  58. package/generated/docs/components-tabset-tab-docs.md +0 -86
  59. package/generated/docs/components-tag-docs.md +0 -115
  60. package/generated/docs/components-text-docs.md +0 -394
  61. package/generated/docs/components-toaster-docs.md +0 -345
  62. package/generated/docs/components-toaster-toast-docs.md +0 -157
  63. package/generated/docs/components-toggle-docs.md +0 -158
  64. package/generated/docs/components-tooltip-docs.md +0 -311
  65. package/generated/docs/components-validationmessage-docs.md +0 -241
  66. package/generated/docs/contact-us-docs.md +0 -27
  67. package/generated/docs/extensions-editor-docs.md +0 -288
  68. package/generated/docs/extensions-editor-recipes-docs.md +0 -39
  69. package/generated/docs/foundations-accessibility-docs.md +0 -62
  70. package/generated/docs/foundations-colours-docs.md +0 -257
  71. package/generated/docs/foundations-consistency-docs.md +0 -52
  72. package/generated/docs/foundations-content-docs.md +0 -23
  73. package/generated/docs/foundations-introduction-docs.md +0 -17
  74. package/generated/docs/foundations-principles-docs.md +0 -70
  75. package/generated/docs/foundations-typography-docs.md +0 -191
  76. package/generated/docs/foundations-user-experience-docs.md +0 -63
  77. package/generated/docs/foundations-visual-design-docs.md +0 -46
  78. package/generated/docs/frequently-asked-questions-docs.md +0 -53
  79. package/generated/docs/get-started-develop-docs.md +0 -209
  80. package/generated/docs/get-started-using-storybook-docs.md +0 -68
  81. package/generated/docs/guidelines.md +0 -812
  82. package/generated/docs/introduction-docs.md +0 -43
  83. package/generated/docs/patterns-loading-docs.md +0 -1304
  84. package/generated/docs/resources-changelog-docs.md +0 -6
  85. package/generated/docs/resources-code-katas-docs.md +0 -29
  86. package/generated/docs/resources-migration-guides-from-v4-to-v5-docs.md +0 -437
  87. package/generated/docs/themes-available-themes-docs.md +0 -66
  88. package/generated/docs/themes-introduction-docs.md +0 -121
  89. package/generated/docs/themes-tokens-docs.md +0 -1200
  90. package/generated/docs/versions-docs.md +0 -17
  91. /package/{LICENSE.txt → LICENSE} +0 -0
@@ -1,2410 +0,0 @@
1
- [](#form)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
- \*Required Name
10
-
11
- \*Required Email address
12
-
13
- Sign up
14
-
15
- Hide code
16
-
17
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
18
-
19
- <IressForm\>
20
- <IressStack gutter\="md"\>
21
- <IressFormField
22
- label\="Name"
23
- name\="name"
24
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
25
- rules\={{
26
- required: 'Name is required'
27
- }}
28
- />
29
- <IressFormField
30
- label\="Email address"
31
- name\="email"
32
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
33
- rules\={{
34
- minLength: {
35
- message: 'Use a longer email address',
36
- value: 6
37
- },
38
- required: 'Email is required'
39
- }}
40
- />
41
- <IressButton
42
- mode\="primary"
43
- type\="submit"
44
- \>
45
- Sign up </IressButton\>
46
- </IressStack\>
47
- </IressForm\>
48
-
49
- Copy
50
-
51
- [](#key-concepts)Key concepts
52
- -----------------------------
53
-
54
- 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.
55
-
56
- ### [](#state-management)State management
57
-
58
- `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).
59
-
60
- Due to this change, there are a few things you should consider during development:
61
-
62
- * 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.
63
- * 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.
64
-
65
- See below an example comparing a version 4 and version 5 `IressForm` when managing form state.
66
-
67
- Mode
68
-
69
- DiffOldNew
70
-
71
- <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 = () =&gt; {</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 = () =&gt; {</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"> &lt;IressForm&gt;</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"> &lt;&gt;</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"> &lt;IressField label="Show fields"&gt;</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"> &lt;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"> &lt;IressCheckboxGroup value={show} onChange={(newValues) =&gt; setShow(newValues)}&gt;</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) =&gt; (</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"> &lt;IressCheckboxGroup {...controlledProps}&gt;</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"> &lt;IressCheckbox value="name"&gt;Name&lt;/IressCheckbox&gt;</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"> &lt;IressCheckbox value="email"&gt;Email&lt;/IressCheckbox&gt;</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"> &lt;/IressCheckboxGroup&gt;</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"> &lt;/IressField&gt;</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') &amp;&amp; (</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"> /&gt;</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"> &lt;IressField label="Name"&gt;</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') &amp;&amp; (</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"> &lt;IressInput name="name" /&gt;</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"> &lt;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"> &lt;/IressField&gt;</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') &amp;&amp; (</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) =&gt; &lt;IressInput {...controlledProps} /&gt;}</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"> &lt;IressField label="Email"&gt;</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"> /&gt;</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"> &lt;IressInput name="email" type="email" /&gt;</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"> &lt;/IressField&gt;</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') &amp;&amp; (</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"> &lt;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"> &lt;/IressForm&gt;</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) =&gt; &lt;IressInput {...controlledProps} type="email" /&gt;}</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"> /&gt;</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"> &lt;/&gt;</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 = () =&gt; (</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"> &lt;IressForm defaultValues={{ show: ['name'] }}&gt;</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"> &lt;ConditionalFields /&gt; </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"> &lt;/IressForm&gt;</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>
72
-
73
- ### [](#validation)Validation
74
-
75
- 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.
76
-
77
- Due to this change, there are a few things you should consider during development:
78
-
79
- * 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.
80
- * 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).
81
- * 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`.
82
-
83
- See below an example comparing a version 4 and version 5 `IressForm` when adding validation rules.
84
-
85
- Mode
86
-
87
- DiffOldNew
88
-
89
- <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 = () =&gt; (</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"> &lt;IressForm valueMissing="{{fieldName}} needs to be filled in!"&gt;</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"> &lt;IressForm&gt;</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"> &lt;IressField label="Name"&gt;</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"> &lt;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"> &lt;IressInput name="name" required /&gt;</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"> &lt;/IressField&gt;</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"> &lt;IressField label="Email"&gt;</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) =&gt; &lt;IressInput {...controlledProps} /&gt;}</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"> &lt;IressInput name="email" maxLength={10} /&gt;</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"> &lt;/IressField&gt;</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"> /&gt;</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"> &lt;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) =&gt; &lt;IressInput {...controlledProps} type="email" maxLength={10} /&gt;}</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"> /&gt;</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"> &lt;IressButton type="submit" mode="primary"&gt;</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"> &lt;/IressButton&gt;</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"> &lt;/IressForm&gt;</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>
90
-
91
- ### [](#syncing-state)Syncing state
92
-
93
- 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.
94
-
95
- 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.
96
-
97
- Consider the following for your development:
98
-
99
- * 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.
100
- * If you need to set form with data coming from an external system, use the `ref` of the form to `reset` the form values.
101
-
102
- \[data-radix-scroll-area-viewport\] {
103
- scrollbar-width: none;
104
- -ms-overflow-style: none;
105
- -webkit-overflow-scrolling: touch;
106
- }
107
- \[data-radix-scroll-area-viewport\]::-webkit-scrollbar {
108
- display: none;
109
- }
110
- :where(\[data-radix-scroll-area-viewport\]) {
111
- display: flex;
112
- flex-direction: column;
113
- align-items: stretch;
114
- }
115
- :where(\[data-radix-scroll-area-content\]) {
116
- flex-grow: 1;
117
- }
118
-
119
- const ref \= useRef<FormRef | null\>(null);
120
- const api \= useApi();
121
- const handleSubmit \= async (data) \=> {
122
- // Sync the form data with your state
123
- const details \= await api.updateUser(data);
124
- // Update the form with the new data
125
- ref.current?.reset(details);
126
- };
127
- return (
128
- <IressForm onSubmit\={handleSubmit} ref\={ref}\>
129
- ... </IressForm\>
130
- );
131
-
132
- Copy
133
-
134
- [](#usage)Usage
135
- ---------------
136
-
137
- ### [](#fields)Fields
138
-
139
- 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.
140
-
141
- It has three required props:
142
-
143
- * `name`: The name of the field, which will be used to identify the field in the form data.
144
- * `label`: The label for the field.
145
- * `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.
146
-
147
- \[data-radix-scroll-area-viewport\] {
148
- scrollbar-width: none;
149
- -ms-overflow-style: none;
150
- -webkit-overflow-scrolling: touch;
151
- }
152
- \[data-radix-scroll-area-viewport\]::-webkit-scrollbar {
153
- display: none;
154
- }
155
- :where(\[data-radix-scroll-area-viewport\]) {
156
- display: flex;
157
- flex-direction: column;
158
- align-items: stretch;
159
- }
160
- :where(\[data-radix-scroll-area-content\]) {
161
- flex-grow: 1;
162
- }
163
-
164
- <IressFormField
165
- name\="email"
166
- label\="Email"
167
- render\={(controlledProps) \=> <IressInput {...controlledProps} type\="email" />}
168
- />
169
-
170
- Copy
171
-
172
- #### [](#supported-form-controls)Supported form controls
173
-
174
- 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.
175
-
176
- \*Required Name
177
-
178
- \*Required Date
179
-
180
- \*Required Gender
181
-
182
- SelectMaleFemaleOther
183
-
184
- \*Required Gender with icons
185
-
186
- MaleFemaleOther
187
-
188
- \*Required Hobbies
189
-
190
- Reading
191
-
192
- Writing
193
-
194
- \*Required Food of preference
195
-
196
- Steak
197
-
198
- Fish
199
-
200
- Salad
201
-
202
- \*Required I agree to the terms and conditions
203
-
204
- \*Required Your star wars name
205
-
206
- Type to copy an existing character's name
207
-
208
- \*Required Your star wars sidekick
209
-
210
- None selected
211
-
212
- \*Required Star wars crew
213
-
214
- None selected
215
-
216
- \*Required What's your pain level?
217
-
218
- 0
219
-
220
- \*Required Favourite email domains
221
-
222
- Sign up
223
-
224
- Hide code
225
-
226
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
227
-
228
- <IressForm\>
229
- <IressStack gutter\="md"\>
230
- <IressFormField
231
- label\="Name"
232
- name\="name"
233
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
234
- rules\={{
235
- required: true
236
- }}
237
- />
238
- <IressFormField
239
- label\="Date"
240
- name\="date"
241
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
242
- rules\={{
243
- required: true
244
- }}
245
- />
246
- <IressFormField
247
- label\="Gender"
248
- name\="gender"
249
- render\={(controlledProps) \=> (
250
- <IressSelect {...controlledProps} placeholder\="Select"\>
251
- <option\>Male</option\>
252
- <option\>Female</option\>
253
- <option\>Other</option\>
254
- </IressSelect\>
255
- )}
256
- rules\={{
257
- required: true
258
- }}
259
- />
260
- <IressFormField
261
- label\="Gender with icons"
262
- name\="gender-with-icons"
263
- render\={(controlledProps) \=> (
264
- <IressRichSelect
265
- {...controlledProps}
266
- options\={\[
267
- {
268
- label: 'Male',
269
- value: 'male',
270
- prepend: <IressIcon name\="mars" />,
271
- },
272
- {
273
- label: 'Female',
274
- value: 'female',
275
- prepend: <IressIcon name\="venus" />,
276
- },
277
- {
278
- label: 'Other',
279
- value: 'other',
280
- prepend: <IressIcon name\="otter" />,
281
- },
282
- \]} />
283
- )}
284
- rules\={{
285
- required: true
286
- }}
287
- />
288
- <IressFormFieldset
289
- label\="Hobbies"
290
- name\="hobbies"
291
- render\={(controlledProps) \=> (
292
- <IressCheckboxGroup {...controlledProps}\>
293
- <IressCheckbox value\="reading"\>Reading</IressCheckbox\>
294
- <IressCheckbox value\="writing"\>Writing</IressCheckbox\>
295
- </IressCheckboxGroup\>
296
- )}
297
- rules\={{
298
- required: 'Select at least one hobby'
299
- }}
300
- />
301
- <IressFormFieldset
302
- label\="Food of preference"
303
- name\="food"
304
- render\={(controlledProps) \=> (
305
- <IressRadioGroup {...controlledProps}\>
306
- <IressRadio value\="steak"\>Steak</IressRadio\>
307
- <IressRadio value\="fish"\>Fish</IressRadio\>
308
- <IressRadio value\="salad"\>Salad</IressRadio\>
309
- </IressRadioGroup\>
310
- )}
311
- rules\={{
312
- required: 'Select at least one food of preference'
313
- }}
314
- />
315
- <IressFormField
316
- label\="I agree to the terms and conditions"
317
- name\="terms"
318
- render\={(controlledProps) \=> (
319
- <IressCheckbox {...controlledProps} defaultChecked\={controlledProps.value} />
320
- )}
321
- rules\={{
322
- required: 'You need to agree to the terms and conditions before proceeding'
323
- }}
324
- />
325
- <IressFormField
326
- hint\="Type to copy an existing character's name"
327
- label\="Your star wars name"
328
- name\="star\_wars\_name"
329
- render\={(controlledProps) \=> (
330
- <IressAutocomplete
331
- {...controlledProps}
332
- options\={searchStarWarsCharacters}
333
- onClear\={(event) \=> {
334
- controlledProps.onChange(event);
335
- }}
336
- />
337
- )}
338
- rules\={{
339
- required: true
340
- }}
341
- />
342
- <IressFormField
343
- label\="Your star wars sidekick"
344
- name\="star\_wars\_sidekick"
345
- render\={(controlledProps) \=> (
346
- <IressCombobox
347
- {...controlledProps}
348
- options\={searchStarWarsCharacters}
349
- onClear\={(event) \=> {
350
- controlledProps.onChange(event);
351
- }}
352
- />
353
- )}
354
- rules\={{
355
- required: true
356
- }}
357
- />
358
- <IressFormField
359
- label\="Star wars crew"
360
- name\="star\_wars\_crew"
361
- render\={(controlledProps) \=> (
362
- <IressMultiCombobox
363
- {...controlledProps}
364
- options\={searchStarWarsCharacters}
365
- onClear\={(event) \=> {
366
- controlledProps.onChange(event);
367
- }}
368
- />
369
- )}
370
- rules\={{
371
- required: true
372
- }}
373
- />
374
- <IressFormField
375
- label\="What's your pain level?"
376
- name\="pain\_level"
377
- render\={(controlledProps) \=> <IressSlider {...controlledProps} />}
378
- rules\={{
379
- required: true
380
- }}
381
- />
382
- <IressFormField
383
- label\="Favourite email domains"
384
- name\="email\_domains"
385
- render\={(controlledProps) \=> <IressTagInput {...controlledProps} />}
386
- rules\={{
387
- required: true
388
- }}
389
- />
390
- <IressButton
391
- mode\="primary"
392
- type\="submit"
393
- \>
394
- Sign up </IressButton\>
395
- </IressStack\>
396
- </IressForm\>
397
-
398
- Copy
399
-
400
- ### [](#rules)Rules
401
-
402
- 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.
403
-
404
- **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`.
405
-
406
- #### [](#required)`required`
407
-
408
- 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.
409
-
410
- \*Required Default message
411
-
412
- \*Required Custom message
413
-
414
- Validate
415
-
416
- Show code
417
-
418
- #### [](#maxlength)`maxLength`
419
-
420
- The maximum character length of the value to accept for this input.
421
-
422
- **Notes**
423
-
424
- * For `IressInput`, you should also set the `maxLength` to stop the user from entering more characters than allowed.
425
- * Only applies to: `IressAutocomplete`, `IressInput`, `IressRadioGroup` and `IressSelect`.
426
-
427
- Default message
428
-
429
- Enter a maximum length of 5 characters
430
-
431
- Custom message
432
-
433
- Enter a maximum length of 5 characters
434
-
435
- Validate
436
-
437
- Hide code
438
-
439
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
440
-
441
- <IressForm\>
442
- <IressStack gutter\="lg"\>
443
- <IressFormField
444
- hint\="Enter a maximum length of 5 characters"
445
- label\="Default message"
446
- name\="IressInput-default"
447
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
448
- rules\={{
449
- maxLength: 5
450
- }}
451
- />
452
- <IressFormField
453
- hint\="Enter a maximum length of 5 characters"
454
- label\="Custom message"
455
- name\="IressInput-custom"
456
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
457
- rules\={{
458
- maxLength: {
459
- message: 'Please enter a max length of 5 characters!',
460
- value: 5
461
- }
462
- }}
463
- />
464
- <IressButton
465
- mode\="primary"
466
- type\="submit"
467
- \>
468
- Validate </IressButton\>
469
- </IressStack\>
470
- </IressForm\>
471
-
472
- Copy
473
-
474
- #### [](#minlength)`minLength`
475
-
476
- The minimum character length of the value to accept for this input.
477
-
478
- **Notes**
479
-
480
- * For `IressInput`, you should also set the `minLength` to stop the user from entering more characters than allowed.
481
- * Only applies to: `IressAutocomplete`, `IressInput`, `IressRadioGroup` and `IressSelect`.
482
-
483
- Default message
484
-
485
- Enter a minimum length of 7 characters
486
-
487
- Custom message
488
-
489
- Enter a minimum length of 7 characters
490
-
491
- Validate
492
-
493
- Hide code
494
-
495
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
496
-
497
- <IressForm\>
498
- <IressStack gutter\="lg"\>
499
- <IressFormField
500
- hint\="Enter a minimum length of 7 characters"
501
- label\="Default message"
502
- name\="IressInput-default"
503
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
504
- rules\={{
505
- minLength: 7
506
- }}
507
- />
508
- <IressFormField
509
- hint\="Enter a minimum length of 7 characters"
510
- label\="Custom message"
511
- name\="IressInput-custom"
512
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
513
- rules\={{
514
- minLength: {
515
- message: 'Please enter a min length of 7 characters!',
516
- value: 7
517
- }
518
- }}
519
- />
520
- <IressButton
521
- mode\="primary"
522
- type\="submit"
523
- \>
524
- Validate </IressButton\>
525
- </IressStack\>
526
- </IressForm\>
527
-
528
- Copy
529
-
530
- #### [](#max)`max`
531
-
532
- The maximum number to accept for this input.
533
-
534
- **Notes**
535
-
536
- * Only applies to: `IressAutocomplete`, `IressInput`, `IressRadioGroup` and `IressSelect`.
537
-
538
- Default message
539
-
540
- Select a maximum of 2
541
-
542
- Custom message
543
-
544
- Select a maximum of 2
545
-
546
- Validate
547
-
548
- Hide code
549
-
550
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
551
-
552
- <IressForm\>
553
- <IressStack gutter\="lg"\>
554
- <IressFormField
555
- hint\="Select a maximum of 2"
556
- label\="Default message"
557
- name\="IressInput-default"
558
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
559
- rules\={{
560
- max: 2
561
- }}
562
- />
563
- <IressFormField
564
- hint\="Select a maximum of 2"
565
- label\="Custom message"
566
- name\="IressInput-custom"
567
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
568
- rules\={{
569
- max: {
570
- message: 'Please enter a max of 2!',
571
- value: 2
572
- }
573
- }}
574
- />
575
- <IressButton
576
- mode\="primary"
577
- type\="submit"
578
- \>
579
- Validate </IressButton\>
580
- </IressStack\>
581
- </IressForm\>
582
-
583
- Copy
584
-
585
- #### [](#min)`min`
586
-
587
- The minimum number to accept for this input.
588
-
589
- **Notes**
590
-
591
- * Only applies to: `IressAutocomplete`, `IressInput`, `IressRadioGroup` and `IressSelect`.
592
-
593
- Default message
594
-
595
- Select a minimum of 20
596
-
597
- Custom message
598
-
599
- Select a minimum of 20
600
-
601
- Validate
602
-
603
- Hide code
604
-
605
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
606
-
607
- <IressForm\>
608
- <IressStack gutter\="lg"\>
609
- <IressFormField
610
- hint\="Select a minimum of 20"
611
- label\="Default message"
612
- name\="IressInput-default"
613
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
614
- rules\={{
615
- min: 20
616
- }}
617
- />
618
- <IressFormField
619
- hint\="Select a minimum of 20"
620
- label\="Custom message"
621
- name\="IressInput-custom"
622
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
623
- rules\={{
624
- min: {
625
- message: 'Please enter a min of 20!',
626
- value: 20
627
- }
628
- }}
629
- />
630
- <IressButton
631
- mode\="primary"
632
- type\="submit"
633
- \>
634
- Validate </IressButton\>
635
- </IressStack\>
636
- </IressForm\>
637
-
638
- Copy
639
-
640
- #### [](#pattern)`pattern`
641
-
642
- The accepted regex pattern for the input.
643
-
644
- **Notes**
645
-
646
- * Only applies to: `IressAutocomplete`, `IressInput`, `IressRadioGroup` and `IressSelect`.
647
-
648
- Default message
649
-
650
- Enter a valid email address
651
-
652
- Custom message
653
-
654
- Enter a valid email address
655
-
656
- Validate
657
-
658
- Hide code
659
-
660
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
661
-
662
- <IressForm\>
663
- <IressStack gutter\="lg"\>
664
- <IressFormField
665
- hint\="Enter a valid email address"
666
- label\="Default message"
667
- name\="IressInput-default"
668
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
669
- rules\={{
670
- 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\])?)\*$/
671
- }}
672
- />
673
- <IressFormField hint="Enter a valid email address" label="Custom message" name="IressInput-custom" render={(controlledProps) \=> <IressInput {...controlledProps} />}
674
- rules={{
675
- pattern: {
676
- message: 'Please enter a valid email address!',
677
- 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\])?)\*$/
678
- }
679
- }}
680
- /> <IressButton
681
- mode\="primary"
682
- type\="submit"
683
- \>
684
- Validate </IressButton\>
685
- </IressStack\>
686
- </IressForm\>
687
-
688
- Copy
689
-
690
- #### [](#mindate)`minDate`
691
-
692
- The minimum date to accept for this input.
693
-
694
- **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.
695
-
696
- Default message
697
-
698
- Enter a date after today
699
-
700
- Custom message
701
-
702
- Enter a date after today
703
-
704
- Validate
705
-
706
- Hide code
707
-
708
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
709
-
710
- <IressForm\>
711
- <IressStack gutter\="lg"\>
712
- <IressFormField
713
- hint\="Enter a date after today"
714
- label\="Default message"
715
- name\="IressInputDate-default"
716
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
717
- rules\={{
718
- minDate: new Date('2025-06-27T03:58:57.380Z')
719
- }}
720
- />
721
- <IressFormField
722
- hint\="Enter a date after today"
723
- label\="Custom message"
724
- name\="IressInputDate-custom"
725
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
726
- rules\={{
727
- minDate: {
728
- message: 'Please enter a date after today!',
729
- value: new Date('2025-06-27T03:58:57.380Z')
730
- }
731
- }}
732
- />
733
- <IressButton
734
- mode\="primary"
735
- type\="submit"
736
- \>
737
- Validate </IressButton\>
738
- </IressStack\>
739
- </IressForm\>
740
-
741
- Copy
742
-
743
- #### [](#maxdate)`maxDate`
744
-
745
- The maximum date to accept for this input.
746
-
747
- **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.
748
-
749
- Default message
750
-
751
- Enter a date before today
752
-
753
- Custom message
754
-
755
- Enter a date before today
756
-
757
- Validate
758
-
759
- Hide code
760
-
761
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
762
-
763
- <IressForm\>
764
- <IressStack gutter\="lg"\>
765
- <IressFormField
766
- hint\="Enter a date before today"
767
- label\="Default message"
768
- name\="IressInputDate-default"
769
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
770
- rules\={{
771
- maxDate: new Date('2025-06-27T03:58:57.380Z')
772
- }}
773
- />
774
- <IressFormField
775
- hint\="Enter a date before today"
776
- label\="Custom message"
777
- name\="IressInputDate-custom"
778
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
779
- rules\={{
780
- maxDate: {
781
- message: 'Please enter a date before today!',
782
- value: new Date('2025-06-27T03:58:57.380Z')
783
- }
784
- }}
785
- />
786
- <IressButton
787
- mode\="primary"
788
- type\="submit"
789
- \>
790
- Validate </IressButton\>
791
- </IressStack\>
792
- </IressForm\>
793
-
794
- Copy
795
-
796
- #### [](#email)`email`
797
-
798
- Ensures the input is a valid email address.
799
-
800
- **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.
801
-
802
- Default message
803
-
804
- Enter an email address
805
-
806
- Custom message
807
-
808
- Enter an email address
809
-
810
- Validate
811
-
812
- Hide code
813
-
814
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
815
-
816
- <IressForm\>
817
- <IressStack gutter\="lg"\>
818
- <IressFormField
819
- hint\="Enter an email address"
820
- label\="Default message"
821
- name\="IressInput-default"
822
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
823
- rules\={{
824
- email: true
825
- }}
826
- />
827
- <IressFormField
828
- hint\="Enter an email address"
829
- label\="Custom message"
830
- name\="IressInput-custom"
831
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
832
- rules\={{
833
- email: 'Please enter a valid email address!'
834
- }}
835
- />
836
- <IressButton
837
- mode\="primary"
838
- type\="submit"
839
- \>
840
- Validate </IressButton\>
841
- </IressStack\>
842
- </IressForm\>
843
-
844
- Copy
845
-
846
- #### [](#validate)`validate`
847
-
848
- 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.
849
-
850
- **Notes**
851
-
852
- * 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.
853
-
854
- Default message
855
-
856
- If checkbox, click two items to pass. Anything else, make sure it is Google.
857
-
858
- Reading
859
-
860
- Writing
861
-
862
- Custom message
863
-
864
- If checkbox, click two items to pass. Anything else, make sure it is Google.
865
-
866
- Reading
867
-
868
- Writing
869
-
870
- Validate
871
-
872
- Hide code
873
-
874
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
875
-
876
- <IressForm\>
877
- <IressStack gutter\="lg"\>
878
- <IressFormFieldset
879
- hint\="If checkbox, click two items to pass. Anything else, make sure it is Google."
880
- label\="Default message"
881
- name\="IressCheckboxGroup-default"
882
- render\={(controlledProps) \=> (
883
- <IressCheckboxGroup {...controlledProps}\>
884
- <IressCheckbox value\="reading"\>Reading</IressCheckbox\>
885
- <IressCheckbox value\="writing"\>Writing</IressCheckbox\>
886
- </IressCheckboxGroup\>
887
- )}
888
- rules\={{
889
- validate: {
890
- atLeastTwoItems: (value) \=> Array.isArray(value) ? value.length \>= 2 : true,
891
- isGoogle: (value) \=> Array.isArray(value) ? true : value \=== "Google"
892
- }
893
- }}
894
- />
895
- <IressFormFieldset
896
- hint\="If checkbox, click two items to pass. Anything else, make sure it is Google."
897
- label\="Custom message"
898
- name\="IressCheckboxGroup-custom"
899
- render\={(controlledProps) \=> (
900
- <IressCheckboxGroup {...controlledProps}\>
901
- <IressCheckbox value\="reading"\>Reading</IressCheckbox\>
902
- <IressCheckbox value\="writing"\>Writing</IressCheckbox\>
903
- </IressCheckboxGroup\>
904
- )}
905
- rules\={{
906
- validate: {
907
- atLeastTwoItems: (value) \=> (Array.isArray(value) ? value.length \>= 2 : true) || "Please select at least two items!",
908
- isGoogle: (value) \=> (Array.isArray(value) ? true : value \=== "Google") || "I guess you are not evil!"
909
- }
910
- }}
911
- />
912
- <IressButton
913
- mode\="primary"
914
- type\="submit"
915
- \>
916
- Validate </IressButton\>
917
- </IressStack\>
918
- </IressForm\>
919
-
920
- Copy
921
-
922
- ### [](#handling-submission)Handling submission
923
-
924
- 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.
925
-
926
- \*Required Name
927
-
928
- \*Required Email address
929
-
930
- Sign up
931
-
932
- Hide code
933
-
934
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
935
-
936
- import {
937
- IressTable,
938
- IressFormProps,
939
- IressForm,
940
- IressModal,
941
- IressFormField,
942
- IressInput,
943
- IressStack,
944
- IressButton,
945
- } from '@iress-oss/ids-components';
946
- import { useState } from 'react';
947
- interface FieldValues {
948
- name?: string;
949
- email?: string;
950
- }
951
- export const FormSubmission \= () \=> {
952
- const \[submitted, setSubmitted\] \= useState<FieldValues | undefined\>(
953
- undefined,
954
- );
955
- return (
956
- <IressForm onSubmit\={(data) \=> setSubmitted(data)}\>
957
- <IressStack gutter\="md"\>
958
- <IressFormField
959
- label\="Name"
960
- name\="name"
961
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
962
- rules\={{
963
- required: 'Name is required',
964
- }}
965
- />
966
- <IressFormField
967
- label\="Email address"
968
- name\="email"
969
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
970
- rules\={{
971
- minLength: {
972
- message: 'Use a longer email address',
973
- value: 6,
974
- },
975
- required: 'Email is required',
976
- }}
977
- />
978
- <IressButton mode\="primary" type\="submit"\>
979
- Sign up </IressButton\>
980
- </IressStack\>
981
- <IressModal
982
- show\={!!submitted}
983
- onShowChange\={(show) \=> !show && setSubmitted(undefined)}
984
- \>
985
- <IressTable
986
- caption\="Submitted details"
987
- rows\={Object.entries(submitted ?? {}).map((entry) \=> ({
988
- name: entry\[0\],
989
- value: JSON.stringify(entry\[1\], null, 2),
990
- }))}
991
- />
992
- </IressModal\>
993
- </IressForm\>
994
- );
995
- };
996
-
997
- Copy
998
-
999
- ### [](#read-only)Read only
1000
-
1001
- 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.
1002
-
1003
- Try hitting submit to see the validation summary: only email is being validated
1004
- -------------------------------------------------------------------------------
1005
-
1006
- Name
1007
-
1008
- Luke Skywalker
1009
-
1010
- \*Required Email address
1011
-
1012
- Sign up
1013
-
1014
- Hide code
1015
-
1016
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
1017
-
1018
- <IressStack gutter\="md"\>
1019
- <IressText element\="h2"\>
1020
- Try hitting submit to see the validation summary: only email is being validated </IressText\>
1021
- <IressForm\>
1022
- <IressStack gutter\="md"\>
1023
- <IressFormField
1024
- label\="Name"
1025
- name\="name"
1026
- readOnly
1027
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
1028
- rules\={{
1029
- required: 'Name is required'
1030
- }}
1031
- />
1032
- <IressFormField
1033
- label\="Email address"
1034
- name\="email"
1035
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
1036
- rules\={{
1037
- minLength: {
1038
- message: 'Use a longer email address',
1039
- value: 6
1040
- },
1041
- required: 'Email is required'
1042
- }}
1043
- />
1044
- <IressButton
1045
- mode\="primary"
1046
- type\="submit"
1047
- \>
1048
- Sign up </IressButton\>
1049
- </IressStack\>
1050
- </IressForm\>
1051
- </IressStack\>
1052
-
1053
- Copy
1054
-
1055
- [](#behaviour)Behaviour
1056
- -----------------------
1057
-
1058
- * 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.
1059
- * 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.
1060
- * After the first submission, fields are validated on change, to provide users instant feedback as they are now at the validation phase.
1061
-
1062
- **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`.
1063
-
1064
- [](#examples)Examples
1065
- ---------------------
1066
-
1067
- ### [](#validation-summary)Validation summary
1068
-
1069
- If errors are present, they are displayed in a summary at the top of the page. This can be customised using the `errorSummaryHeading` prop.
1070
-
1071
- Try hitting submit to see the validation summary
1072
- ------------------------------------------------
1073
-
1074
- \*Required Name
1075
-
1076
- \*Required Email address
1077
-
1078
- Sign up
1079
-
1080
- Hide code
1081
-
1082
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
1083
-
1084
- <IressStack gutter\="md"\>
1085
- <IressText element\="h2"\>
1086
- Try hitting submit to see the validation summary </IressText\>
1087
- <IressForm errorSummaryHeading\="We have seen the following errors in your submission..."\>
1088
- <IressStack gutter\="md"\>
1089
- <IressFormField
1090
- label\="Name"
1091
- name\="name"
1092
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
1093
- rules\={{
1094
- required: 'Name is required'
1095
- }}
1096
- />
1097
- <IressFormField
1098
- label\="Email address"
1099
- name\="email"
1100
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
1101
- rules\={{
1102
- minLength: {
1103
- message: 'Use a longer email address',
1104
- value: 6
1105
- },
1106
- required: 'Email is required'
1107
- }}
1108
- />
1109
- <IressButton
1110
- mode\="primary"
1111
- type\="submit"
1112
- \>
1113
- Sign up </IressButton\>
1114
- </IressStack\>
1115
- </IressForm\>
1116
- </IressStack\>
1117
-
1118
- Copy
1119
-
1120
- ### [](#hidden-validation-summary)Hidden validation summary
1121
-
1122
- Use the `hiddenErrorSummary` prop to hide the summary altogether, and only show the errors on the fields. This is only recommended for short forms, for long forms it is recommended to keep the error summary shown for best accessibility.
1123
-
1124
- Try hitting submit to see the errors only on the fields
1125
- -------------------------------------------------------
1126
-
1127
- \*Required Name
1128
-
1129
- \*Required Email address
1130
-
1131
- Sign up
1132
-
1133
- Hide code
1134
-
1135
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
1136
-
1137
- <IressStack gutter\="md"\>
1138
- <IressText element\="h2"\>
1139
- Try hitting submit to see the errors only on the fields </IressText\>
1140
- <IressForm hiddenErrorSummary\>
1141
- <IressStack gutter\="md"\>
1142
- <IressFormField
1143
- label\="Name"
1144
- name\="name"
1145
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
1146
- rules\={{
1147
- required: 'Name is required'
1148
- }}
1149
- />
1150
- <IressFormField
1151
- label\="Email address"
1152
- name\="email"
1153
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
1154
- rules\={{
1155
- minLength: {
1156
- message: 'Use a longer email address',
1157
- value: 6
1158
- },
1159
- required: 'Email is required'
1160
- }}
1161
- />
1162
- <IressButton
1163
- mode\="primary"
1164
- type\="submit"
1165
- \>
1166
- Sign up </IressButton\>
1167
- </IressStack\>
1168
- </IressForm\>
1169
- </IressStack\>
1170
-
1171
- Copy
1172
-
1173
- ### [](#custom-error-handling)Custom error handling
1174
-
1175
- 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).
1176
-
1177
- One use case for this prop is to create your own error summary.
1178
-
1179
- \*Required Name
1180
-
1181
- \*Required Email address
1182
-
1183
- Sign up
1184
-
1185
- Hide code
1186
-
1187
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
1188
-
1189
- import {
1190
- IressTable,
1191
- IressFormProps,
1192
- IressForm,
1193
- IressModal,
1194
- IressStack,
1195
- IressFormField,
1196
- IressInput,
1197
- IressButton,
1198
- } from '@iress-oss/ids-components';
1199
- import { useState } from 'react';
1200
- import { FieldErrors } from 'react-hook-form';
1201
- interface FieldValues {
1202
- name?: string;
1203
- email?: string;
1204
- }
1205
- export const CustomErrorHandlingForm \= () \=> {
1206
- const \[errors, setErrors\] \= useState<FieldErrors<FieldValues\> | undefined\>(
1207
- undefined,
1208
- );
1209
- return (
1210
- <IressForm {...{
1211
- hiddenErrorSummary: true,
1212
- }} onError\={(data) \=> setErrors(data)}\>
1213
- <IressStack gutter\="md"\>
1214
- <IressFormField
1215
- label\="Name"
1216
- name\="name"
1217
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
1218
- rules\={{
1219
- required: 'Name is required',
1220
- }}
1221
- />
1222
- <IressFormField
1223
- label\="Email address"
1224
- name\="email"
1225
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
1226
- rules\={{
1227
- minLength: {
1228
- message: 'Use a longer email address',
1229
- value: 6,
1230
- },
1231
- required: 'Email is required',
1232
- }}
1233
- />
1234
- <IressButton mode\="primary" type\="submit"\>
1235
- Sign up </IressButton\>
1236
- </IressStack\>
1237
- <IressModal
1238
- show\={!!errors}
1239
- onShowChange\={(show) \=> !show && setErrors(undefined)}
1240
- \>
1241
- <IressTable
1242
- caption\="Errors"
1243
- rows\={Object.entries(errors ?? {}).map((\[name, errorDetails\]) \=> ({
1244
- name,
1245
- errorDetails: (
1246
- <IressStack gutter\="sm"\>
1247
- <ul\>
1248
- <li\>Error type: {String(errorDetails?.type)}</li\>
1249
- <li\>Error message: {String(errorDetails?.message)}</li\>
1250
- </ul\>
1251
- </IressStack\>
1252
- ),
1253
- }))}
1254
- />
1255
- </IressModal\>
1256
- </IressForm\>
1257
- );
1258
- };
1259
-
1260
- Copy
1261
-
1262
- ### [](#pre-fill-the-form)Pre-fill the form
1263
-
1264
- You can set the `defaultValues` prop to pre-fill the form values.
1265
-
1266
- \*Required Name
1267
-
1268
- \*Required Email address
1269
-
1270
- Sign up
1271
-
1272
- Hide code
1273
-
1274
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
1275
-
1276
- <IressForm
1277
- defaultValues\={{
1278
- email: 'luke.skywalker@iress.com',
1279
- name: 'Luke Skywalker'
1280
- }}
1281
- \>
1282
- <IressStack gutter\="md"\>
1283
- <IressFormField
1284
- label\="Name"
1285
- name\="name"
1286
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
1287
- rules\={{
1288
- required: 'Name is required'
1289
- }}
1290
- />
1291
- <IressFormField
1292
- label\="Email address"
1293
- name\="email"
1294
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
1295
- rules\={{
1296
- minLength: {
1297
- message: 'Use a longer email address',
1298
- value: 6
1299
- },
1300
- required: 'Email is required'
1301
- }}
1302
- />
1303
- <IressButton
1304
- mode\="primary"
1305
- type\="submit"
1306
- \>
1307
- Sign up </IressButton\>
1308
- </IressStack\>
1309
- </IressForm\>
1310
-
1311
- Copy
1312
-
1313
- ### [](#values)`values`
1314
-
1315
- 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.
1316
-
1317
- Use cases where you may need the `values` prop:
1318
-
1319
- * Syncing with a server once the values have been processed
1320
- * Syncing the value with browser storage
1321
-
1322
- **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.
1323
-
1324
- \*Required Name
1325
-
1326
- \*Required Email address
1327
-
1328
- Sign up
1329
-
1330
- * * *
1331
-
1332
- Last updateClear
1333
-
1334
- Hide code
1335
-
1336
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
1337
-
1338
- interface FieldValues {
1339
- name?: string;
1340
- email?: string;
1341
- }
1342
- export const ControlledForm \= (args: IressFormProps<FieldValues\>) \=> {
1343
- const \[values, setValues\] \= useState<FieldValues\>({
1344
- name: 'Leia Skywalker',
1345
- email: 'leia.skywalker@iress.com',
1346
- });
1347
- const \[preview, setPreview\] \= useState(false);
1348
- return (
1349
- <\>
1350
- <IressForm
1351
- {...args}
1352
- onSubmit\={(data) \=> {
1353
- setValues(data);
1354
- setPreview(true);
1355
- }}
1356
- values\={values}
1357
- mode\="onChange"
1358
- \>
1359
- <IressStack gutter\="md"\>
1360
- <IressFormField
1361
- label\="Name"
1362
- name\="name"
1363
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
1364
- rules\={{
1365
- required: 'Name is required',
1366
- }}
1367
- />
1368
- <IressFormField
1369
- label\="Email address"
1370
- name\="email"
1371
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
1372
- rules\={{
1373
- minLength: {
1374
- message: 'Use a longer email address',
1375
- value: 6,
1376
- },
1377
- required: 'Email is required',
1378
- }}
1379
- />
1380
- <IressButton mode\="primary" type\="submit"\>
1381
- Sign up </IressButton\>
1382
- </IressStack\>
1383
- </IressForm\>
1384
- <IressDivider />
1385
- <IressInline gutter\="sm"\>
1386
- <IressButton onClick\={() \=> setPreview(true)}\>Last update</IressButton\>
1387
- <IressButton
1388
- onClick\={() \=>
1389
- setValues({
1390
- name: '',
1391
- email: '',
1392
- })
1393
- }
1394
- \>
1395
- Clear </IressButton\>
1396
- </IressInline\>
1397
- <IressModal show\={!!preview} onShowChange\={(show) \=> setPreview(show)}\>
1398
- <IressTable
1399
- caption\="Last update"
1400
- rows\={Object.entries(values).map((entry) \=> ({
1401
- name: entry\[0\],
1402
- value: JSON.stringify(entry\[1\], null, 2),
1403
- }))}
1404
- />
1405
- </IressModal\>
1406
- </\>
1407
- );
1408
- };
1409
-
1410
- Copy
1411
-
1412
- ### [](#disable-validation)Disable validation
1413
-
1414
- Disabling validation is not possible with the `IressForm` component. In cases where you do need to disable validation, please consider the following:
1415
-
1416
- 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.
1417
- 2. Use a native `form` element, and customise the error handling.
1418
-
1419
- Here we have an example showcasing option one.
1420
-
1421
- \*Required Name
1422
-
1423
- \*Required Email address
1424
-
1425
- Sign up
1426
-
1427
- * * *
1428
-
1429
- Save as draft
1430
-
1431
- Hide code
1432
-
1433
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
1434
-
1435
- import {
1436
- FormRef,
1437
- IressButton,
1438
- IressDivider,
1439
- IressForm,
1440
- IressFormField,
1441
- IressFormProps,
1442
- IressInput,
1443
- IressStack,
1444
- IressToasterProvider,
1445
- useToaster,
1446
- } from '@iress-oss/ids-components';
1447
- import { useRef } from 'react';
1448
- interface FieldValues {
1449
- name?: string;
1450
- email?: string;
1451
- }
1452
- const Form \= () \=> {
1453
- const { success, error } \= useToaster();
1454
- const formRef \= useRef<FormRef<FieldValues\>>(null);
1455
- return (
1456
- <\>
1457
- <IressForm
1458
-
1459
- onSubmit\={() \=>
1460
- success({
1461
- heading: 'Passed validation',
1462
- children: JSON.stringify(formRef.current?.api.getValues(), null, 2),
1463
- })
1464
- }
1465
- onError\={() \=>
1466
- error({
1467
- heading: 'Failed validation',
1468
- children: JSON.stringify(formRef.current?.api.getValues(), null, 2),
1469
- })
1470
- }
1471
- ref\={formRef}
1472
- \>
1473
- <IressStack gutter\="md"\>
1474
- <IressFormField
1475
- label\="Name"
1476
- name\="name"
1477
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
1478
- rules\={{
1479
- required: 'Name is required',
1480
- }}
1481
- />
1482
- <IressFormField
1483
- label\="Email address"
1484
- name\="email"
1485
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
1486
- rules\={{
1487
- minLength: {
1488
- message: 'Use a longer email address',
1489
- value: 6,
1490
- },
1491
- required: 'Email is required',
1492
- }}
1493
- />
1494
- <IressButton mode\="primary" type\="submit"\>
1495
- Sign up </IressButton\>
1496
- </IressStack\>
1497
- </IressForm\>
1498
- <IressDivider />
1499
- <IressButton
1500
- onClick\={() \=> {
1501
- success({
1502
- heading: 'Saved as draft (no validation)',
1503
- children: JSON.stringify(formRef.current?.api.getValues(), null, 2),
1504
- });
1505
- }}
1506
- \>
1507
- Save as draft </IressButton\>
1508
- </\>
1509
- );
1510
- };
1511
- export const DisableValidationForm \= () \=> (
1512
- <IressToasterProvider\>
1513
- <Form />
1514
- </IressToasterProvider\>
1515
- );
1516
-
1517
- Copy
1518
-
1519
- ### [](#resetting-the-form)Resetting the form
1520
-
1521
- 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.
1522
-
1523
- **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.
1524
-
1525
- \*Required Name
1526
-
1527
- \*Required Email address
1528
-
1529
- Sign up
1530
-
1531
- * * *
1532
-
1533
- Reset
1534
-
1535
- Hide code
1536
-
1537
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
1538
-
1539
- import {
1540
- IressFormProps,
1541
- IressForm,
1542
- FormRef,
1543
- IressDivider,
1544
- IressButton,
1545
- IressStack,
1546
- IressFormField,
1547
- IressInput,
1548
- } from '@iress-oss/ids-components';
1549
- import { useRef } from 'react';
1550
- interface FieldValues {
1551
- name?: string;
1552
- email?: string;
1553
- }
1554
- export const FormReset \= () \=> {
1555
- const ref \= useRef<FormRef<FieldValues\>>(null);
1556
- return (
1557
- <IressForm {...{
1558
- defaultValues: {
1559
- name: '',
1560
- email: '',
1561
- },
1562
- }} ref\={ref}\>
1563
- <IressStack gutter\="md"\>
1564
- <IressFormField
1565
- label\="Name"
1566
- name\="name"
1567
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
1568
- rules\={{
1569
- required: 'Name is required',
1570
- }}
1571
- />
1572
- <IressFormField
1573
- label\="Email address"
1574
- name\="email"
1575
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
1576
- rules\={{
1577
- minLength: {
1578
- message: 'Use a longer email address',
1579
- value: 6,
1580
- },
1581
- required: 'Email is required',
1582
- }}
1583
- />
1584
- <IressButton mode\="primary" type\="submit"\>
1585
- Sign up </IressButton\>
1586
- </IressStack\>
1587
- <IressDivider />
1588
- <IressButton type\="reset" onClick\={() \=> ref.current?.reset()}\>
1589
- Reset </IressButton\>
1590
- </IressForm\>
1591
- );
1592
- };
1593
-
1594
- Copy
1595
-
1596
- ### [](#custom-form-field-components)Custom form field components
1597
-
1598
- You can integrate custom components within `IressFormField` to create enhanced form experiences.
1599
-
1600
- This demo showcases how to embed a custom `TranscriptTextBox` component into `IressFormField` while leveraging its built-in validation rules, error handling, and state management without additional implementation.
1601
-
1602
- **Reminder:** When building custom form components, avoid managing error message state internally. This helps maintain the IressForm as the single source of truth and ensures consistent, predictable UI behavior.
1603
-
1604
- Key features demonstrated:
1605
-
1606
- * **Universal Integration Pattern**: Shows how any custom component can be embedded in IressFormField
1607
- * **Built-in Validation**: Leverages IressFormField's validation rules with custom validation logic
1608
- * **Multiple Error Messages**: Displays simultaneous validation errors (e.g., wrong file type AND too large)
1609
- * **Drag & Drop**: Files can be dragged and dropped directly onto the textarea
1610
- * **File Upload Button**: Traditional file selection via button click
1611
- * **Visual Feedback**: UI changes during drag operations with border and background updates
1612
- * **Form State Management**: Automatically integrates with form context using controlled props
1613
- * **File Management**: Display uploaded files with remove functionality using `IressPanel`
1614
-
1615
- Custom FormField Components
1616
- ===========================
1617
-
1618
- This demo showcases how to embed any custom component (TranscriptTextBox) into IressFormField while leveraging its form validation, error handling, and state management without additional implementation. When building custom form components, avoid managing error message state internally. This helps maintain the IressForm as the single source of truth and ensures consistent, predictable UI behavior.
1619
-
1620
- \*Required Transcript
1621
-
1622
- Upload or copy and paste transcript here
1623
-
1624
- Upload
1625
-
1626
- Submit
1627
-
1628
- Hide code
1629
-
1630
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
1631
-
1632
- import React, { useState } from 'react';
1633
-
1634
- interface TranscriptFormValues {
1635
- transcript: TranscriptData | string;
1636
- }
1637
- interface TranscriptData {
1638
- content: string;
1639
- size?: number;
1640
- type: 'file' | 'text';
1641
- extension?: string;
1642
- fileName?: string;
1643
- rejectedReasons?: REJECTION\_REASONS\[\];
1644
- }
1645
- interface TranscriptTextBoxProps {
1646
- value: TranscriptData | string;
1647
- onChange: (data: TranscriptData) \=> void;
1648
- placeholder?: string;
1649
- rows?: number;
1650
- style?: React.CSSProperties;
1651
- allowedExtensions?: string\[\];
1652
- maxSizeInMB?: number;
1653
- }
1654
- interface SubmittedValuesDisplayProps {
1655
- submittedValues: TranscriptFormValues | null;
1656
- title?: string;
1657
- }
1658
- enum REJECTION\_REASONS {
1659
- TYPE \= 'type',
1660
- SIZE \= 'size',
1661
- }
1662
- const validateFile \=
1663
- (allowedExtensions: string\[\], maxSizeInMB: number) \=>
1664
- (data: TranscriptData | string) \=> {
1665
- if (
1666
- !!data &&
1667
- typeof data \=== 'object' &&
1668
- data.type \=== 'file' &&
1669
- Array.isArray(data.rejectedReasons) &&
1670
- data.rejectedReasons.length \> 0
1671
- ) {
1672
- const errors: string\[\] \= \[\];
1673
- if (data.rejectedReasons.includes(REJECTION\_REASONS.TYPE)) {
1674
- errors.push(\`Only .${allowedExtensions.join(', ')} accepted\`);
1675
- }
1676
- if (data.rejectedReasons.includes(REJECTION\_REASONS.SIZE)) {
1677
- errors.push(\`File size must be less than ${maxSizeInMB}MB\`);
1678
- }
1679
- if (errors.length \> 0) {
1680
- return errors.join('. ');
1681
- }
1682
- }
1683
- return true;
1684
- };
1685
- const TranscriptTextBox \= ({
1686
- value,
1687
- onChange,
1688
- placeholder \= 'Copy and paste transcripts OR drag and drop / upload recordings, transcripts or documents here (.txt format).',
1689
- rows \= 10,
1690
- style,
1691
- allowedExtensions \= \['txt'\],
1692
- maxSizeInMB \= 10,
1693
- }: TranscriptTextBoxProps) \=> {
1694
- // Extract content and file info from value
1695
- const currentData \=
1696
- typeof value \=== 'string'
1697
- ? { content: value, type: 'text' as const }
1698
- : value;
1699
- const currentFile \=
1700
- currentData?.type \=== 'file' &&
1701
- (!currentData.rejectedReasons || currentData.rejectedReasons.length \=== 0)
1702
- ? {
1703
- name: currentData.fileName ?? 'Unknown file',
1704
- size: currentData.size,
1705
- }
1706
- : null;
1707
- const createTranscriptData \= (
1708
- content: string,
1709
- type: 'file' | 'text',
1710
- additionalData?: Partial<TranscriptData\>,
1711
- ): TranscriptData \=> ({
1712
- content,
1713
- type,
1714
- ...additionalData,
1715
- });
1716
- const handleFileRead \= (file: File) \=> {
1717
- const reader \= new FileReader();
1718
- reader.onload \= (e) \=> {
1719
- const content \= e.target?.result as string;
1720
- onChange(
1721
- createTranscriptData(content, 'file', {
1722
- size: file.size,
1723
- extension: file.name.split('.').pop()?.toLowerCase(),
1724
- fileName: file.name,
1725
- }),
1726
- );
1727
- };
1728
- reader.onerror \= () \=> {
1729
- // Let parent handle errors through validation
1730
- onChange(
1731
- createTranscriptData('', 'file', {
1732
- fileName: file.name,
1733
- }),
1734
- );
1735
- };
1736
- reader.readAsText(file);
1737
- };
1738
- const handleTextChange \= (
1739
- e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement\>,
1740
- textContent: string,
1741
- ) \=> {
1742
- onChange(createTranscriptData(textContent, 'text'));
1743
- };
1744
- const onFileSelected \= (files: File\[\]) \=> {
1745
- if (files.length \=== 0) return;
1746
- const file \= files\[0\];
1747
- handleFileRead(file);
1748
- };
1749
- const { getRootProps, getInputProps, open, isDragActive } \= useDropzone({
1750
- multiple: false,
1751
- noClick: true,
1752
- maxSize: maxSizeInMB \* 1024 \* 1024,
1753
- accept: allowedExtensions.reduce(
1754
- (acc, ext) \=> {
1755
- const mimeType \=
1756
- ext \=== 'txt' ? 'text/plain' : 'application/octet-stream';
1757
- acc\[mimeType\] \= acc\[mimeType\] || \[\];
1758
- acc\[mimeType\].push(\`.${ext}\`);
1759
- return acc;
1760
- },
1761
- {} as Record<string, string\[\]\>,
1762
- ),
1763
- onDrop: (acceptedFiles, rejectedFiles) \=> {
1764
- if (acceptedFiles.length \> 0) {
1765
- onFileSelected(acceptedFiles);
1766
- return;
1767
- }
1768
- if (rejectedFiles.length \> 0) {
1769
- const rejectedFile \= rejectedFiles\[0\];
1770
- const { file, errors } \= rejectedFile;
1771
- // Map error codes to rejection reasons
1772
- const errorCodeMap \= {
1773
- 'file-invalid-type': REJECTION\_REASONS.TYPE,
1774
- 'file-too-large': REJECTION\_REASONS.SIZE,
1775
- } as const;
1776
- const rejectedReasons \= errors .map((error) \=> errorCodeMap\[error.code as keyof typeof errorCodeMap\])
1777
- .filter((reason): reason is REJECTION\_REASONS \=> Boolean(reason));
1778
- onChange(
1779
- createTranscriptData('', 'file', {
1780
- fileName: file.name,
1781
- rejectedReasons,
1782
- }),
1783
- );
1784
- }
1785
- },
1786
- });
1787
- const handleUploadClick \= () \=> {
1788
- open();
1789
- };
1790
- const removeFile \= () \=> {
1791
- onChange(createTranscriptData('', 'text'));
1792
- };
1793
- return (
1794
- <IressStack gutter\="sm"\>
1795
- <div {...getRootProps()} style\={{ position: 'relative' }}\>
1796
- <input {...getInputProps()} />
1797
- <IressInput
1798
- value\={currentData?.content || ''}
1799
- onChange\={handleTextChange}
1800
- rows\={rows}
1801
- placeholder\={isDragActive ? 'Drop your file here...' : placeholder}
1802
- style\={{
1803
- boxSizing: 'border-box',
1804
- border: isDragActive ? '1px dashed #007acc' : undefined,
1805
- backgroundColor: isDragActive ? '#f0f8ff' : undefined,
1806
- ...style,
1807
- }}
1808
- />
1809
- </div\>
1810
- {currentFile && (
1811
- <IressPanel\>
1812
- <IressInline horizontalAlign\="between" verticalAlign\="middle"\>
1813
- <IressText\>📄 {currentFile.name}</IressText\>
1814
- <IressButton mode\="secondary" onClick\={removeFile}\>
1815
- Remove </IressButton\>
1816
- </IressInline\>
1817
- </IressPanel\>
1818
- )}
1819
- <IressButton
1820
- mode\="secondary"
1821
- onClick\={handleUploadClick}
1822
- prepend\={<IressIcon name\="upload" />}
1823
- \>
1824
- Upload </IressButton\>
1825
- </IressStack\>
1826
- );
1827
- };
1828
- const SubmittedValuesDisplay: React.FC<SubmittedValuesDisplayProps\> \= ({
1829
- submittedValues,
1830
- title \= 'Submitted Values:',
1831
- }) \=> {
1832
- if (!submittedValues) {
1833
- return null;
1834
- }
1835
- return (
1836
- <IressPanel\>
1837
- <IressStack gutter\="sm"\>
1838
- <IressText variant\="bold"\>{title}</IressText\>
1839
- <IressText\>
1840
- <strong\>Type:</strong\>
1841
- {typeof submittedValues.transcript \=== 'string'
1842
- ? 'text'
1843
- : submittedValues.transcript.type}
1844
- </IressText\>
1845
- <IressText\>
1846
- <strong\>Content:</strong\>
1847
- {typeof submittedValues.transcript \=== 'string'
1848
- ? submittedValues.transcript
1849
- : submittedValues.transcript.content}
1850
- </IressText\>
1851
- {typeof submittedValues.transcript \=== 'object' &&
1852
- submittedValues.transcript.fileName && (
1853
- <IressText\>
1854
- <strong\>File Name:</strong\> {submittedValues.transcript.fileName}
1855
- </IressText\>
1856
- )}
1857
- {typeof submittedValues.transcript \=== 'object' &&
1858
- submittedValues.transcript.size && (
1859
- <IressText\>
1860
- <strong\>File Size:</strong\>
1861
- {(submittedValues.transcript.size / 1024).toFixed(2)} KB </IressText\>
1862
- )}
1863
- </IressStack\>
1864
- </IressPanel\>
1865
- );
1866
- };
1867
- const Heading \= () \=> {
1868
- return (
1869
- <\>
1870
- <IressText element\="h1"\>Custom FormField Components</IressText\>
1871
- <IressText element\="p"\>
1872
- This demo showcases how to embed any custom component (TranscriptTextBox) into IressFormField while leveraging its form validation, error handling, and state management without additional implementation. When building custom form components, avoid managing error message state internally. This helps maintain the IressForm as the single source of truth and ensures consistent, predictable UI behavior. </IressText\>
1873
- </\>
1874
- );
1875
- };
1876
- export const CustomFormFieldComponents \= (
1877
- args: IressFormProps<TranscriptFormValues\>,
1878
- ) \=> {
1879
- const \[submittedValues, setSubmittedValues\] \=
1880
- useState<TranscriptFormValues | null\>(null);
1881
- const allowedExtensions \= \['txt'\];
1882
- const maxSizeInMB \= 0.1;
1883
- const handleSubmit \= (data: TranscriptFormValues) \=> {
1884
- setSubmittedValues(data);
1885
- console.log('Form submitted:', data);
1886
- };
1887
- return (
1888
- <\>
1889
- <Heading />
1890
- <IressForm<TranscriptFormValues> {...args}
1891
- mode="onChange" hiddenErrorSummary onSubmit={handleSubmit}
1892
- defaultValues={{ transcript: { content: '', type: 'text' } }}
1893
- > <IressStack gutter\="md"\>
1894
- <IressFormField
1895
- label\="Transcript"
1896
- name\="transcript"
1897
- hint\="Upload or copy and paste transcript here"
1898
- render\={(controlledProps) \=> (
1899
- <TranscriptTextBox
1900
- {...controlledProps}
1901
- allowedExtensions\={allowedExtensions}
1902
- maxSizeInMB\={maxSizeInMB}
1903
- />
1904
- )}
1905
- rules\={{
1906
- required: 'Transcript is required',
1907
- validate: {
1908
- file: validateFile(allowedExtensions, maxSizeInMB),
1909
- },
1910
- }}
1911
- />
1912
- <IressButton type\="submit" mode\="primary"\>
1913
- Submit </IressButton\>
1914
- <SubmittedValuesDisplay submittedValues\={submittedValues} />
1915
- </IressStack\>
1916
- </IressForm\>
1917
- </\>
1918
- );
1919
- };
1920
-
1921
- Copy
1922
-
1923
- ### [](#forms-in-expanders-lazy-loading)Forms in expanders (lazy loading)
1924
-
1925
- If you are using forms in expanders, or in other scenarios when the loading of the form may be delayed, it is recommended to only load the form when it is required. This will improve performance of your application, and make it more predictable. It may also fix act warnings in tests if you are seeing some appearing due to conditionally loaded `IressForm` elements.
1926
-
1927
- Sender
1928
-
1929
- Recipient
1930
-
1931
- Hide code
1932
-
1933
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
1934
-
1935
- import {
1936
- IressExpander,
1937
- IressForm,
1938
- IressFormField,
1939
- IressFormProps,
1940
- IressInput,
1941
- IressStack,
1942
- } from '@iress-oss/ids-components';
1943
- import { useState } from 'react';
1944
- interface FieldValues {
1945
- name?: string;
1946
- email?: string;
1947
- }
1948
- const Form \= () \=> (
1949
- <IressForm \>
1950
- <IressStack gutter\="md"\>
1951
- <IressFormField
1952
- label\="Name"
1953
- name\="name"
1954
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
1955
- rules\={{
1956
- required: 'Name is required',
1957
- }}
1958
- />
1959
- <IressFormField
1960
- label\="Email address"
1961
- name\="email"
1962
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
1963
- rules\={{
1964
- minLength: {
1965
- message: 'Use a longer email address',
1966
- value: 6,
1967
- },
1968
- required: 'Email is required',
1969
- }}
1970
- />
1971
- </IressStack\>
1972
- </IressForm\>
1973
- );
1974
- export const FormExpanders \= () \=> {
1975
- const \[expander, setExpander\] \= useState('');
1976
- const isOpen \= (name: string) \=> expander \=== name;
1977
- return (
1978
- <IressStack gutter\="sm"\>
1979
- <IressExpander
1980
- activator\="Sender"
1981
- open\={isOpen('Sender')}
1982
- onChange\={(open) \=> open && setExpander('Sender')}
1983
- \>
1984
- {isOpen('Sender') && <Form />}
1985
- </IressExpander\>
1986
- <IressExpander
1987
- activator\="Recipient"
1988
- open\={isOpen('Recipient')}
1989
- onChange\={(open) \=> open && setExpander('Recipient')}
1990
- \>
1991
- {isOpen('Recipient') && <Form />}
1992
- </IressExpander\>
1993
- </IressStack\>
1994
- );
1995
- };
1996
-
1997
- Copy
1998
-
1999
- ### [](#conditional-fields-usewatch)Conditional fields (useWatch)
2000
-
2001
- When you have fields that are conditionally shown, you can use the `IressForm.useWatch` hook to watch the value of another field and conditionally render the field.
2002
-
2003
- **Notes:**
2004
-
2005
- * You can use the `api.watch` method on the `IressForm`'s ref to watch the value of a field, but it is recommended to use the hook for better performance by isolating re-rendering at the component level.
2006
-
2007
- This is a conditional field example using "useWatch".
2008
-
2009
- \*Required Select fields to show
2010
-
2011
- Name
2012
-
2013
- Email
2014
-
2015
- * * *
2016
-
2017
- Submit
2018
-
2019
- Hide code
2020
-
2021
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
2022
-
2023
- import {
2024
- IressButton,
2025
- IressCheckbox,
2026
- IressCheckboxGroup,
2027
- IressDivider,
2028
- IressForm,
2029
- IressFormField,
2030
- IressFormProps,
2031
- IressInput,
2032
- IressStack,
2033
- IressText,
2034
- } from '@iress-oss/ids-components';
2035
- interface FieldValues {
2036
- show?: string\[\];
2037
- name?: string;
2038
- email?: string;
2039
- }
2040
- /\*\*
2041
- \* Conditional fields need to be rendered in a sub-component, to allow it to use the \`useWatch\` \* hook to watch the value of the field dictating the display of conditional fields. \*/
2042
- const FormSectionWithConditionalFields \= () \=> {
2043
- const show \= IressForm.useWatch<FieldValues\>({ name: 'show' });
2044
- return (
2045
- <IressStack gutter\="md"\>
2046
- <IressText\>
2047
- This is a conditional field example using "useWatch". </IressText\>
2048
- <IressFormField
2049
- name\="show"
2050
- label\="Select fields to show"
2051
- rules\={{
2052
- required: 'Please select at least one field to show',
2053
- }}
2054
- render\={(controlledProps) \=> (
2055
- <IressCheckboxGroup {...controlledProps} layout\="inline"\>
2056
- <IressCheckbox value\="name"\>Name</IressCheckbox\>
2057
- <IressCheckbox value\="email"\>Email</IressCheckbox\>
2058
- </IressCheckboxGroup\>
2059
- )}
2060
- />
2061
- {show?.includes('name') && (
2062
- <IressFormField
2063
- name\="name"
2064
- label\="Name"
2065
- rules\={{
2066
- required: 'Name is required',
2067
- }}
2068
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
2069
- />
2070
- )}
2071
- {show?.includes('email') && (
2072
- <IressFormField
2073
- name\="email"
2074
- label\="Email"
2075
- rules\={{
2076
- required: 'Email is required',
2077
- pattern: {
2078
- value:
2079
- /^\[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\])?)\*$/,
2080
- message: 'Please enter a valid email address',
2081
- },
2082
- }}
2083
- render\={(controlledProps) \=> (
2084
- <IressInput {...controlledProps} type\="email" />
2085
- )}
2086
- /\>
2087
- )}
2088
- </IressStack\>
2089
- );
2090
- };
2091
- export const UseWatchForm \= () \=> (
2092
- <IressForm \>
2093
- <FormSectionWithConditionalFields />
2094
- <IressDivider />
2095
- <IressButton type\="submit" mode\="primary"\>
2096
- Submit </IressButton\>
2097
- </IressForm\>
2098
- );
2099
-
2100
- Copy
2101
-
2102
- [](#iresshookform)`IressHookForm`
2103
- ---------------------------------
2104
-
2105
- `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.
2106
-
2107
- 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.
2108
-
2109
- Some use cases:
2110
-
2111
- 1. You may need to use the `useForm` hook in a parent component to share the form state with multiple child components.
2112
- 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.
2113
-
2114
- \*Required First Name
2115
-
2116
- \*Required Last Name
2117
-
2118
- Submit
2119
-
2120
- Hide code
2121
-
2122
- \[data-radix-scroll-area-viewport\] { scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; } \[data-radix-scroll-area-viewport\]::-webkit-scrollbar { display: none; } :where(\[data-radix-scroll-area-viewport\]) { display: flex; flex-direction: column; align-items: stretch; } :where(\[data-radix-scroll-area-content\]) { flex-grow: 1; }
2123
-
2124
- interface FieldValues {
2125
- firstName: string;
2126
- lastName: string;
2127
- }
2128
- export const HookFormExample \= () \=> {
2129
- const form \= useForm<FieldValues\>();
2130
- const firstName \= form.watch('firstName');
2131
- const lastName \= form.watch('lastName');
2132
- return (
2133
- <IressHookForm form\={form}\>
2134
- <IressStack gutter\="md"\>
2135
- {firstName && lastName && (
2136
- <IressPanel\>
2137
- Name: {firstName} {lastName}
2138
- </IressPanel\>
2139
- )}
2140
- <IressFormField
2141
- name\="firstName"
2142
- label\="First Name"
2143
- render\={(controlledProps) \=> <IressInput {...controlledProps} />}
2144
- rules\={{ required: true }}
2145
- />
2146
- <IressFormField
2147
- name\="lastName"
2148
- label\="Last Name"
2149
- render\={(controlledProps) \=> (
2150
- <IressInput {...controlledProps} type\="email" />
2151
- )}
2152
- rules\={{ required: true }}
2153
- />
2154
- <IressButton type\="submit" mode\="primary"\>
2155
- Submit </IressButton\>
2156
- </IressStack\>
2157
- </IressHookForm\>
2158
- );
2159
- };
2160
-
2161
- Copy
2162
-
2163
- [](#migration-to-version-5)Migration to version 5
2164
- -------------------------------------------------
2165
-
2166
- 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.
2167
-
2168
- 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.
2169
-
2170
- In version 5 we have decided to provide two alternative methods of using form components to better accommodate our consumer’s needs.
2171
-
2172
- 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.
2173
-
2174
- 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.
2175
-
2176
- [](#testing)Testing
2177
- -------------------
2178
-
2179
- 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)\]
2180
-
2181
- Here is an example of testing a form submission.
2182
-
2183
- \[data-radix-scroll-area-viewport\] {
2184
- scrollbar-width: none;
2185
- -ms-overflow-style: none;
2186
- -webkit-overflow-scrolling: touch;
2187
- }
2188
- \[data-radix-scroll-area-viewport\]::-webkit-scrollbar {
2189
- display: none;
2190
- }
2191
- :where(\[data-radix-scroll-area-viewport\]) {
2192
- display: flex;
2193
- flex-direction: column;
2194
- align-items: stretch;
2195
- }
2196
- :where(\[data-radix-scroll-area-content\]) {
2197
- flex-grow: 1;
2198
- }
2199
-
2200
- render(
2201
- <IressForm\>
2202
- <IressFormField
2203
- label\="Email"
2204
- name\="email"
2205
- rules\={{ required: true }}
2206
- render\={(controlledProps) \=> (
2207
- <IressInput {...controlledProps} type\="email" />
2208
- )}
2209
- />
2210
- <IressButton type\="submit"\>Submit</IressButton\>
2211
- </IressForm\>,
2212
- );
2213
- // May be needed sometimes to get over the act warning
2214
- await screen.getByRole('form');
2215
- const emailInput \= screen.getByRole('textbox');
2216
- const submitButton \= screen.getByRole('button', { name: 'Submit' });
2217
- await userEvent.click(submitButton);
2218
- // Errors are asynchronous, so we need to wait for them to appear
2219
- const summaryError \= await screen.findByText(
2220
- 'There was a problem submitting this form',
2221
- );
2222
- expect(summaryError).toBeInTheDocument();
2223
-
2224
- Copy
2225
-
2226
- [](#caveat)Caveat
2227
- -----------------
2228
-
2229
- ### [](#using-iressselect-with-non-string-values-in-iressformfield)Using `IressSelect` with non-string values in `IressFormField`
2230
-
2231
- 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.
2232
-
2233
- **Problem:**
2234
-
2235
- \[data-radix-scroll-area-viewport\] {
2236
- scrollbar-width: none;
2237
- -ms-overflow-style: none;
2238
- -webkit-overflow-scrolling: touch;
2239
- }
2240
- \[data-radix-scroll-area-viewport\]::-webkit-scrollbar {
2241
- display: none;
2242
- }
2243
- :where(\[data-radix-scroll-area-viewport\]) {
2244
- display: flex;
2245
- flex-direction: column;
2246
- align-items: stretch;
2247
- }
2248
- :where(\[data-radix-scroll-area-content\]) {
2249
- flex-grow: 1;
2250
- }
2251
-
2252
- <IressFormField
2253
- name\="dependents"
2254
- label\="Dependents"
2255
- render\={(controlledProps) \=> (
2256
- <IressSelect {...controlledProps}\>
2257
- <option key\="0" value\={0}\>
2258
- 0 </option\>
2259
- <option key\="1" value\={1}\>
2260
- 1 </option\>
2261
- <option key\="2" value\={2}\>
2262
- 2 </option\>
2263
- </IressSelect\>
2264
- )}
2265
- />
2266
-
2267
- Copy
2268
-
2269
- In this case, React Hook Form will try to convert the value to string, which might cause type issues.
2270
-
2271
- **Solution:** Override the `onChange` handler to pass the actual value as a second parameter:
2272
-
2273
- \[data-radix-scroll-area-viewport\] {
2274
- scrollbar-width: none;
2275
- -ms-overflow-style: none;
2276
- -webkit-overflow-scrolling: touch;
2277
- }
2278
- \[data-radix-scroll-area-viewport\]::-webkit-scrollbar {
2279
- display: none;
2280
- }
2281
- :where(\[data-radix-scroll-area-viewport\]) {
2282
- display: flex;
2283
- flex-direction: column;
2284
- align-items: stretch;
2285
- }
2286
- :where(\[data-radix-scroll-area-content\]) {
2287
- flex-grow: 1;
2288
- }
2289
-
2290
- <IressFormField
2291
- name\="dependents"
2292
- label\="Dependents"
2293
- render\={(controlledProps) \=> (
2294
- <IressSelect
2295
- {...controlledProps}
2296
- onChange\={(\_e, value) \=> controlledProps.onChange(value)}
2297
- \>
2298
- <option key\="0" value\={0}\>
2299
- 0 </option\>
2300
- <option key\="1" value\={1}\>
2301
- 1 </option\>
2302
- <option key\="2" value\={2}\>
2303
- 2 </option\>
2304
- </IressSelect\>
2305
- )}
2306
- />
2307
-
2308
- Copy
2309
-
2310
- 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).
2311
-
2312
- ### [](#properly-resetting-fields)Properly resetting fields
2313
-
2314
- 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.
2315
-
2316
- **Problem:**
2317
-
2318
- \[data-radix-scroll-area-viewport\] {
2319
- scrollbar-width: none;
2320
- -ms-overflow-style: none;
2321
- -webkit-overflow-scrolling: touch;
2322
- }
2323
- \[data-radix-scroll-area-viewport\]::-webkit-scrollbar {
2324
- display: none;
2325
- }
2326
- :where(\[data-radix-scroll-area-viewport\]) {
2327
- display: flex;
2328
- flex-direction: column;
2329
- align-items: stretch;
2330
- }
2331
- :where(\[data-radix-scroll-area-content\]) {
2332
- flex-grow: 1;
2333
- }
2334
-
2335
- const { resetField } \= IressForm.useFormContext();
2336
- resetField('rich-select', {
2337
- label: '',
2338
- value: '',
2339
- });
2340
-
2341
- Copy
2342
-
2343
- 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.
2344
-
2345
- **Solution:** Override the `onChange` handler to pass the actual value as a second parameter:
2346
-
2347
- \[data-radix-scroll-area-viewport\] {
2348
- scrollbar-width: none;
2349
- -ms-overflow-style: none;
2350
- -webkit-overflow-scrolling: touch;
2351
- }
2352
- \[data-radix-scroll-area-viewport\]::-webkit-scrollbar {
2353
- display: none;
2354
- }
2355
- :where(\[data-radix-scroll-area-viewport\]) {
2356
- display: flex;
2357
- flex-direction: column;
2358
- align-items: stretch;
2359
- }
2360
- :where(\[data-radix-scroll-area-content\]) {
2361
- flex-grow: 1;
2362
- }
2363
-
2364
- const { resetField } \= IressForm.useFormContext();
2365
- resetField('rich-select', null); // or undefined
2366
-
2367
- Copy
2368
-
2369
- This will properly reset the field to null and clear the field value.
2370
-
2371
- ### [](#conflicting-versions-of-react-hook-forms)Conflicting versions of React Hook Forms
2372
-
2373
- If you are using React Hook Forms in your application, please ensure it is the same version IDS uses, otherwise there will be conflicts.
2374
-
2375
- The version we are using in IDS is:
2376
-
2377
- yarn add react-hook-form@7.53.1
2378
-
2379
- On this page
2380
-
2381
- * [Overview](#overview)
2382
- * [Props](#props)
2383
- * [Key concepts](#key-concepts)
2384
- * [State management](#state-management)
2385
- * [Validation](#validation)
2386
- * [Syncing state](#syncing-state)
2387
- * [Usage](#usage)
2388
- * [Fields](#fields)
2389
- * [Rules](#rules)
2390
- * [Handling submission](#handling-submission)
2391
- * [Read only](#read-only)
2392
- * [Behaviour](#behaviour)
2393
- * [Examples](#examples)
2394
- * [Validation summary](#validation-summary)
2395
- * [Hidden validation summary](#hidden-validation-summary)
2396
- * [Custom error handling](#custom-error-handling)
2397
- * [Pre-fill the form](#pre-fill-the-form)
2398
- * [values](#values)
2399
- * [Disable validation](#disable-validation)
2400
- * [Resetting the form](#resetting-the-form)
2401
- * [Custom form field components](#custom-form-field-components)
2402
- * [Forms in expanders (lazy loading)](#forms-in-expanders-lazy-loading)
2403
- * [Conditional fields (useWatch)](#conditional-fields-usewatch)
2404
- * [IressHookForm](#iresshookform)
2405
- * [Migration to version 5](#migration-to-version-5)
2406
- * [Testing](#testing)
2407
- * [Caveat](#caveat)
2408
- * [Using IressSelect with non-string values in IressFormField](#using-iressselect-with-non-string-values-in-iressformfield)
2409
- * [Properly resetting fields](#properly-resetting-fields)
2410
- * [Conflicting versions of React Hook Forms](#conflicting-versions-of-react-hook-forms)