hubspot-form

0.0.43 • Public • Published

OWM hubspot form config

An internal form library designed to integrate into our hubspot React Extension UI projects. This package simplifies nested form creation by using a config-driven approach, making it easy to manage and validate forms by scope.

To Install the package, run:

npm install @hubspot-form

Understanding Form-config concepts

  // formState is a dictionary
  const formState = {
    // user_info is a scope on formState dictionary
    user_info: {
      fields: {
        first_name: createField({
          name: first_name,
          type: text
        }),
        // array field
        addresses: [
          {
            fields: {
              type: createField({
                name: type,
                type: select,
                options: [
                  { label: "Permanent", value: "Permanent" },
                  { label: "Mailing", value: "Mailing" }
                ]
              }),
              street: createField({
                name: street,
                type: text,
              }),
              unit_n: createField({
                name: unit_n,
                type: text,
                required: false,
              })
            },
            // nested scope validation
            scope_validation: {
              isValid: false,
            }
          }
        ],
      },
      // user_info scope validation
      scope_validation: {
        isValid: false,
      }
    }
  }

  // UI usage
  <Scope name="user_info">
    <CustomInput name="first_name">
      {formState.user_info.fields.addresses.map((address, index) => (
        <>
          <Scope name="fields.addresses" index={index}>
            <CustomInput name="type">
            <CustomInput name="street">
            <CustomInput name="unit_n">
          </Scope>
          <Button onClick={() => {
            // remove address item
            handleDeleteItem(
              {
                scope: "user_info.fields.",
                name: "addresses",
                index
              }
            )
          }}>
            Remove Address
          </Button>
        </>
      ))}

      <Button onClick={() => {
        handleNewItem(
          {
            scope: "user_info.fields",
            name: "addresses",
            data: {}
          }
        )
      }}>
        Add new Address
      </Button>
  </Scope>

  <Button
    isDisabled={!isValidScope(formState, "user_info")}
    onClick={() => {
      const { data } = handleSubmit()
      // { first_name: "John Doe", addresses: [] }
  }}>
    Add new Address
  </Button>

Understanding FormConfig features

// feature functions:
import {
  createField,
  handleNewItem,
  handleUpdateField,
  handleResetScope,
  handleDeleteItem,
  handleSubmit,
  isScopeValid,
} from '@owm-hubspot-form';

// create fields fn is necessary to create a new
// this function will infer FormField type.
first_name: createField({
  name: "first_name",
  type: "text",
  label: "First Name",
  defaultValue: contact?.first_name,
  validate: (value) => {},
  visibility: (state, scope, parent) => {},
  rules: {
    minDigits: 0,
    maxDigits: 0,
    isEmai: true
  },
}),

// handleNewItem fn will add a item in one array field:
// on this function we are adding a new task on scope "board",
// field > in-progress which is an array of tasks
handleNewItem({
  scope: "board",
  name: "in-progress",
  data: {
    task: "new task",
    priority: "2",
    id: new Date().toString()
  }
})


// handleUpdateField is used to update field properties dynamically
// in this example we are updating last_name on scope user_info
// to be dynamically required.
handleUpdateField({
  scope: "user_info",
  name: "last_name",
  data: {
    ...user_info.fields.middle_name,
    required: true,
  }
})

// Fn is required to reset the fields values on that particular scope
handleResetScope({
  scope: "user_info",
});


// handleSubmit fn will return all your scope values with:
// data: {properties: { first_name: "value" }}
const response = handleSubmit()


// handleInputChange fn will return all your scope values with:
// data: {properties: { first_name: "value" }}
handleInputChange({
  scope:, // string
  name, // string
  value, // fieldValues
});

Creating first Panel Steps

import React, { useEffect, useState } from "react";
import {
  FormProvider,
  createField,
  type FormState,
  handleSubmit
} from '@owm-hubspot-form';

