@iress-oss/ids-mcp-server 5.15.0 → 6.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/generated/docs/components-alert-docs.md +579 -7
- package/generated/docs/components-autocomplete-docs.md +694 -15
- package/generated/docs/components-autocomplete-recipes-docs.md +1 -1
- package/generated/docs/components-badge-docs.md +442 -59
- package/generated/docs/components-button-docs.md +1150 -70
- package/generated/docs/components-buttongroup-docs.md +441 -3
- package/generated/docs/components-card-docs.md +487 -37
- package/generated/docs/components-checkbox-docs.md +506 -5
- package/generated/docs/components-checkboxgroup-docs.md +586 -191
- package/generated/docs/components-checkboxgroup-recipes-docs.md +3 -3
- package/generated/docs/components-col-docs.md +451 -36
- package/generated/docs/components-container-docs.md +32 -0
- package/generated/docs/components-divider-docs.md +427 -27
- package/generated/docs/components-expander-docs.md +487 -108
- package/generated/docs/components-field-docs.md +1400 -68
- package/generated/docs/components-filter-docs.md +268 -55
- package/generated/docs/components-hide-docs.md +447 -10
- package/generated/docs/components-icon-docs.md +522 -259
- package/generated/docs/components-image-docs.md +493 -0
- package/generated/docs/components-inline-docs.md +1179 -44
- package/generated/docs/components-input-docs.md +544 -12
- package/generated/docs/components-input-recipes-docs.md +4 -4
- package/generated/docs/components-inputcurrency-docs.md +532 -0
- package/generated/docs/components-inputcurrency-recipes-docs.md +4 -5
- package/generated/docs/components-introduction-docs.md +450 -0
- package/generated/docs/components-label-docs.md +454 -27
- package/generated/docs/components-link-docs.md +586 -0
- package/generated/docs/components-menu-docs.md +531 -89
- package/generated/docs/components-menu-menuitem-docs.md +556 -10
- package/generated/docs/components-modal-docs.md +814 -55
- package/generated/docs/components-panel-docs.md +418 -198
- package/generated/docs/components-placeholder-docs.md +420 -1
- package/generated/docs/components-popover-docs.md +1097 -32
- package/generated/docs/components-popover-recipes-docs.md +39 -73
- package/generated/docs/components-progress-docs.md +464 -0
- package/generated/docs/components-provider-docs.md +57 -2
- package/generated/docs/components-radio-docs.md +460 -4
- package/generated/docs/components-radiogroup-docs.md +586 -116
- package/generated/docs/components-readonly-docs.md +450 -4
- package/generated/docs/components-richselect-docs.md +4660 -1257
- package/generated/docs/components-row-docs.md +2065 -588
- package/generated/docs/components-select-docs.md +489 -5
- package/generated/docs/components-skeleton-docs.md +399 -16
- package/generated/docs/components-skeleton-recipes-docs.md +7 -7
- package/generated/docs/components-skiplink-docs.md +548 -27
- package/generated/docs/components-slideout-docs.md +648 -150
- package/generated/docs/components-slider-docs.md +515 -33
- package/generated/docs/components-spinner-docs.md +393 -2
- package/generated/docs/components-stack-docs.md +732 -74
- package/generated/docs/components-table-ag-grid-docs.md +497 -127
- package/generated/docs/components-table-docs.md +1049 -27
- package/generated/docs/components-tabset-docs.md +454 -27
- package/generated/docs/components-tabset-tab-docs.md +464 -0
- package/generated/docs/components-tag-docs.md +452 -19
- package/generated/docs/components-text-docs.md +322 -131
- package/generated/docs/components-toaster-docs.md +463 -53
- package/generated/docs/components-toggle-docs.md +476 -20
- package/generated/docs/components-tooltip-docs.md +443 -7
- package/generated/docs/components-validationmessage-docs.md +933 -13
- package/generated/docs/extensions-editor-docs.md +906 -13
- package/generated/docs/extensions-editor-recipes-docs.md +51 -1
- package/generated/docs/foundations-accessibility-docs.md +1 -23
- package/generated/docs/foundations-grid-docs.md +74 -0
- package/generated/docs/foundations-introduction-docs.md +6 -4
- package/generated/docs/foundations-responsive-breakpoints-docs.md +193 -0
- package/generated/docs/foundations-tokens-colour-docs.md +564 -0
- package/generated/docs/foundations-tokens-elevation-docs.md +155 -0
- package/generated/docs/foundations-tokens-introduction-docs.md +190 -0
- package/generated/docs/foundations-tokens-radius-docs.md +71 -0
- package/generated/docs/foundations-tokens-spacing-docs.md +89 -0
- package/generated/docs/foundations-tokens-typography-docs.md +322 -0
- package/generated/docs/foundations-z-index-stacking-docs.md +31 -0
- package/generated/docs/guidelines.md +1537 -295
- package/generated/docs/introduction-docs.md +65 -21
- package/generated/docs/news-version-6-docs.md +93 -0
- package/generated/docs/patterns-form-docs.md +3902 -0
- package/generated/docs/patterns-form-recipes-docs.md +1370 -0
- package/generated/docs/patterns-introduction-docs.md +24 -0
- package/generated/docs/patterns-loading-docs.md +2940 -201
- package/generated/docs/resources-introduction-docs.md +38 -0
- package/generated/docs/resources-mcp-server-docs.md +27 -0
- package/generated/docs/styling-props-colour-docs.md +172 -0
- package/generated/docs/styling-props-elevation-docs.md +88 -0
- package/generated/docs/styling-props-radius-docs.md +86 -0
- package/generated/docs/styling-props-reference-docs.md +160 -0
- package/generated/docs/styling-props-screen-readers-docs.md +71 -0
- package/generated/docs/styling-props-sizing-docs.md +627 -0
- package/generated/docs/styling-props-spacing-docs.md +2282 -0
- package/generated/docs/styling-props-typography-docs.md +121 -0
- package/generated/docs/themes-available-themes-docs.md +29 -29
- package/generated/docs/themes-introduction-docs.md +1 -1
- package/package.json +3 -22
- package/generated/docs/components-button-recipes-docs.md +0 -76
- package/generated/docs/components-card-recipes-docs.md +0 -89
- package/generated/docs/components-combobox-docs.md +0 -1016
- package/generated/docs/components-form-docs.md +0 -2410
- package/generated/docs/components-form-recipes-docs.md +0 -886
- package/generated/docs/components-navbar-docs.md +0 -291
- package/generated/docs/components-navbar-recipes-docs.md +0 -413
- package/generated/docs/components-toaster-toast-docs.md +0 -157
- package/generated/docs/foundations-colours-docs.md +0 -257
- package/generated/docs/foundations-typography-docs.md +0 -191
- package/generated/docs/resources-changelog-docs.md +0 -6
- package/generated/docs/themes-tokens-docs.md +0 -1200
|
@@ -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 = () => {</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text">const ConditionalFields = () => {</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> // We need to create our own state to manage the visibility of the fields, </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> // Instead of creating our own state, we can now use the form state via the useWatch hook, </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> // which means we have two sources of truth potentially making our code harder to maintain</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> // meaning we still have a single source of truth</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> const [show, setShow] = useState(['name']);</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> const show = IressForm.useWatch({ name: 'show'});</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-10regm7-empty-line"><pre></pre></td><td class="css-vl0irh-content css-10regm7-empty-line"><pre class="css-o1u8iu-content-text"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text"> return (</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressForm></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> <></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressField label="Show fields"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> <IressFormField </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressCheckboxGroup value={show} onChange={(newValues) => setShow(newValues)}></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> label="Show fields" </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> name="show"</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> render={(controlledProps) => (</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> <IressCheckboxGroup {...controlledProps}></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text"> <IressCheckbox value="name">Name</IressCheckbox></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text"> <IressCheckbox value="email">Email</IressCheckbox></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text"> </IressCheckboxGroup></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> </IressField></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> )}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> {show.includes('name') && (</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> /></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressField label="Name"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> {show?.includes('name') && (</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressInput name="name" /></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> <IressFormField </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> </IressField></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> label="Name" </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> )}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> name="name"</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> {show.includes('email') && (</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> render={(controlledProps) => <IressInput {...controlledProps} />}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressField label="Email"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> /></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressInput name="email" type="email" /></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> )}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> </IressField></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> {show?.includes('email') && (</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> )}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> <IressFormField </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> </IressForm></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> label="Email" </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> name="email"</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> render={(controlledProps) => <IressInput {...controlledProps} type="email" />}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> /></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> )}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> </></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text"> );</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text">};</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-10regm7-empty-line css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-10regm7-empty-line css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text">export const App = () => (</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> <IressForm defaultValues={{ show: ['name'] }}></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> <ConditionalFields /> </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> </IressForm></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text">);</pre></td></tr></tbody></table>
|
|
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 = () => (</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressForm valueMissing="{{fieldName}} needs to be filled in!"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> <IressForm></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressField label="Name"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> <IressFormField </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressInput name="name" required /></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> label="Name"</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> </IressField></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> name="name"</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressField label="Email"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> render={(controlledProps) => <IressInput {...controlledProps} />}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> <IressInput name="email" maxLength={10} /></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> rules={{ required: 'Name needs to be filled in!' }}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-rq9a2a-diff-removed"><pre>-</pre></td><td class="css-vl0irh-content css-rq9a2a-diff-removed"><pre class="css-o1u8iu-content-text"> </IressField></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> /></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> <IressFormField </pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> label="Email"</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> name="email"</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> render={(controlledProps) => <IressInput {...controlledProps} type="email" maxLength={10} />}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> rules={{ maxLength: 10 }}</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker css-cnnxkz-diff-added"><pre>+</pre></td><td class="css-vl0irh-content css-cnnxkz-diff-added"><pre class="css-o1u8iu-content-text"> /></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text"> <IressButton type="submit" mode="primary"></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text"> Sign up</pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text"> </IressButton></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text"> </IressForm></pre></td></tr><tr class="css-1n7ec1i-line"><td class="css-17vezug-marker"><pre></pre></td><td class="css-vl0irh-content"><pre class="css-o1u8iu-content-text">);</pre></td></tr></tbody></table>
|
|
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)
|