2.8.2 ā€¢ Published 1 year ago

@licenserocks/mega-flow v2.8.2

Weekly downloads
33
License
ISC
Repository
github
Last release
1 year ago

What is MegaFlow? šŸš€

MegaFlow is a React component dedicated to build workflows based on JSON schemas, powered by React Hook Forms.

As there are multiple workflows to generate in the company, which all of them are custom, also can be modified, we decided to have a component which will be used across all of our projects, so that it is possible to keep consistency along them.

Main libraries/technologies used in MegaFlow: šŸ¦¾

Below you can find main libraries/technologies which we have used in Rockskit. All other libraries are used by basing on design needs. Moreover, there is an example (story) which shows how this component works, based on provided schema in Storybook.

  • Storybook šŸ‘‰ Shortly, it is a development playground. We use it as an environment for components development. Also we build and deploy it to Github Pages as a showcase for the components we have.
  • Styled-Components šŸ‘‰ CSS-in-JS library which we use to make style neatly arranged through every component
  • Rocskit šŸ‘‰ Most of the components are built on top of Rockskit, it lets us save significant amount of time, since we do not need to write everything from scratch and also be consistent wth the design in our projects
  • React-Hook-Form šŸ‘‰ Form elements are implemented in a way to be compatible with RHF, as it's one of the best form libraries for React.

How to use MegaFlow? šŸŽ®

  1. Like you do for any other JS lib or package, just run yarn add @licenserocks/mega-flow
  2. Import the component <MegaFlow /> in your app and use that like in example below šŸ‘‡

āš ļø Remember: schema prop is required to be passed. This component working rule is based on parsing JSON file, so that schema has to be like in example *.json

import React from "react";
import schema from "./sample.json";

export const App = () => {
  return (
    <>
      <MegaFlow schema={schema} />
    </>
  );
};

Part of schema example šŸ‘‡

{
  "steps": [
    {
      "rows": [
        {
          "label": "Title",
          "fields": [
            {
              "name": "title",
              "required": "Name is required",
              "placeholder": "Name of your listed image"
            }
          ]
        }
      ],
      "title": "License Metrics"
    }
  ]
}

šŸ“ Good practce: Visit a storybook for this project, and check which props you can pass to imported component šŸ‘‡ https://licenserocks.github.io/mega-flow

Props šŸ‘Øā€šŸ”§

  • defaultValues šŸ‘‰ values rendered as default in provided schema
  • icons šŸ‘‰ additional icons which are included in form
  • schema šŸ‘‰ json file, the core of a form
  • onFinish šŸ‘‰ function executed at the end of a form
  • onStepSubmit šŸ‘‰ function executed on concrete step
  • renderActionButtons šŸ‘‰ additional buttons passed to the end of a form
  • theme šŸ‘‰ theme variables used in styling part
  • watcher šŸ‘‰ function which is executed to show declared variables in inspect mode
  • watchList šŸ‘‰ array of string variables shown in inspect mode
  • wizardProps šŸ‘‰ props passed directly to Wizard component
  • wrapperProps šŸ‘‰ props passed directly to Wrapper component

How MegaFlow works āœļø

There is a sample schema file in src folder which is named sample.json and it is used in the Storybook for testing and developing purposes. You can also refer to it to see how the schema works in different situations.

MegaFlow parses provided schema file and builds form basing on this data. Below is the most fundamental example of parsing JSON šŸ‘‡

const json = '{"result":true, "count":42}';
const obj = JSON.parse(json);

console.log(obj.count);
// expected output: 42

console.log(obj.result);
// expected output: true

Returning to MegaFlow parsing, this process is made by getting firstly, entire schema as parsedchema, then steps data is isolated from it, so that it is possible to render correctly complex forms with multiple steps.

// Parse if schema was type of JSON string
const parsedSchema = typeof schema === "string" ? JSON.parse(schema) : schema;
const { steps } = parsedSchema;

How MegaFlow displays form based on parsed schema?

Basing on {steps} generated from parsedSchema, imported Form component is included in function renderForm,

import { Form } from "./components";

const [wizardData, setWizardData] = useState({});
const stepFormData = wizardData[currentStep] || defaultValues;

const stepsArray = steps.map((st) => ({
  title: st.title,
}));

const renderForm = () => (
  <Form
    data={steps[currentStep]}
    key={currentStep}
    stepIndex={currentStep}
    stepFormData={stepFormData}
    defaultValues={defaultValues}
  />
);

which is passed further to Wizard component as currentStepContent. There was also created an array which contains steps titles, so that it can be easily displayed in Wizard

