Skip to content

Commit

Permalink
feat(react-formio): add onAsyncSubmit to bind server error message to…
Browse files Browse the repository at this point in the history
… a form control
  • Loading branch information
Romakita committed Jul 7, 2022
1 parent 036092c commit 5d72652
Show file tree
Hide file tree
Showing 13 changed files with 1,593 additions and 1,037 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ jobs:
CI: true
GH_TOKEN: ${{ secrets.GH_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
run: yarn release
- uses: actions/upload-artifact@v2
with:
Expand Down
20 changes: 20 additions & 0 deletions .github/workflows/close-issue-message.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Closed Issue Message
on:
issues:
types: [closed]
jobs:
auto_comment:
runs-on: ubuntu-latest
steps:
- uses: aws-actions/closed-issue-message@v1
with:
# These inputs are both required
repo-token: "${{ secrets.GITHUB_TOKEN }}"
message: |
## 🎉 Are you happy?
If you appreciated the support, know that it is **free** and is carried out on **personal time** ;)
A support, even a little bit **makes a difference** for me and **continues to bring you answers**!
[![github](https://img.shields.io/static/v1?label=Github%20sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86)](https://github.com/sponsors/romakita) [![opencollective](https://img.shields.io/static/v1?label=OpenCollective%20sponsor&message=%E2%9D%A4&logo=OpenCollective&color=%23fe8e86)](https://opencollective.com/tsed)
50 changes: 50 additions & 0 deletions .github/workflows/code-ql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: "Code scanning - action"

on:
push:
paths-ignore:
- "**.md"
- "docs"
pull_request:
paths-ignore:
- "**.md"
- "docs"
schedule:
- cron: "0 14 * * 4"

jobs:
CodeQL-Build:
runs-on: ubuntu-latest
permissions:
# required for all workflows
security-events: write

steps:
- name: Checkout repository
uses: actions/checkout@v3

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
# Override language selection by uncommenting this and choosing your languages
# with:
# languages: go, javascript, csharp, python, cpp, java

# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2

# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl

# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language

#- run: |
# make bootstrap
# make release

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
Binary file added docs/assets/form-submission-server-error.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@
"prettier": "^2.6.2",
"prettier-eslint": "^14.0.3",
"react-scripts": "4.0.3",
"semantic-release": "17.3.9",
"semantic-release": "19.0.3",
"semantic-release-slack-bot": "3.5.3",
"typescript": "^4.1.3",
"webpack": "4.44.2"
},
Expand Down
69 changes: 51 additions & 18 deletions packages/react-formio/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@

<hr />

A [React](http://facebook.github.io/react/) library for rendering out forms based on the [Form.io](https://www.form.io) platform.
A [React](http://facebook.github.io/react/) library for rendering out forms based on the [Form.io](https://www.form.io)
platform.

This module is based on the original [react-formio](https://github.com/formio/react-formio) and add extra features listed above.
This module is based on the original [react-formio](https://github.com/formio/react-formio) and add extra features
listed above.

See our [storybook](https://formio.tsed.io/) to see all available components.

Expand Down Expand Up @@ -83,7 +85,10 @@ export default App;

### Form

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.
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.

#### Props

Expand All @@ -101,6 +106,7 @@ You can respond to various events in the form. Simply pass in a prop with a func

| Name | Parameters | Description |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `onAsyncSubmit` | `submission`: object | When the submit button is pressed and the submission has started. Use this event if you submit data to external service. |
| `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. |
| `onSubmitDone` | `submission`: object | When the submission has successfully been made to the server. This will only fire if `src` is set. |
| `onChange` | `submission`: object, `submission.changed`: object of what changed, `submission.isValid`: boolean - if the submission passes validations. | A value in the submission has changed. |
Expand Down Expand Up @@ -151,21 +157,35 @@ const form = {
ReactDOM.render(
<Form<MyFormData>
form={form}
onSubmit={(submission) => {
console.log(submission);
onAsyncSubmit={(submission) => {
return httpClient.post("/path/to/external/service", { data: submission }).catch((er) => {
err.errors = [
{
message: "My custom message about this field",
type: "custom",
path: ["title"],
level: "error"
}
];
throw error;
});
}}
/>,
document.getElementById("example")
);
```

> See [Form with error from server](https://formio.tsed.io/?path=/story/reactformio-form--trigger-error)
### FormBuilder

The [FormBuilder](<[FormsTable](https://formio.tsed.io/?path=/story/reactformio-formbuilder--sandbox)>) class can be used
The [FormBuilder](https://formio.tsed.io/?path=/story/reactformio-formbuilder--sandbox)) class can be
used
to embed a form builder directly in your react application.
Please note that you'll need to include the CSS for the form builder from formio.js as well.

Please note that the [FormBuilder](<[FormsTable](https://formio.tsed.io/?path=/story/reactformio-formbuilder--sandbox)>) component
Please note that the [FormBuilder](<[FormsTable](https://formio.tsed.io/?path=/story/reactformio-formbuilder--sandbox)>)
component
does not load and save from/to a url. You must handle the form definition loading and saving yourself or use
the [FormEdit](https://formio.tsed.io/?path=/story/reactformio-formedit--sandbox) component.

Expand Down Expand Up @@ -200,15 +220,19 @@ ReactDOM.render(<FormBuilder form={{ display: "form" }} onChange={(schema) => co
### FormEdit

The [FormEdit](https://formio.tsed.io/?path=/story/reactformio-formedit--sandbox) component wraps
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.
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.

#### Props

| Name | Type | Default | Description |
| ---------- | ------ | ------------------- | ---------------------------------------------------- | --------------------------------------------------------------- |
| `form` | object | {display: 'form' \ | 'wizard'} | The form definition of the exiting form that is to be modified. |
| `options` | object | {} | The options to be passed to FormBuilder |
| `saveText` | string | '' | The string that will be displayed in the save-button |
| Name | Type | Default | Description |
| --------------------------------------------------------------- | ------ | ------------------- | ----------- | --------------------------------------------------------------- |
| --------------------------------------------------------------- |
| `form` | object | {display: 'form' \ | 'wizard'} | The form definition of the exiting form that is to be modified. |

|
| `options` | object | {} | The options to be passed to FormBuilder |
| `saveText` | string | '' | The string that will be displayed in the save-button |

#### Event Props

Expand All @@ -218,7 +242,8 @@ the [FormBuilder](<[FormsTable](https://formio.tsed.io/?path=/story/reactformio-

### FormsTable

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.
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.

#### Props

Expand Down Expand Up @@ -308,16 +333,24 @@ Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com

## Sponsors

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)]
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)]

## License

The MIT License (MIT)

Copyright (c) 2016 - 2021 Romain Lenzotti

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:
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:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
Software.

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.
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.
4 changes: 3 additions & 1 deletion packages/react-formio/src/components/form/form.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ Form.propTypes = {
noAlerts: PropTypes.bool,
i18n: PropTypes.any,
template: PropTypes.string,
saveDraft: PropTypes.bool
saveDraft: PropTypes.bool,
hooks: PropTypes.any
}),
onPrevPage: PropTypes.func,
onNextPage: PropTypes.func,
Expand All @@ -56,6 +57,7 @@ Form.propTypes = {
onCustomEvent: PropTypes.func,
onComponentChange: PropTypes.func,
onSubmit: PropTypes.func,
onAsyncSubmit: PropTypes.func,
onSubmitDone: PropTypes.func,
onFormLoad: PropTypes.func,
onError: PropTypes.func,
Expand Down
68 changes: 66 additions & 2 deletions packages/react-formio/src/components/form/form.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from "react";
import { Form } from "./form.component";
import {Form} from "./form.component";
import form from "../__fixtures__/form.fixture.json";
import {Submission} from "../../interfaces";

export default {
title: "ReactFormio/Form",
Expand All @@ -25,11 +26,74 @@ export const Sandbox = (args: any) => {
onFormReady={(formio) => {
console.log("ready", formio);
}}
options={{ template: "tailwind", iconset: "bx" }}
options={{template: "tailwind", iconset: "bx"}}
/>
);
};

Sandbox.args = {
form
};

export const TriggerError = (args: any) => {
delete args.onRender;
delete args.onComponentChange;

return (
<Form
{...args}
form={args.form}
onAsyncSubmit={(submission: Submission) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error("server error"));
}, 500);
}).catch((error) => {
error.errors = {
"message": "My custom message about this field",
"type": "custom",
"path": ["firstName"],
"level": "error"
}
throw error
});
}}
options={{
hooks: {
template: "tailwind",
iconset: "bx"
}
}}
/>
);
};

TriggerError.args = {
form: {
"type": "form",
"display": "form",
"tags": [],
"components": [
{
"label": "First name",
"widget": {
"type": "input"
},
"errorLabel": "",
"key": "firstName",
"inputType": "text",
"type": "textfield",
"id": "eqb1o4r",
"defaultValue": ""
},
{
"label": "Submit",
"showValidations": false,
"tableView": false,
"key": "submit",
"type": "button",
"input": true
}
]
}
};
26 changes: 25 additions & 1 deletion packages/react-formio/src/components/form/useForm.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export interface UseFormHookProps<Data = any> extends Record<string, any> {
onCustomEvent?: (obj: { type: string; event: string; component: ExtendedComponentSchema; data: any }) => void;
onComponentChange?: (component: ExtendedComponentSchema) => void;
onSubmit?: (submission: Submission<Data>) => void;
onAsyncSubmit?: (submission: Submission<Data>) => Promise<void>;
onSubmitDone?: (submission: Submission<Data>) => void;
onFormLoad?: Function;
onError?: (errors: any) => void;
Expand All @@ -61,12 +62,29 @@ export function useForm<Data = any>(props: UseFormHookProps<Data>) {
const instance = useRef<Form>();
const events = useRef<Map<string, any>>(new Map());

async function customValidation(submission: Submission, callback: (err: Error | null) => void) {
if (events.current.has("onAsyncSubmit")) {
try {
await events.current.get("onAsyncSubmit")(submission);
} catch (err) {
callback(err?.errors || err);
}
} else {
callback(null);
}
}

const createWebForm = (srcOrForm: any, options: any) => {
options = Object.assign({}, options);
srcOrForm = typeof srcOrForm === "string" ? srcOrForm : cloneDeep(srcOrForm);

if (!instance.current) {
isLoaded.current = false;
options.hooks = {
...(options.hooks || {}),
customValidation: options?.hooks?.customValidation || customValidation
};

instance.current = new Form(element.current, srcOrForm, options);

instance.current.onAny((event: string, ...args: any[]): void => {
Expand All @@ -92,7 +110,9 @@ export function useForm<Data = any>(props: UseFormHookProps<Data>) {
const fn = callLast(funcs[funcName], 100);
events.current.set(funcName, fn);
}
events.current.get(funcName)(...args);

instance.current.instance.setAlert("success", "");
events.current.get(funcName)(...args, instance.current);
}
}
});
Expand Down Expand Up @@ -156,6 +176,10 @@ export function useForm<Data = any>(props: UseFormHookProps<Data>) {
props.onSubmit && events.current.set("onSubmit", props.onSubmit);
}, [props.onSubmit, events]);

useEffect(() => {
props.onAsyncSubmit && events.current.set("onAsyncSubmit", props.onAsyncSubmit);
}, [props.onAsyncSubmit, events]);

useEffect(() => {
props.onSubmitDone && events.current.set("onSubmitDone", props.onSubmitDone);
}, [props.onSubmitDone, events]);
Expand Down
Loading

0 comments on commit 5d72652

Please sign in to comment.