moksa-form-components

1.0.9 • Public • Published

MoksaMedia Form Assistant for React Bootstrap

A wrapper library for React Bootstrap form components.

This library is a combination of a ES6 form helper class that takes a form description object as an input and simplifies common form tasks such as state management, validation, pre- and post-change hooks, and value extraction for data submission.

It is written to work with React Bootstrap, but might be useful with other form systems.

Features:

  • required values
  • default form values
  • validate callback to handle form validation
  • inputTransformer to handle raw change events or transform input values
  • postChangeHook to process value changes after validation logic and allow the setting of other state values in response to value changes (such as showing and hiding other elements)
  • descriptive form syntax removed from JSX that greatly cleans up from JSX
  • creation of other arbitrary form element state values
  • date range picker using react-dates
  • tag input using react-tag-input
  • compatible with Rubix react theme or React Bootstrap

The library consists of the MKFormAssistant class that wraps all of the form logic, handling props and state and the various callbacks for changes, validation, and submission, and a a collection of React Bootstrap form element wrapper classes.

Motivation

Building a web application with a lot of forms using React Bootstrap I noticed that I was creating a lot of repeated boilerplate code that could be unified and simplified. Each element had a lot of common functionality, such as validation, change handling, visibility, etc...

This library is how I simplified and unified the form creation process using React Bootstrap. It consists of a form assistant class that wraps up a lot of common functions and logic, and some wrapper classes around the React Bootstrap form elements. To create a form you write a formDescription that tells the form assistant (MKFormAssistant) about the form you are creating, and you pass this to the MKFormAssistant in the constructor of the React element for your form. You then use the bundled form element items in your form and pass them the generated initial state and the formProps.

A very simple example


let formDescription = {

  name: {
    validate: (value) => {
      if (value.length > 5) {
        return {state:'success', message:null};
      }
      else {
        return {state:'error', message:"Too short!"};
      }
    },
    required: true,
    default: 'Your Name'
  }

};


class TestForm extends React.Component {

  constructor(props) {
    super(props);
    new MKFormAssistant(formDescription, this);
  }

  handleSubmit(status, data) {

    if (status.success) {
      // validation passed, handle data
    }
    else {
      // validation failed
    }

  };

  render() {

    return (

      <Form id='create-form'>

        {/* NAME */}
        <ValidatedFormGroup
          label="Name"
          placeHolder="Choose a public name for your event"
          {...this.formProps.name}
          {...this.state.name}
        />

        <FormGroup controlId="formSubmit">
          <Button className="submit" type="submit" onClick={this.formProps.handleSubmit}>
            Submit
          </Button>
        </FormGroup>

      </Form>

    )

  }

};

Styles

Don't forget to import the style sheet: @import '~moksa-form-components/css/styles.scss';

Form Elements

CheckboxGroup

  • a group of CheckboxItems

CheckboxGroup2Column

  • like CheckboxGroup, but splits the checkboxes into two columns for better layout

RadioGroup

  • a list of RadioItems that can allow multiple select

SelectBox

  • dropdown select box with a list of options

DateRangePickerWrapper

  • uses react-dates to wrap a control for selecting a range of dates

TagInput

  • wraps react-tag-input to provide a nice interface for inputting tags

ValidatedFormGroup

  • wraps react-bootstrap's FormGroup and FormControl to provide a text field and text area
  • supports pre- & post-addons

Form description schema

The form description is a javascript object with key / object pairs that contain the attributes that describe each form element or item. The form item name must be unique and cannot clash with any other state or prop entries. Form state and props will be created using this name.


formDescription: {

  ...

  <form item name> : {
      value:
        - an initial value for the item
        - string or array (for items that allow multiple values)
      required:
        - is a validated and non-null and non-empty value required to submit the form
        - boolean
      default:
        - what is the default value for the item (used as the initial state if value isn't specified)
        - string or array
      visible:
        - is the item visible (can be used to show and hide the item dynamically)
        - boolean
      validate:
        - called by handleChange to return new validation state
        - function, returns { state:<new state>, message:<new message> }
        - state is one of 'success', 'error', or null
        - message is any string
      inputTransformer
        - called by handleChange before validation, passed the new value
        - function, returns a transformed value to be validated
      postChangeHook:
        - called by handleChange after validation, passed the new value, a function used to set a path on the state, and the new state
        - function, returns nothing but can be used to set arbitrary values in the state based on user input
  }

  ...

}

Notes on callback functions

validate: called by handleChange when onChange fires. May be null or not specified, in which case the form item is unvalidated and any value is accepted. Is passed the new value, and must return an object with state = ('error' | 'success' | null) and message = "Any arbitrary string". If state is null, this will clear any validation decoration.

inputTransformer: passed the new value, pre-validation, and can return a transformed value

postChangeHook: passed the new value, post-validation, as well as a set() function that can be used to update the state and the new state itself.

postChangeHook(newValue, set, newState)

set() is a closure that can be used to make arbitrary updates to the form element state. It can be called with a value and path, in which case the path in the state is set to the value, or just a value, in which case the path is assumed to be the current form item's value.

set(value, path) -> set any arbitrary path in the state to value set(value) -> set value of current form item to value

handleChange

You do not need to specify a change handler for form elements. This is created automatically and injected into the formProps for each form item, which is passed to the item using the spread operator, typically: {...this.formProps.<item name>}

state and formProps

When you create the MKFormAssistant object in the form element constructor, the initial state and the formProps are set on the form element object for you automatically.


constructor(props) {
  super(props);
  this.formAssistant = new MKFormAssistant(formDescription, otherState, this.handleSubmit, this);
  // state and formProps are generated and injected into form element and can
  // be used in render method
}

formProps for each form item contains two fields:

  • required
  • handleChange

the form state for each item contains:

  • value
  • visible
  • validationState
  • validationMessage

Example Form description


// formDescription is passed to the MKFormAssistant object to setup
// things like default values, initial values, pre- & post-change hooks
// and if a form item is required

let formDescription = {
  name: {
    validate: (value) => {
      if (value.length > 5) {
        return {state:'success', message:null};
      }
      else {
        return {state:'error', message:"Too short!"};
      }
    },
    required: true,
    default: 'Your Name'
  },
  description: {
    validate: (value) => {
      return {state:'error', message:'Doh!'};
    },
    required: false,
    default: '',
    inputTransformer: (value) => {
      return value.toUpperCase();
    }
  },
  location: {
    default: '',
    postChangeHook:(value, set) => {
      set(value.toLowerCase());
    }
  },
  message: {
    default: '',
    postChangeHook:(value, set) => {
      set("I'm setting the location value", 'location.value');
    }
  },
  other: {
    default:'',
    postChangeHook:(value, set) => {
      set(false, 'showShow.visible');
    }
  }
};

// Other state is merged with generated form state
// but isn't processed by the form assistant

let otherState = {
  setShow: {
    visible: true,
    someVal: "It's a trap!"
  }

};

Example Form



class TestForm extends React.Component {

  constructor(props) {
    super(props);
    // The form assistant handles all of the onChange callbacks and validation callback,
    // as described in the formDescription
    this.formAssistant = new MKFormAssistant(formDescription, otherState, this.handleSubmit, this);
    this.formProps = this.formAssistant.formProps;
    this.state = this.formAssistant.formState;
    this.submitted = {};
  }

  handleSubmit(data) {
    // for testing purposes
    this.submitted = data;
  };

  render() {

    return (
      <Form id='retreat-create-form'>
    
        {/*
         GENERAL INFO
         */}
    
        <h3>General Info</h3>
        <Well>
    
          {/* NAME */}
          <ValidatedFormGroup
            label="Name"
            placeHolder="Choose a public name"
            {...this.formProps.name}
            {...this.state.name}
          />
    
          {/* LOCATION */}
          <ValidatedFormGroup
            label="Location"
            placeHolder="Where will the event be held?"
            {...this.formProps.location}
            {...this.state.location}
          />
    
    
          {/* DESCRIPTION */}
          <ValidatedFormGroup
            label="Description"
            inlineHelpBlock="Describe the experience in a couple paragraphs."
            placeHolder="This retreat is going to be..."
            componentClass="textarea"
            controlStyle={{height: "20em"}}
            {...this.formProps.description}
            {...this.state.description}
          />
    
        </Well>
    
        <FormGroup controlId="retreatCreateFormSubmit">
          <Button className="submit" type="submit" onClick={this.formProps.handleSubmit}>
            Submit
          </Button>
        </FormGroup>
    
      </Form>
    )
  }

};

Dependencies (5)

Dev Dependencies (29)

Package Sidebar

Install

npm i moksa-form-components

Weekly Downloads

1

Version

1.0.9

License

ISC

Last publish

Collaborators

  • moksamedia