@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.
- package/dist/components/actions-table/actionsTable.component.d.ts +0 -1
- package/dist/components/actions-table/actionsTable.stories.d.ts +0 -1
- package/dist/components/alert/alert.component.d.ts +1 -2
- package/dist/components/alert/alert.stories.d.ts +0 -1
- package/dist/components/card/card.stories.d.ts +0 -1
- package/dist/components/form/form.component.d.ts +3 -2
- package/dist/components/form/form.component.spec.d.ts +1 -0
- package/dist/components/form/form.stories.d.ts +3689 -79
- package/dist/components/form/useForm.hook.d.ts +1 -1
- package/dist/components/form-access/formAccess.stories.d.ts +1 -2
- package/dist/components/form-action/formAction.stories.d.ts +0 -1
- package/dist/components/form-builder/formBuilder.stories.d.ts +518 -153
- package/dist/components/form-edit/formEdit.component.d.ts +0 -1
- package/dist/components/form-edit/formEdit.stories.d.ts +18 -19
- package/dist/components/form-settings/formSettings.component.d.ts +0 -1
- package/dist/components/form-settings/formSettings.stories.d.ts +1 -2
- package/dist/components/forms-table/components/formCell.component.d.ts +0 -1
- package/dist/components/forms-table/formsTable.component.d.ts +0 -1
- package/dist/components/forms-table/formsTable.stories.d.ts +0 -1
- package/dist/components/input-tags/inputTags.component.d.ts +0 -1
- package/dist/components/input-tags/inputTags.stories.d.ts +2 -3
- package/dist/components/input-text/inputText.component.d.ts +0 -1
- package/dist/components/input-text/inputText.stories.d.ts +0 -1
- package/dist/components/loader/loader.component.d.ts +1 -1
- package/dist/components/loader/loader.stories.d.ts +0 -1
- package/dist/components/modal/modal.component.d.ts +1 -1
- package/dist/components/modal/modal.stories.d.ts +0 -1
- package/dist/components/pagination/pagination.component.d.ts +0 -1
- package/dist/components/pagination/pagination.stories.d.ts +0 -1
- package/dist/components/react-component/reactComponent.component.d.ts +3 -3
- package/dist/components/select/select.stories.d.ts +2 -3
- package/dist/components/submissions-table/submissionsTable.component.d.ts +0 -1
- package/dist/components/submissions-table/submissionsTable.stories.d.ts +13 -14
- package/dist/components/table/components/defaultArrowSort.component.d.ts +0 -1
- package/dist/components/table/components/defaultCell.component.d.ts +0 -1
- package/dist/components/table/components/defaultCellHeader.component.d.ts +0 -1
- package/dist/components/table/components/defaultCellOperations.component.d.ts +0 -1
- package/dist/components/table/components/defaultOperationButton.component.d.ts +0 -1
- package/dist/components/table/filters/defaultColumnFilter.component.d.ts +0 -1
- package/dist/components/table/filters/selectColumnFilter.component.d.ts +0 -1
- package/dist/components/table/filters/sliderColumnFilter.component.d.ts +0 -1
- package/dist/components/table/table.stories.d.ts +4 -5
- package/dist/components/tabs/tabs.component.stories.d.ts +0 -1
- package/dist/hooks/useTooltip.d.ts +1 -1
- package/dist/index.js +342 -274
- package/dist/index.js.map +1 -1
- package/dist/index.modern.js +313 -268
- package/dist/index.modern.js.map +1 -1
- package/dist/stores/auth/auth.selectors.d.ts +1 -1
- package/jest.config.js +1 -1
- package/package.json +5 -5
- package/readme.md +51 -18
- package/src/components/__fixtures__/form.fixture.json +23 -0
- package/src/components/form/form.component.spec.tsx +56 -0
- package/src/components/form/form.component.tsx +5 -3
- package/src/components/form/form.stories.tsx +182 -11
- package/src/components/form/useForm.hook.ts +54 -29
- package/src/components/form-access/formAccess.component.tsx +1 -1
- package/src/components/form-access/formAccess.utils.ts +13 -13
- package/src/components/form-action/formAction.component.tsx +1 -1
- package/src/components/form-builder/formBuilder.component.tsx +1 -1
- package/src/components/form-edit/formCtas.component.tsx +32 -30
- package/src/components/form-edit/formEdit.component.tsx +1 -1
- package/src/components/form-settings/formSettings.utils.ts +3 -3
- package/src/components/input-tags/inputTags.component.tsx +3 -3
- package/src/components/input-text/inputText.component.spec.tsx +1 -1
- package/src/components/input-text/inputText.component.tsx +3 -3
- package/src/components/modal/modal.component.tsx +2 -2
- package/src/components/react-component/reactComponent.component.tsx +9 -6
- package/src/components/select/select.component.tsx +2 -2
- package/src/components/submissions-table/submissionsTable.component.tsx +6 -6
- package/src/components/table/table.component.tsx +58 -44
- package/src/components/table/utils/mapFormToColumns.tsx +1 -1
- package/src/components/tabs/tabs.component.tsx +1 -1
- package/src/hooks/useTooltip.ts +1 -1
- package/src/stores/action-info/action-info.selectors.ts +1 -1
- package/src/stores/auth/auth.utils.tsx +2 -2
- package/src/stores/auth/getAccess.action.ts +2 -2
- package/src/stores/auth/logout.action.spec.ts +1 -0
- package/src/stores/form/form.selectors.ts +1 -1
- package/src/stores/root/root.selectors.ts +2 -2
- package/tsconfig.json +10 -27
- package/tsconfig.node.json +8 -0
- package/craco.config.js +0 -11
- 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")
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
30
|
-
"@tsed/tailwind-formio": "1.
|
|
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)
|
|
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
|
|
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
|
|
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
|
-
|
|
155
|
-
|
|
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](
|
|
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)>)
|
|
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
|
|
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
|
|
208
|
-
|
|
|
209
|
-
|
|
|
210
|
-
| `
|
|
211
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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={
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
2
|
-
import {
|
|
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 {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
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
|
-
|
|
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 {
|
|
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
|
|
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
|
|
121
|
+
const eventName = `on${event.charAt(7).toUpperCase()}${event.slice(8)}`;
|
|
79
122
|
|
|
80
|
-
if (
|
|
123
|
+
if (eventName === "onChange") {
|
|
81
124
|
if (isEqual(get(submission, "data"), args[0].data)) {
|
|
82
125
|
return;
|
|
83
126
|
}
|
|
84
127
|
}
|
|
85
128
|
|
|
86
|
-
|
|
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
|
}
|