Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@visma/formula

Package Overview
Dependencies
Maintainers
0
Versions
263
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@visma/formula

React component for configurable forms. Optionally connect to the backend to fetch external config and submit form data to.

  • 0.4.262
  • latest
  • Source
  • npm
  • Socket score

Version published
Maintainers
0
Created
Source

@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:
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
  1. Add Vite / Webpack alias for @emotion/core:
// 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';

<Formula
  config={{
    title: 'Log In',
    elements: [
      {
        key: 'email',
        type: 'email',
        name: 'Email Address',
        required: true,
      },
      {
        key: 'password',
        type: 'password',
        name: 'Password',
        required: true,
      },
    ],
  }}
  onSubmit={({ values }) => console.log(values)}
/>;

Use external config, prefill some fields

import Formula from '@visma/formula';

<Formula
  axios={(axios) => {
    axios.defaults.baseURL = 'https://example.com/formula/api';
    axios.defaults.headers.common.Authorization = 'Bearer <token>';
  }}
  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

<Formula>

Props

One of config, id or dataId is required. Rest are optional.

NameTypeDescription
configFormForm config
formDataanyOptional, prefilled form data. Ensure the reference does not change undesirably, e.g. using useMemo.
idstringExternal form config id
dataIdstringResume editing
onPreSubmit`async (args: Args, event: SubmitEvent) => void \boolean \
onSubmit({ values }) => voidOverride default submit handler
onPostSubmit(dataId, { values }) => voidGet dataId of submitted form data
confirm`boolean \{ title: ReactElement, description: ReactElement }`
axiosaxios => voidGet access to API client's axios instance e.g. to set defaults
dateFnsLocaleLocale from date-fnsExamples:
import useDateFnsLocale from '@visma/react-app-locale-utils/lib/useDateFnsLocale.js';
import { fi } from 'date-fns/locale';
childrenReactElementOverride default submit button. Set <></> (empty React Frament) to render nothing.
reviewbooleanShow review after the form has been submitted. Default: true
forceReviewbooleanShow 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, reviewFieldcomponentCustomize
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.
buttonActionsobjectFunctions for button elements, {functionKey: (buttonActionProps) => boolean}

<FormulaProvider>

Provide options for any <Form> component in children.

Required to use API hooks.

Props

Same as for <Formula>, except:

  • Without config, id, dataId
  • children: ReactElement: App, wrapped forms

<Form>

Props

config, id, dataId and children from <Formula>

Hooks

See src/api.js for all API hooks.

List forms

import { useForms } from '@visma/formula';

function ListForms() {
  const forms = useForms({ status: 'published', visibility: 'public' });
  // ...
}

Form config details

import { useForm } from '@visma/formula';

function FormTitle({ id }) {
  const form = useForm(id);

  return <h1>{form.title}</h1>;
}

Intercept built-in submit function

import { Formula, useMutations } from '@visma/formula';
// ...
const { submit } = useMutations();

<Formula
  onSubmit={async (...args) => {
    try {
      return await submit(...args);
    } catch (error) {
      logger(error);
      throw error;
    }
  }}
  // ...
/>;

Customize

Confirm dialog (confirmComponent)

Example:

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,
        <DialogContentText>
          <FormattedMessage
            defaultMessage="Vastauksesi antavat sinulle {score} pistettä."
            values={{
              score: Math.ceil(Math.random() * config.meta.maxScore),
            }}
          />
        </DialogContentText>
      );
    }

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

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] = (
          <div style={{ display: 'flex' }}>
            <div style={{ flex: '1 1' }}>{dataElement}</div>
            <div
              style={{
                height: '1.2rem',
                width: '1.2rem',
                color: highlight.color,
              }}
            >
              <svg
                xmlns="http://www.w3.org/2000/svg"
                className="h-6 w-6"
                fill="none"
                viewBox="0 0 24 24"
                stroke="currentColor"
                strokeWidth={2}
              >
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
                />
              </svg>
            </div>
          </div>
        );
      }
    }
  });
}

Load library dynamically

  1. Call init from @visma/formula/lib/dll before using the API:

    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(
        <React.StrictMode>
          <App />
        </React.StrictMode>,
        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:

    import DLL from '@visma/formula/lib/dll';
    
    <DLL.Formula
      axios={(axios) => {
        axios.defaults.baseURL = 'https://example.com/formula/api';
        axios.defaults.headers.common.Authorization = 'Bearer <token>';
      }}
      id="1"
    />;
    

EXAMPLES
App wrapped with FormulaProvider
 async function main() {
  await init('https://example.com/formula');


  ReactDOM.render(
    <DLL.FormulaProvider
    axios={(axios) => {
      axios.defaults.baseURL = 'https://example.com/formula';
    }}
  >
    <App />
  </DLL.FormulaProvider>
    , document.getElementById('root'));
}
main();



After wrapping App with the Provider, Formula component can be used anywhere inside the App

FormulaComponent

   import {IntlProvider} from "react-intl";
   import DLL from "@visma/formula/lib/dll";
   import React from "react";
   
   const FormulaComponent = (props) => {
     return (
       <IntlProvider locale={'fi-FI'}>
         <DLL.Form
           id={props?.formId}
           dataId={props?.formResponseId}
           credentials={props?.credentials}
           ...
         >
         </DLL.Form>
       </IntlProvider>
     );
   }
   
   export default FormulaComponent;

Using FormulaComponent inside App
   ...
   <FormulaComponent
     formId={formId}
     formResponseId={formResponseId}
     credentials={formulaToken}
     ...
   />
   ...

FAQs

Package last updated on 26 Nov 2024

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc