@tsed/react-formio 1.12.0 → 1.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/dist/components/actions-table/actionsTable.component.d.ts +0 -1
  2. package/dist/components/actions-table/actionsTable.stories.d.ts +0 -1
  3. package/dist/components/alert/alert.component.d.ts +1 -2
  4. package/dist/components/alert/alert.stories.d.ts +0 -1
  5. package/dist/components/card/card.stories.d.ts +0 -1
  6. package/dist/components/form/form.component.d.ts +3 -2
  7. package/dist/components/form/form.component.spec.d.ts +1 -0
  8. package/dist/components/form/form.stories.d.ts +3689 -79
  9. package/dist/components/form/useForm.hook.d.ts +1 -1
  10. package/dist/components/form-access/formAccess.stories.d.ts +1 -2
  11. package/dist/components/form-action/formAction.stories.d.ts +0 -1
  12. package/dist/components/form-builder/formBuilder.stories.d.ts +518 -153
  13. package/dist/components/form-edit/formEdit.component.d.ts +0 -1
  14. package/dist/components/form-edit/formEdit.stories.d.ts +18 -19
  15. package/dist/components/form-settings/formSettings.component.d.ts +0 -1
  16. package/dist/components/form-settings/formSettings.stories.d.ts +1 -2
  17. package/dist/components/forms-table/components/formCell.component.d.ts +0 -1
  18. package/dist/components/forms-table/formsTable.component.d.ts +0 -1
  19. package/dist/components/forms-table/formsTable.stories.d.ts +0 -1
  20. package/dist/components/input-tags/inputTags.component.d.ts +0 -1
  21. package/dist/components/input-tags/inputTags.stories.d.ts +2 -3
  22. package/dist/components/input-text/inputText.component.d.ts +0 -1
  23. package/dist/components/input-text/inputText.stories.d.ts +0 -1
  24. package/dist/components/loader/loader.component.d.ts +1 -1
  25. package/dist/components/loader/loader.stories.d.ts +0 -1
  26. package/dist/components/modal/modal.component.d.ts +1 -1
  27. package/dist/components/modal/modal.stories.d.ts +0 -1
  28. package/dist/components/pagination/pagination.component.d.ts +0 -1
  29. package/dist/components/pagination/pagination.stories.d.ts +0 -1
  30. package/dist/components/react-component/reactComponent.component.d.ts +3 -3
  31. package/dist/components/select/select.stories.d.ts +2 -3
  32. package/dist/components/submissions-table/submissionsTable.component.d.ts +0 -1
  33. package/dist/components/submissions-table/submissionsTable.stories.d.ts +13 -14
  34. package/dist/components/table/components/defaultArrowSort.component.d.ts +0 -1
  35. package/dist/components/table/components/defaultCell.component.d.ts +0 -1
  36. package/dist/components/table/components/defaultCellHeader.component.d.ts +0 -1
  37. package/dist/components/table/components/defaultCellOperations.component.d.ts +0 -1
  38. package/dist/components/table/components/defaultOperationButton.component.d.ts +0 -1
  39. package/dist/components/table/filters/defaultColumnFilter.component.d.ts +0 -1
  40. package/dist/components/table/filters/selectColumnFilter.component.d.ts +0 -1
  41. package/dist/components/table/filters/sliderColumnFilter.component.d.ts +0 -1
  42. package/dist/components/table/table.stories.d.ts +4 -5
  43. package/dist/components/tabs/tabs.component.stories.d.ts +0 -1
  44. package/dist/hooks/useTooltip.d.ts +1 -1
  45. package/dist/index.js +342 -274
  46. package/dist/index.js.map +1 -1
  47. package/dist/index.modern.js +313 -268
  48. package/dist/index.modern.js.map +1 -1
  49. package/dist/stores/auth/auth.selectors.d.ts +1 -1
  50. package/jest.config.js +1 -1
  51. package/package.json +5 -5
  52. package/readme.md +51 -18
  53. package/src/components/__fixtures__/form.fixture.json +23 -0
  54. package/src/components/form/form.component.spec.tsx +56 -0
  55. package/src/components/form/form.component.tsx +5 -3
  56. package/src/components/form/form.stories.tsx +182 -11
  57. package/src/components/form/useForm.hook.ts +54 -29
  58. package/src/components/form-access/formAccess.component.tsx +1 -1
  59. package/src/components/form-access/formAccess.utils.ts +13 -13
  60. package/src/components/form-action/formAction.component.tsx +1 -1
  61. package/src/components/form-builder/formBuilder.component.tsx +1 -1
  62. package/src/components/form-edit/formCtas.component.tsx +32 -30
  63. package/src/components/form-edit/formEdit.component.tsx +1 -1
  64. package/src/components/form-settings/formSettings.utils.ts +3 -3
  65. package/src/components/input-tags/inputTags.component.tsx +3 -3
  66. package/src/components/input-text/inputText.component.spec.tsx +1 -1
  67. package/src/components/input-text/inputText.component.tsx +3 -3
  68. package/src/components/modal/modal.component.tsx +2 -2
  69. package/src/components/react-component/reactComponent.component.tsx +9 -6
  70. package/src/components/select/select.component.tsx +2 -2
  71. package/src/components/submissions-table/submissionsTable.component.tsx +6 -6
  72. package/src/components/table/table.component.tsx +58 -44
  73. package/src/components/table/utils/mapFormToColumns.tsx +1 -1
  74. package/src/components/tabs/tabs.component.tsx +1 -1
  75. package/src/hooks/useTooltip.ts +1 -1
  76. package/src/stores/action-info/action-info.selectors.ts +1 -1
  77. package/src/stores/auth/auth.utils.tsx +2 -2
  78. package/src/stores/auth/getAccess.action.ts +2 -2
  79. package/src/stores/auth/logout.action.spec.ts +1 -0
  80. package/src/stores/form/form.selectors.ts +1 -1
  81. package/src/stores/root/root.selectors.ts +2 -2
  82. package/tsconfig.json +10 -27
  83. package/tsconfig.node.json +8 -0
  84. package/craco.config.js +0 -11
  85. package/tsconfig.test.json +0 -6
