Nascent Personality Manifestation

    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>
        )
      }
    
    };
    
    

    Keywords

    none

    Install

    npm i moksa-form-components

    DownloadsWeekly Downloads

    10

    Version

    1.0.9

    License

    ISC

    Last publish

    Collaborators

    • moksamedia