const MOCKED_USER_DATA = {
  first_name: "John"
  middle_name: null,
  last_name: "Doe",
  banks: [
    {
      id: new Date(),
      institution: "",
      account: "",
      transit_n: ""
      agenc_n: "",
    }
  ]:
}

// Create your first scope config "user_info" to support your mocked data

const getUserInfoFields = (contact: typeof MOCKED_USER_DATA) => {
  return {
    fields: {
      // normal field
      first_name: createField({
        name: "first_name",
        type: "text",
        defaultValue: contact?.first_name
      }),
      // not required field
      middle_name: createField({
        name: "middle_name",
        type: "text",
        required: false,
        defaultValue: contact?.middle_name
      }),
      // visibility field by "scope"
      last_name: createField({
        name: "last_name",
        type: "text",
        required: false,
        defaultValue: contact?.last_name
        visibility: (state, scope) => {
          // state is your whole form config obj
          // scope is your current scope fields "user_info"
          return scope.fields.first_name.value
        }
      }),
      // array field on scope
      // here you could loop your banks data and returns the array of items
      banks: [
        {
          fields: {
            institution: createField({
              name: "institution",
              type: "text",
            }),
            account: createField({
              name: "account",
              type: "text",
            })
            transit_n: createField({
              name: "transit_n",
              type: "text",
              // example of validation
              validate: (value => {
                if(value && value?.length >= 6 ) {
                  return "Value should have max: 6 digits"
                }
              })
            })
            agency_n: createField({
              name: "agency_n",
              type: "text",
              validate: (value => {
                if(value && value?.length >= 4) {
                  return "Value should have max: 4 digits"
                }
              }),
              // rules feature is still in dev mode, for now it does not validate the field, you should use validate fn above
              rules: {
                minDigits: 1,
                maxDigits: 5,
                isEmail: true,
              }
            })
          },

          // Scope validation means that:
          // If all Field has value, is visible or it's not required
          // we validate the scope.
          scope_validation: {
            isValid: false,
          }
        }
      ],

      // This scope relies on "banks" to be validated as banks is part of the scope
      scope_validation: {
        isValid: false,
      }
    }
  }
}

// configure your scopes types like this:
type FormConfig= {
  user_info = ReturnType<typeof getUserInfoFields>;
  // you can have more scope as needed
}

// Now, let's display make use of the form on the page.

const MyPanel = () => {
  const [formState, setFormState] = useState<FormState<FormConfig>>();

   const initFormState = async () => {
    // await contact fetching
    const response = MOCKED_USER_DATA

    // set USER_INFO scope to the state, with pre-filled fields based on response
    setFormState({
      user_info: getUserInfoFields(response)
    });
  };

  useEffect(() => {
    initFormState()
  }, [])

  return(
     <FormProvider<FormConfig> initialConfig={formState}>
        <FormContext.Consumer>
          {(formContext: IFormContextProps<FormConfig>) => {
          const { formState } = formContext;
          return (
            <>
              <Scope name="user_info" index={0}>
                <CustomInput name="first_name" />
                <CustomInput name="middle_name" />
                <CustomInput name="last_name" />
                // here you can loop and pass the index on scope
                <Scope name="fields.banks" index={0}>
                  <CustomInput name="institution" />
                  <CustomInput name="account" />
                  <CustomInput name="transit_n" />
                  <CustomInput name="agency_n" />
                </Scope>
              </Scope>

              <Button type="button" isDisabled={!isFormScopeValid(formState,['user_info'])} onClick={async () => {
                  // handleSubmit will return:
                  // data: { properties: [field]: value }
                  const formData = handleSubmit();
                  await submit(formData)
                }}
              >
              Submit
              </Button>
            </>
          )}}
        </FormContext.Consumer>
     </FormProvider>
  );
};

export default MyPanel;

Dependencies (2)

Dev Dependencies (13)

Package Sidebar

Install

npm i hubspot-form

Weekly Downloads

889

Version

0.0.43

License

ISC

Unpacked Size

72.2 kB

Total Files

49

Last publish

Collaborators

  • optimize-luciano
  • optimize-arthur