3.0.13 • Published 4 years ago

@rexlabs-spicerhaart/form v3.0.13

Weekly downloads
-
License
UNLICENSED
Repository
-
Last release
4 years ago

Form - Vivid Component

A form abstraction built on top of @aaronuu/react-forms to track the state of forms locally.

There is two ways to use this component

  • ReactForms component with a render prop (recommended)
  • withForm({ ...config }) higher order component

The withForm({ ...config }) HOC is just a light wrapper around the ReactForms component, just with less flexibility (it is mostly included for compatibility reasons)

The <Form /> component is a light wrapper around a basic HTML form that adds the neccessary event handlers and also lets you give a FormError component as a prop to display all of the form errors as a group, if needed.

The <Field /> components are responsible for mapping the form state to the input component within that field. The name given to the <Field /> will be the key of the values object in state (nested object and array names are supported - a FieldArray component is exposed to help with array fields).

Validation can be achieved by giving a validation function to the validate prop of ReactForms or as a property on the configuration options given to the withForm({ ...config }) HOC. Validating this way is a form level concern, with individual errors being passed down from the formik context to the fields individually, instead of each field being able to validate itself. Each Field has it's own validate prop that will be used to validate individual fields and if set will be used instead.

Note - In certain circumstances (where the individual field validation is synchronous and the outer form validation is asynchronous) then the resolved error will override the synchronous error, but only when the form is submitted.

The asyncValuesReady prop will reset the form when this value changes from false to true the very first time.

API

Form

FormError: React Component

This component will receive an object of errors under an error prop but only if there are errors

Field

name: String

Name that the field will be referred by internally, can be flattened object or array notation (i.e. nested.name.0)

label: String

Label that will be given to the field

isInlineLabel: boolean

Whether or not to inline the label

inputProps: Object

Object that will be spread over the input component

Input: React Component

Input component to render

FieldLabel: React Component

Field label component to render

FieldError: React Component

Field error component to render

onFocus: Function

Function to run when the field is focused

onChange: Function

Function to run when the field is changed

onBlur: Function

Function to run when the field is blurred

FieldArray

Takes all the same props as <Field /> and will give them to the <Field /> if the user spreads the return value of the getFieldProps function on to the field

Examples

ReactForms

import React, { Component } from 'react';
import { ReactForms, Form, Field } from '@rexlabs/form';

@entityModel
class FormParent extends Component {
  render() {
    return (
      <ReactForms>
        {({ values, errors, touched, setValues /* etc. */ }) => (
          <Form>
            <Field
              name="address.line_1"
              label="Address line 1"
              Input={TextInput}
            />
            <Field
              name="address.line_2"
              label="Address line 2"
              Input={TextInput}
            />
            <Field name="address.postcode" label="Postcode" Input={TextInput} />
          </Form>
        )}
      </ReactForms>
    );
  }
}

withForm({ ...config })

import React, { Component } from 'react';
import {
  withForm,
  Form,
  Field,
  FieldArray
} from '@rexlabs/form';

@entityModel // Entity HOC that needs to pass initial state to form should be above withForm
@withForm({
  name: 'MyForm',
  handleSubmit: (values, {
    props,
    setValues,
      setFieldValue
      setErrors,
      setFieldError,
      setTouched,
      setFieldTouched,
      setStatus,
      resetForm,
      submitForm
  }) => {
    // submit action here
    return props.entityModel.create(values);
  },
  mapPopsToValues: props => ({
    // return initial state object here
    email: props.entity.email
  }),
  validate: (values, props) => {
    const errors = {};

    if (!values.email) {
      errors.email = 'Email is required';
    }

    return errors;
  },
  validateOnBlur: true,
  validateOnChange: false,
  // asyncValuesReady function runs each render until it returns true
  // this will ensure that when your async content loads the form
  // is populated with those initial values
  asyncValuesReady = props => props.entity.status === 'loaded'
})
class FormParent extends Component {
  render () {
    return (
      <Form>
        <Field
          name="email"
          label="Email"
          onBlur={e => {
            // action to take on input blur
            this.props.Test.submit();
          }}
          onChange={e => {
            // action to take on input change
            this.props.Test.change('emailClone', e.target.value);
          }}
          Input={TextInput}
          inputProps={{
            placeholder: 'Enter email',
            debounce: 0
          }}
        />
        <Field
          name="emailClone"
          label="Email Clone"
          Input={TextInput}
          inputProps={{
            placeholder: 'Enter email',
            debounce: 0
          }}
        />
        {/* Add a default field array for dynamic fields */}
        <FieldArray
          name='friends'
          Input={TextInput}
          inputProps={{
            placeholder: 'Add friend'
          }}
        >
        {/* FieldArray will accept a render prop for more custom behaviour */}
        <FieldArray
          name='friends'
          Input={TextInput}
          inputProps={{
            placeholder: 'Add friend'
          }}
        >
          {({ fields, push, pop, swap, move, insert, unshift, remove, getFieldProps }) => (
            <div>
              {_.map(fields, (field, index) => (
                <div key={field.id}>
                  <Field
                    {...getFieldProps(field, index)}
                  />
                  <button onClick={() => { remove(index); }}>Remove</button>
                </div>
              ))}
              <button
                onClick={() => {
                  push({
                     // Custom options for each field pushed will be merged with options
                     // given to FieldArray with individual overrides taking preference
                    Input: Checkbox,
                    validate: val => !val ? 'Must be checked' : null
                  })
                }}
              >
                Add new
              </button>
            </div>
          )}
        </FieldArray>
      </Form>
    );
  }
}

Development

Install dependencies

$ yarn

Available Commands

$ yarn start              # starts storybook, for visually testing form
$ yarn test               # runs all units tests
$ yarn test:watch         # runs unit tests when files change
$ yarn build              # bundles the package for production

Legal

Copyright © 2018 Rex Software All Rights Reserved.