Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/Visma-Consulting/formula
https://github.com/Visma-Consulting/formula
Last synced: 4 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/Visma-Consulting/formula
- Owner: Visma-Consulting
- Created: 2021-04-26T12:28:10.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2024-05-06T13:31:32.000Z (7 months ago)
- Last Synced: 2024-05-07T15:05:31.573Z (7 months ago)
- Language: JavaScript
- Size: 113 MB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
Awesome Lists containing this project
README
# @visma/formula 🏎
React component for configurable forms. Optionally connect to the backend to fetch external config and submit form data to.
## Requirements
1. Material UI v4, MUI v5 and `react-intl` are required. Install and set up if necessary:
```sh
npm i @visma/formula @emotion/styled @emotion/react @mui/x-date-pickers @mui/base @mui/material @material-ui/core @material-ui/styles @material-ui/icons @material-ui/lab react-intl --legacy-peer-deps
```2. Add Vite / Webpack alias for `@emotion/core`:
```js
// vite.config.js
//...
export default defineConfig({
resolve: {
alias: {
'@emotion/core': '@emotion/react',
},
},
});// webpack.config.js
module.exports = {
//...
resolve: {
alias: {
'@emotion/core': '@emotion/react',
},
},
};
```
```## Examples
### Login form
```js
import Formula from '@visma/formula';console.log(values)}
/>;
```### Use external config, prefill some fields
```js
import Formula from '@visma/formula';{
axios.defaults.baseURL = 'https://example.com/formula/api';
axios.defaults.headers.common.Authorization = 'Bearer ';
}}
id="1"
// Assuming form has at least a formGroup with key `customer`, containing
// fields with keys `firstName` & `lastName`.
formData={useMemo(
() => ({
customer: {
firstName: user.firstName,
lastName: user.lastName,
},
}),
[user]
)}
/>;
```## Components
### ``
#### Props
One of `config`, `id` or `dataId` is required. Rest are optional.
| Name | Type | Description |
|---------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `config` | [Form](https://visma-consulting.github.io/formula/docs/interfaces/Form.html) | Form config |
| `formData` | `any` | Optional, prefilled form data. Ensure the reference does not change undesirably, e.g. using `useMemo`. |
| `id` | `string` | External form config id |
| `dataId` | `string` | Resume editing |
| `onPreSubmit` | `async (args: Args, event: SubmitEvent) => void \ | boolean \ | [Args, SubmitEvent]` | Run code before submit. Falsy return value prevents the submit. Return `true` or modified args to submit. |
| `onSubmit` | `({ values }) => void` | Override default submit handler |
| `onPostSubmit` | `(dataId, { values }) => void` | Get `dataId` of submitted form data |
| `confirm` | `boolean \ | { title: ReactElement, description: ReactElement }` | Show confirm dialog or use object for other messages. Default: `true` |
| `axios` | `axios => void` | Get access to API client's axios instance e.g. to set defaults |
| `dateFnsLocale` | `Locale` from `date-fns` | Examples:
`import useDateFnsLocale from '@visma/react-app-locale-utils/lib/useDateFnsLocale.js';`
`import { fi } from 'date-fns/locale';` |
| `children` | `ReactElement` | Override default submit button. Set `<>>` (empty React Frament) to render nothing. |
| `review` | `boolean` | Show review after the form has been submitted. Default: `true` |
| `forceReview` | `boolean` | Show review directly. Default: `false` |
| `reviewProps` | `{ actions: ReactNode, showSuccessText: boolean, highlightSuccessText: boolean, hideNotAnswered: boolean }` | actions: Additional action buttons
showSuccessText: show success text and summary in review, default `true`
highlightSuccessText: make summary more noticeable, default `false`
hideNotAnswered: hide not answered fields in review, user can see the full form by unchecking a checkbox, default `false` |
| `fillProps` | `{ actions: ReactNode, disableSteps: boolean, disableResetFormdata: boolean, disableElementButtons: boolean, showScores: boolean, disablePrint: boolean }` | actions: Additional action buttons
disableSteps: disables steps when filling the form, default `false`
disableResetFormdata: disable resetting formData to initial formData in disabled fields, default `false`
disableElementButtons: disable all button elements, default `false`
showScores: show scores if required metadata is available, default `false`
disablePrint: disable print button in ConfirmDialog, default `false` |
| `confirmComponent`, `previewField`, `reviewField` | `component` | [Customize](#customize) |
| `customMessages` | `{ submit: string, reviewSubmitConfirmation: string, confirmDialogTitle: string, confirmDialogConsent: string, confirmDialogPreview: string, confirmDialogSendButton: string, confirmDialogCancelButton: string, error: string }` | Overrides default texts in submit button, confirmation dialog, confirm message and error. |
| `buttonActions` | `object` | Functions for button elements, `{functionKey: (buttonActionProps) => boolean}` |### ``
Provide options for any `` component in children.
Required to use API hooks.
#### Props
Same as for ``, except:
- Without `config`, `id`, `dataId`
- `children: ReactElement`: App, wrapped forms### ``
#### Props
`config`, `id`, `dataId` and `children` from ``
## Hooks
See [src/api.js](src/api.js) for all API hooks.
### List forms
```js
import { useForms } from '@visma/formula';function ListForms() {
const forms = useForms({ status: 'published', visibility: 'public' });
// ...
}
```### Form config details
```js
import { useForm } from '@visma/formula';function FormTitle({ id }) {
const form = useForm(id);return
{form.title}
;
}
```### Intercept built-in submit function
```js
import { Formula, useMutations } from '@visma/formula';
// ...
const { submit } = useMutations();{
try {
return await submit(...args);
} catch (error) {
logger(error);
throw error;
}
}}
// ...
/>;
```## Customize
### Confirm dialog (`confirmComponent`)
Example:
```js
import {
DialogActions,
DialogContent,
DialogContentText,
} from '@material-ui/core';
import produce, { original } from 'immer';
import { FormattedMessage, useIntl } from 'react-intl';export function CustomConfirm({ config, formData, children }) {
const intl = useIntl();// children, the original dialog, is readonly – use produce from immer to make deep immutable changes.
return produce(children, (children) => {
const dialogContentElement = children.props.children.find(
(element) => element && original(element)?.type === DialogContent
);if (config.meta?.showScoreOnPreview && dialogContentElement) {
dialogContentElement.props.children.splice(
2,
0,
);
}// Reverse dialog children order 🤪
children.props.children.reverse();// Reverse dialog action button order
children.props.children
.find((element) => element && original(element)?.type === DialogActions)
?.props.children.reverse();// If set, override consent message
const consentElement = dialogContentElement?.props.children.find(
(element) => element?.key === 'consent'
);
if (consentElement) {
consentElement.props.label = intl.formatMessage({
defaultMessage:
'Kyllä, haluan lähettää tiedot ja osallistua palkinnon arvontaan 🏆',
});
}
});
}
```### Preview (`previewField`) & Review Field (`reviewField`)
Example:
```js
import produce from 'immer';
import { sortBy } from 'lodash';export function CustomPreviewField({ formData, uiSchema, children }) {
const dataElement = children[1];// children, the original field, is readonly – use produce from immer to make deep immutable changes.
return produce(children, (children) => {
if (uiSchema['ui:options'].element.meta.showScoreOnPreview) {
const highlight = sortBy(
uiSchema['ui:options'].element.meta.highlightColors,
['scoreGreaterThan']
)
.reverse()
.find(({ scoreGreaterThan }) => scoreGreaterThan < formData);if (highlight) {
children[1] = (
{dataElement}
);
}
}
});
}
```## Load library dynamically
1. Call `init` from `@visma/formula/lib/dll` before using the API:
```js
import { init } from '@visma/formula/lib/dll';
import App from 'components/App';
import React from 'react';
import ReactDOM from 'react-dom';async function main() {
await init('https://example.com/formula');ReactDOM.render(
,
document.getElementById('root')
);
}main();
```2. Import the API from `@visma/formula/lib/dll`. Note that all components and hooks are available only using the default export:
```js
import DLL from '@visma/formula/lib/dll';{
axios.defaults.baseURL = 'https://example.com/formula/api';
axios.defaults.headers.common.Authorization = 'Bearer ';
}}
id="1"
/>;
```
EXAMPLES
App wrapped with FormulaProvider```js
async function main() {
await init('https://example.com/formula');
ReactDOM.render(
{
axios.defaults.baseURL = 'https://example.com/formula';
}}
>
, document.getElementById('root'));
}
main();
```
After wrapping App with the Provider, Formula component can be used anywhere inside the AppFormulaComponent
```js
import {IntlProvider} from "react-intl";
import DLL from "@visma/formula/lib/dll";
import React from "react";
const FormulaComponent = (props) => {
return (
);
}
export default FormulaComponent;
```
Using FormulaComponent inside App
```js
...
...
```