<Wizard
  currentStepContent={renderForm()}
  currentStepIndex={currentStep}
  renderActionButtons={() => renderActionButtons(getOutputData(wizardData))}
  setCurrentStepIndex={setCurrentStep}
  steps={stepsArray}
  {...wizardProps}
  {...props}
/>

Imported Form component is build mostly on Rockskit library, it seperates from passed data concrete rows and renders it in similar way as renderForm function.

To collect data from current step, MegaFlow uses wizardData variable which is passed to Wizard component. This component is imported from rockskit.

const [currentStep, setCurrentStep] = useState(0);
const isCurrentLastStep = currentStep === steps.length - 1;
const [wizardData, setWizardData] = useState({});
const stepFormData = wizardData[currentStep] || defaultValues;

currentStep is set to indicate first step, to start proceeding form at the beginning stepFormata is getting values from wizardData at concrete step or passed defaultValues

const getOutputData = (output) =>
  Object.values(output).reduce((obj, acc) => ({ ...obj, ...acc }), {});

const onSubmit = (data) => {
  const currentState = {
    ...wizardData,
    [currentStep]: data,
  };

  // Set step data in global wizard object
  setWizardData(currentState);

  if (!isCurrentLastStep) {
    setCurrentStep((prev) => prev + 1);
  } else {
    onFinish(getOutputData(currentState));
  }
};

Submitting is executed by a function onSubmit, this function firstly, declares constant variable currentState which contains previously passed wizardData and data collected from current step. Then wizardData is updated end form displays next step or executes onFinish function.

How to create a correct schema? šŸ™‹ā€ā™‚ļø

As it can be seen in How it works part, schema should be divided into steps and then rows. It is the way how it is rendered and displayed, depending on the content of each row. MegaFlow using Wizard component shows each row content.

Let's look deeper on example json schema.

{
  "steps": [
    {
      "rows": [
        {
          "hint": "Supported file types: image/* (jpg, png, jpeg) - Max size: 2MB",
          "label": "Cover",
          "fields": [
            {
              "name": "cover",
              "type": "fileUpload",
              "accept": "image/*",
              "multiple": false,
              "required": "You must upload image cover"
            }
          ],
          "labelAlign": "start",
          "labelGutter": true
        }
      ],
      "title": "Media Assets"
    },
    {
      "rows": [
        {
          "label": "Title",
          "fields": [
            {
              "name": "title",
              "required": "Name is required",
              "placeholder": "Name of your listed image"
            }
          ]
        }
      ],
      "title": "License Metrics"
    }
  ]
}

To understand how rows are rendered into exact form, it is neccessary to dive into Wizard component, which is imported from Rockskit package šŸ‘‰ https://github.com/LicenseRocks/rockskit and /Form folder in MegaFlow

Displayed content in Wizard is declared as a component WizardStepContent, it is shown below šŸ‘‡

const content = (
  <WizardStepContent
    content={steps[currentStepIndex]?.content || currentStepContent}
    //other props
  />
);

Like it is coded, content is declared as an array of steps content attribute or currentStepContent passed before as a function renderForm()

Declared above content as WizardStepContent is executed in the place which depends on orientation prop, passed to Wizard as wizardProps. By default it has a value horizontal

If the place of rendered form is known (how it depends of orientation prop), let's jump into Form component šŸ‘‡

