npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@visma/formula

v0.4.261

Published

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

Downloads

354

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

| Name | Type | Description | |---------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | config | Form | 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 buttonsshowSuccessText: show success text and summary in review, default truehighlightSuccessText: make summary more noticeable, default falsehideNotAnswered: 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 buttonsdisableSteps: disables steps when filling the form, default falsedisableResetFormdata: disable resetting formData to initial formData in disabled fields, default falsedisableElementButtons: disable all button elements, default falseshowScores: show scores if required metadata is available, default falsedisablePrint: disable print button in ConfirmDialog, default false | | confirmComponent, previewField, reviewField | component | 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} |

<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"
    />;
 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;
   ...
   <FormulaComponent
     formId={formId}
     formResponseId={formResponseId}
     credentials={formulaToken}
     ...
   />
   ...