@@ -1,6 +1,6 @@
1
1
  import { RoleSchema, Submission } from "../../interfaces";
2
2
  import { AuthState } from "./auth.reducers";
3
3
  export declare const selectAuth: (state: any) => AuthState<any>;
4
- export declare const selectUser: <User = any>(state: any) => Submission<User>;
4
+ export declare const selectUser: <User = any>(state: any) => Submission<User> | null;
5
5
  export declare const selectRoles: (state: any) => Record<string, RoleSchema>;
6
6
  export declare const selectIsAuthenticated: (state: any) => boolean;
package/jest.config.js CHANGED
@@ -1 +1 @@
1
- module.exports = require("@tsed/config/jest.config")("react-formio");
1
+ module.exports = require("@tsed/config/jest/jest.web.config.js");
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@tsed/react-formio",
3
- "version": "1.12.0",
3
+ "version": "1.13.2",
4
4
  "description": "Provide a react formio wrapper. Written in TypeScript.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.modern.js",
7
7
  "source": "src/index.ts",
8
8
  "scripts": {
9
- "test": "cross-env CI=1 yarn craco test --coverage",
9
+ "test": "cross-env NODE_ENV=test jest --coverage",
10
10
  "build": "microbundle --no-compress --format modern,cjs --jsx React.createElement",
11
11
  "watch": "microbundle watch --no-compress --format modern,cjs --jsx React.createElement"
12
12
  },
13
13
  "dependencies": {
14
- "@tsed/redux-utils": "1.12.0",
14
+ "@tsed/redux-utils": "1.13.2",
15
15
  "eventemitter2": "^6.4.3",
16
16
  "prop-types": "^15.7.2"
17
17
  },
@@ -26,8 +26,8 @@
26
26
  "tooltip.js": ">=1.3.3"
27
27
  },
28
28
  "devDependencies": {
29
- "@tsed/tailwind": "1.12.0",
30
- "@tsed/tailwind-formio": "1.12.0"
29
+ "@tsed/tailwind": "1.13.2",
30
+ "@tsed/tailwind-formio": "1.13.2"
31
31
  },
32
32
  "repository": "https://github.com/TypedProject/tsed-formio",
