Form Builder

Overview

Our DAOhaus apps utilize many forms, and we decided to build a Form Builder tool that can be used to scaffold React forms. Form Builder is designed to take in a form configuration and then leverages existing UI elements and other scaffolding to output a fully functional form component.

This library is highly composable and declarative, and utilizes form elements from our UI library. Form Builder pairs well with Tx Builder to reduce much of the complexity involved in creating froms from scratch. We utilize Form Builder and Tx Builder in our apps where users need to input data for a transaction. Since this a common use case when developing apps, especially web3 apps, we wanted to open source the libraries to allow other developers to benefit from our learnings.

Components

Form Builder leverages form components from our UI library. It's possible to add custom components to a Form Builder instance, but the default inputs and other elements commonly used in a form (such as title, subtitle, description) utilize our DAOhaus component designs.

Since Form Builder is an integrated library, the default input components include our learnings from buiding forms over the years. Here are some of the best practices utilized:

  • Inputs use forwardRef so that the reference to the element is maintained (when passing from React Hook Form) to the input
    • user events need these
  • After getting the input, it's styled with styled components
    • input.tsx with types at the top
    • input.styles
    • input.stories
  • These all go together into the folder for each form element
  • These then move into the wrapped input version, which wraps the input with the FieldWrapper which pulls in quite a lot from RHF (such as the helper text, required asterisks, etc)
  • We have a buildable generic that adds the fieldwrapper and primitivewrapper
  • info prop receives a string and renders a tooltip
  • hasRules is the validation -- every form lego can accept the rules for validation
    • receive RHF validation rules, but can also pass in custom values
  • CoreFieldLookup is for the FormBuilder and is the factory for all the supported inputs
    • New official DH inputs can be added to the CoreFieldLookup
      • This would pull it into FieldLego and ensure that the types match the values
      • data used can only be the props of the mapped component, which helps for typing the fields

When Building...

  • When building a component, need to consider if its a form element that takes in data and if so you'll want to wrap in the Buildable
    • Can look at the other wrapped components as examples
      • Get the useForm context and registers everything
  • Since folks may want just the field we create the wrapped and unwrapped versions
  • Composing elements example:
    • CSInput wraps WrappedInput and watches eerything that comes in to this input and creates helper text and slices things up via the setValueAs -- slices up things that are separated by commas and turns into an array of strings
      • Gets props from the WrappedInput and adds 1 thing
    • Can keep wrapping and layering in the functionality
  • Can folks overwrite rules if they don't want certain aspects?
    • We need consistent behavior, so it's tricky to overwrite -- the higher level rules take precedent so someone could add to the lower level rules or create their own wrapper (such as doing a slash separated input instead of a comma separated one)
  • Like a pyramid -- higher level up is more specific and opinionated that you get
  • WrappedInput won't be setting its own rules

Component Library Colors and Themes

  • themes.ts
    • the components point to somewhere else via the variables (such as in secondaryDark)
      • base layer is the constants, but these point to a color theme such as indigoDark
  • theming.ts
    • Doing a module declaration for styled components, but need to change in both places and also update the light theme