const Form = ({ data, defaultValues, stepIndex, stepFormData }) => {


  const renderRows = (index) => (
    <FormRows
      data={data}
      index={index}
      isRecurring={isRecurring}
      rows={data.rows}
      stepIndex={stepIndex}
      stepData={stepFormData}
    />
  );

//further part of a file

Data converted into a form is passed by props stepFormData and data into renderRows function, mainly into FormRows component.

FormRows component renders the data, all rows are mapped and fields are passed to FormField component and other variables such as hint, label displayed to the user as a custom component imported from Rockskit.

To see how fields are generated, it is crucial to see which fieldType prop should be passed in schema. It is perfectly and understandable represented in mapFieldTypeToComponent variable.

const mapFieldTypeToComponent = (fieldType) => {
  switch (fieldType) {
    case "datepicker":
      return FormDatepicker;
    case "select":
      return Select;
    case "borderedRadio":
      return BorderedRadio;
    case "checkbox":
      return Checkbox;
    case "radio":
      return Radio;
    case "toggleSwitch":
      return ToggleSwitch;
    case "fileUpload":
      return FileUpload;
    case "filePond":
      return FilePond;
    case "price":
      return PriceField;
    case "reactSelect":
      return ReactSelect;
    case "stepper":
      return Stepper;
    case "textArea":
      return TextArea;
    default:
      return Input;
  }
};

Each represented fieldType generates proper component from rockskit and is displayed to the user šŸ˜

To sum up: If you are wondered how to create correct json schema, divide it into steps, then rows and follow Form and Field files to pass proper values šŸ˜‰

How to go through process of builiding and packaging MegaFlow? šŸ§‘šŸ»ā€šŸ’»

We use Rollup for this process. It is configured in a way to use Babel to transpile the code and export the package in two formats: ESModules and CommonJS.

Now imagine we want to update a code in one of the components:

  1. Update the component code and make sure everything is working, at least in Storybook. If needed, update the code of component story in stories.js file.
  2. Begin the building process by running yarn build and make sure, that build phase passes successfully and we have updated files in dist directory.
  3. Update the version of package either manually or using npm version (better to do it manually).
  4. Make sure you have an access to @licenserocks packages using your npm account, then run npm login to complete authentication process.
  5. Now run npm publish. Keep in mind that you should have configured your npm CLI before running this command. Then use npm login to login into your account. For more information about how to login to NPM, visit this link: https://docs.npmjs.com/logging-in-to-an-npm-enterprise-registry-from-the-command-line .
  6. Commit your changes and push to Github repository.

That's all!šŸš€

Running Locally šŸ”

To run Rockskit with full view through storybook on localhost, follow steps below šŸ‘‡

  1. Clone repo to your machine, by running git clone <remote url>
  2. Install dependencies by running yarn
  3. Run on your local by yarn start

You can start modifying MegaFlow now šŸ˜‰

Configuration āš™ļø

If it is super important to change some configurations for Babel or Rollup, it can be done carefully by editing rollup.config.js and babel.config.js files.

2.8.1

1 year ago

2.8.0

1 year ago

2.8.2

1 year ago

2.7.55

1 year ago

2.7.54

1 year ago

2.7.56

1 year ago

2.7.403

2 years ago

2.7.401

2 years ago

2.7.402

2 years ago

2.7.4

2 years ago

2.7.5

2 years ago

2.7.51

2 years ago

2.7.52

2 years ago

2.7.3

2 years ago

2.7.2

2 years ago

2.6.806

2 years ago

2.6.807

2 years ago

2.6.804

2 years ago

2.6.805

2 years ago

2.6.808

2 years ago

2.6.809

2 years ago

2.6.802

2 years ago

2.6.803

2 years ago

2.6.801

2 years ago

2.7.0

2 years ago

2.7.1

2 years ago

2.6.7

2 years ago

2.6.9

2 years ago

2.6.3

2 years ago

2.6.2

2 years ago

2.6.5

2 years ago

2.6.4

2 years ago

2.6.6

2 years ago

2.6.1

2 years ago

2.6.0

2 years ago

2.5.6

2 years ago

2.5.5

2 years ago

2.5.8

2 years ago

2.5.7

2 years ago

2.5.9

2 years ago

2.5.2

2 years ago

2.5.4

2 years ago

2.5.3

2 years ago

2.5.0

2 years ago

2.5.1

2 years ago

2.4.9

2 years ago

2.4.1

3 years ago

2.4.0

3 years ago

2.4.3

2 years ago

2.4.2

2 years ago

2.4.5

2 years ago

2.4.4

2 years ago

2.4.7

2 years ago

2.4.6

2 years ago

2.4.8

2 years ago

2.3.1

3 years ago

2.3.0

3 years ago

2.2.1

3 years ago

2.2.0

3 years ago

2.2.3

3 years ago

2.2.2

3 years ago

2.2.4

3 years ago

2.1.6

3 years ago

2.1.5

3 years ago

2.1.4

3 years ago

2.1.3

3 years ago

2.1.2

3 years ago

2.1.1

3 years ago

2.1.0

3 years ago

2.0.2

3 years ago

2.0.1

3 years ago

2.0.0

3 years ago

1.8.1

3 years ago

1.8.0

3 years ago

1.7.0

3 years ago

1.6.1

3 years ago

1.6.0

3 years ago

1.5.0

3 years ago

1.4.3

3 years ago

1.4.2

3 years ago

1.4.1

3 years ago

1.4.0

3 years ago

1.3.0

3 years ago

1.2.9

3 years ago

1.2.8

3 years ago

1.2.7

3 years ago

1.2.6

3 years ago

1.2.5

3 years ago

1.2.4

3 years ago

1.2.3

3 years ago

1.2.2

4 years ago

1.2.1

4 years ago

1.2.0

4 years ago

1.1.3

4 years ago

1.1.1

4 years ago

1.1.2

4 years ago

1.1.0

4 years ago

1.0.9

4 years ago

1.0.8

4 years ago

1.0.7

4 years ago

1.0.6

4 years ago

1.0.5

4 years ago

1.0.4

4 years ago

1.0.3

4 years ago

1.0.2

4 years ago

1.0.1

4 years ago

1.0.0

4 years ago