33
33
  "bugs": {
package/readme.md CHANGED
@@ -25,9 +25,11 @@
25
25
 
26
26
  <hr />
27
27
 
28
- A [React](http://facebook.github.io/react/) library for rendering out forms based on the [Form.io](https://www.form.io) platform.
28
+ A [React](http://facebook.github.io/react/) library for rendering out forms based on the [Form.io](https://www.form.io)
29
+ platform.
29
30
 
30
- This module is based on the original [react-formio](https://github.com/formio/react-formio) and add extra features listed above.
31
+ This module is based on the original [react-formio](https://github.com/formio/react-formio) and add extra features
32
+ listed above.
31
33
 
32
34
  See our [storybook](https://formio.tsed.io/) to see all available components.
33
35
 
@@ -83,7 +85,10 @@ export default App;
83
85
 
84
86
  ### Form
85
87
 
86
- The form component is the primary component of the system. It is what takes the form definition (json) and renders the form into html. There are multiple ways to send the form to the Form component. The two main ways are to pass the `src` prop with a url to the form definition, usually a form.io server. The other is to pass the `form` prop with the json definition and optionally a `url` prop with the location of the form.
88
+ The form component is the primary component of the system. It is what takes the form definition (json) and renders the
89
+ form into html. There are multiple ways to send the form to the Form component. The two main ways are to pass the `src`
90
+ prop with a url to the form definition, usually a form.io server. The other is to pass the `form` prop with the json
91
+ definition and optionally a `url` prop with the location of the form.
87
92
 
88
93
  #### Props
89
94
 
@@ -101,6 +106,7 @@ You can respond to various events in the form. Simply pass in a prop with a func
101
106
 
102
107
  | Name | Parameters | Description |
103
108
  | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
109
+ | `onAsyncSubmit` | `submission`: object | When the submit button is pressed and the submission has started. Use this event if you submit data to external service. |
104
110
  | `onSubmit` | `submission`: object | When the submit button is pressed and the submission has started. If `src` is not provided, this will be the final submit event. |
105
111
  | `onSubmitDone` | `submission`: object | When the submission has successfully been made to the server. This will only fire if `src` is set. |
106
112
  | `onChange` | `submission`: object, `submission.changed`: object of what changed, `submission.isValid`: boolean - if the submission passes validations. | A value in the submission has changed. |
@@ -151,21 +157,35 @@ const form = {
151
157
  ReactDOM.render(
152
158
  <Form<MyFormData>
153
159
  form={form}
154
- onSubmit={(submission) => {
155
- console.log(submission);
160
+ onAsyncSubmit={(submission) => {
161
+ return httpClient.post("/path/to/external/service", { data: submission }).catch((er) => {
162
+ err.errors = [
163
+ {
164
+ message: "My custom message about this field",
165
+ type: "custom",
166
+ path: ["title"],
167
+ level: "error"
168
+ }
169
+ ];
170
+ throw error;
171
+ });
156
172
  }}
157
173
  />,
158
174
  document.getElementById("example")
159
175
  );
160
176
  ```
161
177
 
178
+ > See [Form with error from server](https://formio.tsed.io/?path=/story/reactformio-form--trigger-error)
179
+
162
180
  ### FormBuilder
163
181
 
164
- The [FormBuilder](<[FormsTable](https://formio.tsed.io/?path=/story/reactformio-formbuilder--sandbox)>) class can be used
182
+ The [FormBuilder](https://formio.tsed.io/?path=/story/reactformio-formbuilder--sandbox)) class can be
183
+ used
165
184
  to embed a form builder directly in your react application.
166
185
  Please note that you'll need to include the CSS for the form builder from formio.js as well.
167
186
 
168
- Please note that the [FormBuilder](<[FormsTable](https://formio.tsed.io/?path=/story/reactformio-formbuilder--sandbox)>) component
187
+ Please note that the [FormBuilder](<[FormsTable](https://formio.tsed.io/?path=/story/reactformio-formbuilder--sandbox)>)
188
+ component
169
189
  does not load and save from/to a url. You must handle the form definition loading and saving yourself or use
170
190
  the [FormEdit](https://formio.tsed.io/?path=/story/reactformio-formedit--sandbox) component.
171
191
 
@@ -200,15 +220,19 @@ ReactDOM.render(<FormBuilder form={{ display: "form" }} onChange={(schema) => co
200
220
  ### FormEdit
201
221
 
202
222
  The [FormEdit](https://formio.tsed.io/?path=/story/reactformio-formedit--sandbox) component wraps
203
- the [FormBuilder](<[FormsTable](https://formio.tsed.io/?path=/story/reactformio-formbuilder--sandbox)>) component and adds the title, display, name and path fields at the top along with a save button.
223
+ the [FormBuilder](<[FormsTable](https://formio.tsed.io/?path=/story/reactformio-formbuilder--sandbox)>) component and
224
+ adds the title, display, name and path fields at the top along with a save button.
204
225
 
205
226
  #### Props
206
227
 
207
- | Name | Type | Default | Description |
208
- | ---------- | ------ | ------------------- | ---------------------------------------------------- | --------------------------------------------------------------- |
209
- | `form` | object | {display: 'form' \ | 'wizard'} | The form definition of the exiting form that is to be modified. |
210
- | `options` | object | {} | The options to be passed to FormBuilder |
211
- | `saveText` | string | '' | The string that will be displayed in the save-button |
228
+ | Name | Type | Default | Description |
229
+ | --------------------------------------------------------------- | ------ | ------------------- | ----------- | --------------------------------------------------------------- |
230
+ | --------------------------------------------------------------- |
231
+ | `form` | object | {display: 'form' \ | 'wizard'} | The form definition of the exiting form that is to be modified. |
232
+
233
+ |
234
+ | `options` | object | {} | The options to be passed to FormBuilder |
235
+ | `saveText` | string | '' | The string that will be displayed in the save-button |
212
236
 
213
237
  #### Event Props
214
238
 
@@ -218,7 +242,8 @@ the [FormBuilder](<[FormsTable](https://formio.tsed.io/?path=/story/reactformio-
218
242
 
219
243
  ### FormsTable
220
244
 
221
- The [FormsTable](https://formio.tsed.io/?path=/story/reactformio-formstable--sandbox) component can be used to render a list of forms with buttons to edit, view, delete, etc on each row.
245
+ The [FormsTable](https://formio.tsed.io/?path=/story/reactformio-formstable--sandbox) component can be used to render a
246
+ list of forms with buttons to edit, view, delete, etc on each row.
222
247
 
223
248
  #### Props
224
249
 
@@ -308,7 +333,8 @@ Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com
308
333
 
309
334
  ## Sponsors
310
335
 
311
- Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/tsed#sponsor)]
336
+ Support this project by becoming a sponsor. Your logo will show up here with a link to your
337
+ website. [[Become a sponsor](https://opencollective.com/tsed#sponsor)]
312
338
 
313
339
  ## License
314
340
 
@@ -316,8 +342,15 @@ The MIT License (MIT)
316
342
 
317
343
  Copyright (c) 2016 - 2021 Romain Lenzotti
318
344
 
319
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
345
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
346
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
347
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
348
+ persons to whom the Software is furnished to do so, subject to the following conditions:
320
349
 
321
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
350
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
351
+ Software.
322
352
 
323
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
353
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
354
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
355
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
356
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1966,6 +1966,29 @@
1966
1966
  "id": "em22wkd",
1967
1967
  "defaultValue": null
1968
1968
  },
1969
+ {
1970
+ "label": "Edit Grid",
1971
+ "tableView": false,
1972
+ "rowDrafts": false,
1973
+ "key": "editGrid",
1974
+ "type": "editgrid",
1975
+ "displayAsTable": false,
1976
+ "input": true,
1977
+ "components": [
1978
+ {
1979
+ "label": "Currency",
1980
+ "mask": false,
1981
+ "spellcheck": true,
1982
+ "tableView": true,
1983
+ "currency": "USD",
1984
+ "inputFormat": "plain",
1985
+ "key": "currency",
1986
+ "type": "currency",
1987
+ "input": true,
1988
+ "delimiter": true
1989
+ }
1990
+ ]
1991
+ },
1969
1992
  {
1970
1993
  "type": "button",
1971
1994
  "label": "Submit",
@@ -0,0 +1,56 @@
1
+ import {render} from "@testing-library/react";
2
+ import {screen} from "@testing-library/dom";
3
+ import {Form} from "./form.component";
4
+
5
+ async function createFixture(props: any = {}) {
6
+ const onSubmit = jest.fn();
7
+ const form = {
8
+ type: "form",
9
+ display: "form",
10
+ tags: [],
11
+ components: [
12
+ {
13
+ label: "First name",
14
+ "placeholder": "Fill first name",
15
+ widget: {
16
+ type: "input"
17
+ },
18
+ errorLabel: "",
19
+ key: "firstName",
20
+ inputType: "text",
21
+ type: "textfield",
22
+ id: "eqb1o4r",
23
+ defaultValue: ""
24
+ },
25
+ {
26
+ label: "Submit form",
27
+ showValidations: false,
28
+ tableView: false,
29
+ key: "submit",
30
+ type: "button",
31
+ input: true
32
+ }
33
+ ]
34
+ };
35
+
36
+ render(<Form {...props} form={form} onSubmit={onSubmit}/>);
37
+
38
+ return {onSubmit};
39
+ }
40
+
41
+ describe("Form", () => {
42
+ it("should render the Form", async () => {
43
+ await createFixture({
44
+ data: [],
45
+ model: "MyForm",
46
+ groups: ["creation"],
47
+ params: {
48
+ env: "prod"
49
+ },
50
+ isActive: false
51
+ });
52
+
53
+ expect(screen.getByTestId("formioContainer")).toBeInTheDocument();
54
+ expect(screen.getByText("First name")).toBeInTheDocument();
55
+ });
56
+ });
@@ -14,10 +14,10 @@ export interface FormProps<Data = any> extends UseFormHookProps<Data> {
14
14
  className?: string;
15
15
  }
16
16
 
17
- export function Form(props: Partial<FormProps>) {
17
+ export function Form<Data = any>(props: Partial<FormProps<Data>>) {
18
18
  const { element } = useForm(props);
19
19
 
20
- return <div data-testid={"formioContainer" + (props.name || "")} ref={element} className={props.className} />;
20
+ return <div data-testid={`formioContainer${props.name || ""}`} ref={element} className={props.className} />;
21
21
  }
22
22
 
23
23
  Form.propTypes = {
@@ -47,7 +47,8 @@ Form.propTypes = {
47
47
  noAlerts: PropTypes.bool,
48
48
  i18n: PropTypes.any,
49
49
  template: PropTypes.string,
50
- saveDraft: PropTypes.bool
50
+ saveDraft: PropTypes.bool,
51
+ hooks: PropTypes.any
51
52
  }),
52
53
  onPrevPage: PropTypes.func,
53
54
  onNextPage: PropTypes.func,
@@ -56,6 +57,7 @@ Form.propTypes = {
56
57
  onCustomEvent: PropTypes.func,
57
58
  onComponentChange: PropTypes.func,
58
59
  onSubmit: PropTypes.func,
60
+ onAsyncSubmit: PropTypes.func,
59
61
  onSubmitDone: PropTypes.func,
60
62
  onFormLoad: PropTypes.func,
61
63
  onError: PropTypes.func,
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
- import { Form } from "./form.component";
2
+ import {Form} from "./form.component";
3
3
  import form from "../__fixtures__/form.fixture.json";
4
+ import {Submission} from "../../interfaces";
4
5
 
5
6
  export default {
6
7
  title: "ReactFormio/Form",
@@ -10,22 +11,113 @@ export default {
10
11
  control: {
11
12
  type: "object"
12
13
  }
13
- }
14
- },
15
- parameters: {}
14
+ },
15
+ onPrevPage: {action: "onPrevPage"},
16
+ onNextPage: {action: "onNextPage"},
17
+ onCancel: {action: "onCancel"},
18
+ onChange: {action: "onChange"},
19
+ onCustomEvent: {action: "onCustomEvent"},
20
+ onComponentChange: {action: "onComponentChange"},
21
+ onSubmit: {action: "onSubmit"},
22
+ onAsyncSubmit: {action: "onAsyncSubmit"},
23
+ onSubmitDone: {action: "onSubmitDone"},
24
+ onFormLoad: {action: "onFormLoad"},
25
+ onError: {action: "onError"},
26
+ onRender: {action: "onRender"},
27
+ onAttach: {action: "onAttach"},
28
+ onBuild: {action: "onBuild"},
29
+ onFocus: {action: "onFocus"},
30
+ onBlur: {action: "onBlur"},
31
+ onInitialized: {action: "onInitialized"},
32
+ onFormReady: {action: "onFormReady"}
33
+ }
16
34
  };
17
35
 
36
+ function filter(args: any[]) {
37
+ return args
38
+ .map((item) => {
39
+ if (item && (item._form)) {
40
+ return "FormioInstance";
41
+ }
42
+
43
+ if (item && item.component) {
44
+ return ["Component", item.component.type, item.component.key].filter(Boolean).join(":");
45
+ }
46
+
47
+ if (item && item.changed) {
48
+ return `${item.changed.component.key}(${item.changed.value})`;
49
+ }
50
+
51
+ return item;
52
+ });
53
+ }
54
+
55
+ function wrap(args: any) {
56
+ return {
57
+ ...args,
58
+ onPrevPage: (...list: any[]) => {
59
+ return args.onPrevPage(...filter(list));
60
+ },
61
+ onNextPage: (...list: any[]) => {
62
+ return args.onNextPage(...filter(list));
63
+ },
64
+ onCancel: (...list: any[]) => {
65
+ return args.onCancel(...filter(list));
66
+ },
67
+ onChange: (...list: any[]) => {
68
+ return args.onChange(...filter(list));
69
+ },
70
+ onCustomEvent: (...list: any[]) => {
71
+ return args.onCustomEvent(...filter(list));
72
+ },
73
+ onComponentChange: (...list: any[]) => {
74
+ return args.onComponentChange(...filter(list));
75
+ },
76
+ onSubmit: (...list: any[]) => {
77
+ return args.onSubmit(...filter(list));
78
+ },
79
+ onAsyncSubmit: (...list: any[]) => {
80
+ return args.onAsyncSubmit(...filter(list));
81
+ },
82
+ onSubmitDone: (...list: any[]) => {
83
+ return args.onSubmitDone(...filter(list));
84
+ },
85
+ onFormLoad: (...list: any[]) => {
86
+ return args.onFormLoad(...filter(list));
87
+ },
88
+ onError: (...list: any[]) => {
89
+ return args.onError(...filter(list));
90
+ },
91
+ onRender: (...list: any[]) => {
92
+ return args.onRender(...filter(list));
93
+ },
94
+ onAttach: (...list: any[]) => {
95
+ return args.onAttach(...filter(list));
96
+ },
97
+ onBuild: (...list: any[]) => {
98
+ return args.onBuild(...filter(list));
99
+ },
100
+ onFocus: (...list: any[]) => {
101
+ return args.onFocus(...filter(list));
102
+ },
103
+ onBlur: (...list: any[]) => {
104
+ return args.onBlur(...filter(list));
105
+ },
106
+ onInitialized: (...list: any[]) => {
107
+ return args.onInitialized(...filter(list));
108
+ },
109
+ onFormReady: (...list: any[]) => {
110
+ return args.onFormReady(...filter(list));
111
+ }
112
+ };
113
+ }
114
+
18
115
  export const Sandbox = (args: any) => {
19
- delete args.onRender;
20
- delete args.onComponentChange;
21
116
  return (
22
117
  <Form
23
- {...args}
118
+ {...wrap(args)}
24
119
  form={args.form}
25
- onFormReady={(formio) => {
26
- console.log("ready", formio);
27
- }}
28
- options={{ template: "tailwind", iconset: "bx" }}
120
+ options={{template: "tailwind", iconset: "bx"}}
29
121
  />
30
122
  );
31
123
  };
@@ -33,3 +125,82 @@ export const Sandbox = (args: any) => {
33
125
  Sandbox.args = {
34
126
  form
35
127
  };
128
+
129
+ export const TriggerError = (args: any) => {
130
+ const onAsyncSubmit = (submission: Submission) => {
131
+ return new Promise((resolve, reject) => {
132
+ setTimeout(() => {
133
+ reject(new Error("server error"));
134
+ }, 500);
135
+ }).catch((error) => {
136
+ error.errors = {
137
+ "message": "My custom message about this field",
138
+ "type": "custom",
139
+ "path": ["firstName"],
140
+ "level": "error"
141
+ };
142
+ throw error;
143
+ });
144
+ };
145
+
146
+ return (
147
+ <Form<any>
148
+ {...wrap(args)}
149
+ form={args.form}
150
+ onAsyncSubmit={onAsyncSubmit}
151
+ />
152
+ );
153
+ };
154
+
155
+ TriggerError.args = {
156
+ form: {
157
+ "type": "form",
158
+ "display": "form",
159
+ "tags": [],
160
+ "components": [
161
+ {
162
+ "label": "First name",
163
+ "widget": {
164
+ "type": "input"
165
+ },
166
+ "errorLabel": "",
167
+ "key": "firstName",
168
+ "inputType": "text",
169
+ "type": "textfield",
170
+ "id": "eqb1o4r",
171
+ "defaultValue": ""
172
+ },
173
+ {
174
+ "label": "Submit",
175
+ "showValidations": false,
176
+ "tableView": false,
177
+ "key": "submit",
178
+ "type": "button",
179
+ "input": true
180
+ }
181
+ ]
182
+ }
183
+ };
184
+
185
+
186
+ export const ReadOnly = (args: any) => {
187
+ return (
188
+ <Form
189
+ {...wrap(args)}
190
+ options={{template: "tailwind", iconset: "bx", readOnly: args.readOnly}}
191
+ form={args.form}
192
+ submission={{
193
+ data: {
194
+ editGrid: [
195
+ {currency: "EUR"}
196
+ ]
197
+ }
198
+ }}
199
+ />
200
+ );
201
+ };
202
+
203
+ ReadOnly.args = {
204
+ readonly: true,
205
+ form
206
+ };
@@ -1,10 +1,10 @@
1
- import { ExtendedComponentSchema, Form } from "formiojs";
2
- import { get } from "lodash";
1
+ import {ExtendedComponentSchema, Form} from "formiojs";
2
+ import {get} from "lodash";
3
3
  import cloneDeep from "lodash/cloneDeep";
4
4
  import isEqual from "lodash/isEqual";
5
- import { useEffect, useRef } from "react";
6
- import { callLast } from "../../utils/callLast";
7
- import { FormOptions, FormSchema, Submission } from "../../interfaces";
5
+ import {useEffect, useRef} from "react";
6
+ import {callLast} from "../../utils/callLast";
7
+ import {FormOptions, FormSchema, Submission} from "../../interfaces";
8
8
 
9
9
  export interface ChangedSubmission<T = any> extends Submission<T> {
10
10
  changed: any;
@@ -34,6 +34,8 @@ export interface UseFormHookProps<Data = any> extends Record<string, any> {
34
34
  * Data submission
35
35
  */
36
36
  submission?: Submission<Data>;
37
+
38
+ /// events
37
39
  onPrevPage?: (obj: FormPageChangeProps<Data>) => void;
38
40
  onNextPage?: (obj: FormPageChangeProps<Data>) => void;
39
41
  onCancel?: Function;
@@ -41,6 +43,7 @@ export interface UseFormHookProps<Data = any> extends Record<string, any> {
41
43
  onCustomEvent?: (obj: { type: string; event: string; component: ExtendedComponentSchema; data: any }) => void;
42
44
  onComponentChange?: (component: ExtendedComponentSchema) => void;
43
45
  onSubmit?: (submission: Submission<Data>) => void;
46
+ onAsyncSubmit?: (submission: Submission<Data>) => Promise<any>;
44
47
  onSubmitDone?: (submission: Submission<Data>) => void;
45
48
  onFormLoad?: Function;
46
49
  onError?: (errors: any) => void;
@@ -51,15 +54,50 @@ export interface UseFormHookProps<Data = any> extends Record<string, any> {
51
54
  onBlur?: Function;
52
55
  onInitialized?: Function;
53
56
  onFormReady?: (formio: Form) => void;
54
- formioform?: any;
57
+ }
58
+
59
+ function useDebounce(event: string, callback: any, events: Map<string, any>) {
60
+ useEffect(() => {
61
+ callback && events.set(event, callLast(callback, 100));
62
+ }, [callback, event, events]);
63
+ }
64
+
65
+ function useEvents(funcs: any) {
66
+ const events = useRef<Map<string, any>>(new Map());
67
+
68
+ const hasEvent = (event: string) => {
69
+ return funcs.hasOwnProperty(event) && typeof funcs[event] === "function"
70
+ }
71
+ const emit = (event: string, ...args: any[]) => {
72
+ if (hasEvent(event)) {
73
+ const fn = events.current.has(event) ? events.current.get(event) : funcs[event]
74
+ return fn(...args);
75
+ }
76
+ };
77
+
78
+ useDebounce("onChange", funcs.onChange, events.current);
79
+
80
+ return {events, emit, hasEvent};
55
81
  }
56
82
 
57
83
  export function useForm<Data = any>(props: UseFormHookProps<Data>) {
58
- const { src, form, options = {}, submission, url, ...funcs } = props;
84
+ const {src, form, options = {}, submission, url, ...funcs} = props;
59
85
  const element = useRef<any>();
60
86
  const isLoaded = useRef<boolean>();
61
87
  const instance = useRef<Form>();
62
- const events = useRef<Map<string, any>>(new Map());
88
+ const {emit, hasEvent} = useEvents(funcs);
89
+
90
+ async function customValidation(submission: Submission, callback: (err: Error | null) => void) {
91
+ if (hasEvent("onAsyncSubmit")) {
92
+ try {
93
+ await emit("onAsyncSubmit", submission, instance.current);
94
+ } catch (err) {
95
+ callback(err?.errors || err);
96
+ }
97
+ } else {
98
+ callback(null);
99
+ }
100
+ }
63
101
 
64
102
  const createWebForm = (srcOrForm: any, options: any) => {
65
103
  options = Object.assign({}, options);
@@ -67,6 +105,11 @@ export function useForm<Data = any>(props: UseFormHookProps<Data>) {
67
105
 
68
106
  if (!instance.current) {
69
107
  isLoaded.current = false;
108
+ options.hooks = {
109
+ ...(options.hooks || {}),
110
+ customValidation: options?.hooks?.customValidation || customValidation
111
+ };
112
+
70
113
  instance.current = new Form(element.current, srcOrForm, options);
71
114
 
72
115
  instance.current.onAny((event: string, ...args: any[]): void => {
@@ -75,25 +118,15 @@ export function useForm<Data = any>(props: UseFormHookProps<Data>) {
75
118
  }
76
119
 
77
120
  if (event.startsWith("formio.")) {
78
- const funcName = `on${event.charAt(7).toUpperCase()}${event.slice(8)}`;
121
+ const eventName = `on${event.charAt(7).toUpperCase()}${event.slice(8)}`;
79
122
 
80
- if (funcName === "onChange") {
123
+ if (eventName === "onChange") {
81
124
  if (isEqual(get(submission, "data"), args[0].data)) {
82
125
  return;
83
126
  }
84
127
  }
85
128
 
86
- if (
87
- // eslint-disable-next-line no-prototype-builtins
88
- props.hasOwnProperty(funcName) &&
89
- typeof funcs[funcName] === "function"
90
- ) {
91
- if (!events.current.has(funcName)) {
92
- const fn = callLast(funcs[funcName], 100);
93
- events.current.set(funcName, fn);
94
- }
95
- events.current.get(funcName)(...args);
96
- }
129
+ emit(eventName, ...args, instance.current)
97
130
  }
98
131
  });
99
132
 
@@ -152,14 +185,6 @@ export function useForm<Data = any>(props: UseFormHookProps<Data>) {
152
185
  };
153
186
  }, []);
154
187
 
155
- useEffect(() => {
156
- props.onSubmit && events.current.set("onSubmit", props.onSubmit);
157
- }, [props.onSubmit, events]);
158
-
159
- useEffect(() => {
160
- props.onSubmitDone && events.current.set("onSubmitDone", props.onSubmitDone);
161
- }, [props.onSubmitDone, events]);
162
-
163
188
  return {
164
189
  element
165
190
  };
@@ -51,7 +51,7 @@ function useFormAccess({ form: formDefinition, roles, onSubmit, options }: FormA
51
51
  submissions,
52
52
  onChange,
53
53
  onSubmit: () => {
54
- onSubmit(submissionsToDataAccess(formDefinition, submissions));
54
+ onSubmit && onSubmit(submissionsToDataAccess(formDefinition, submissions));
55
55
  }
56
56
  };
57
57
  }