All files / src VeasyForm.jsx

100% Statements 34/34
100% Branches 12/12
100% Functions 9/9
100% Lines 29/29
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87                1x 1x 1x     1x   1x       49x   16x   12x 12x 12x   2x       10x 10x                 28x 50x   49x 10x     39x   13x     13x 13x       1x 1x 1x       15x 15x 15x               2x         2x              
/* eslint-disable react/forbid-prop-types,import/no-extraneous-dependencies */
import is from 'is_js';
import PropTypes from 'prop-types';
import React from 'react';
import * as lib from './helpers';
 
export default class VeasyForm extends React.Component {
  triggerValidation = e => {
    e.persist();
    const { schema, allState, update } = this.props;
    lib.startValidating(e.target, schema, update, allState);
  };
 
  handleOnChange = e => this.triggerValidation(e);
 
  handleBlur = e => this.triggerValidation(e);
 
  isRegisteredComponent = child => {
    // Skip HTML element
    if (is.string(child.type)) return false;
 
    if (!is.propertyDefined(child.props, 'name')) return false;
 
    const childName = child.props.name;
    const names = Object.keys(this.props.schema);
    if (names.includes(childName)) return true;
 
    return false;
  };
 
  cloneElement = (child, childName) => {
    const childProp = this.props.allState[childName];
    return React.cloneElement(child, {
      status: childProp.status,
      errorText: childProp.errorText,
      value: childProp.value,
      onChange: this.handleOnChange
    });
  };
 
  recursiveCloneChildren = children =>
    React.Children.map(children, child => {
      if (!React.isValidElement(child)) return child;
      
      if (this.isRegisteredComponent(child)) {
        return this.cloneElement(child, child.props.name);
      }
 
      if (is.not.propertyDefined(child.props, 'children')) return child;
 
      const childProps = {};
 
      // String has no Prop
      childProps.children = this.recursiveCloneChildren(child.props.children);
      return React.cloneElement(child, childProps);
    });
 
  handleReset = e => {
    e.preventDefault();
    const { update, schema, allState } = this.props;
    update(lib.resetForm(schema, allState));
  };
 
  render() {
    const { schema, allState, update, children, tag, ...reset } = this.props;
    const Component = tag;
    return (
      <Component onBlur={this.handleBlur} onReset={this.handleReset} {...reset}>
        {this.recursiveCloneChildren(children)}
      </Component>
    );
  }
}
 
VeasyForm.defaultProps = {
  allState: undefined,
  tag: 'form'
};
 
VeasyForm.propTypes = {
  schema: PropTypes.object.isRequired,
  allState: PropTypes.object,
  update: PropTypes.func.isRequired,
  tag: PropTypes.string,
  children: PropTypes.any.isRequired
};