All files validate.jsx

100% Statements 29/29
100% Branches 12/12
100% Functions 11/11
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 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120            1x         9x   9x 9x         19x   19x             19x 19x       19x           19x           9x 9x 9x 9x           9x   9x               10x       9x     9x   9x 10x 5x 5x           5x         10x       47x                 28x       1x                 1x            
import React, { Component, PropTypes } from "react";
import merge from "lodash/merge";
import union from "lodash/union";
import validationRules from "./validationRules";
import createValidationRulesFromInput from "./createValidationRulesFromInput";
 
const _ = { merge, union };
 
 
class Validate extends Component {
  static checkErrorCount(errorObject) {
    return Object.keys(errorObject).length &&
      Object.keys(errorObject).reduce((acc, curr) => {
        const total = acc += Object.keys(errorObject[curr]).length;
        return total;
      }, 0);
  }
 
  constructor(props) {
    super(props);
 
    this.state = {
      errorMessages: {},
      argumentSeperator: ":",
      allValid: false,
      errorCount: 0,
    };
 
    this.handleValidate = this.handleValidate.bind(this);
    this.testForValidation = this.testForValidation.bind(this);
  }
 
  componentWillMount() {
    const validations = _.merge(
      {},
      createValidationRulesFromInput(this.renderChildren()),
      this.props.validations,
    );
 
    this.setState({
      validations,
    });
  }
 
  handleValidate(e) {
    const fieldName = e.target.name;
    const fieldValue = e.target.value;
    const fieldErrorMessages = this.testForValidation(fieldName, fieldValue);
    const allErrors = Object.assign(
        {},
        this.state.errorMessages,
        { [fieldName]: fieldErrorMessages },
      );
 
    const errorCount = Validate.checkErrorCount(allErrors);
 
    this.setState({
      errorMessages: allErrors,
      errorCount,
      allValid: errorCount === 0,
    });
  }
 
  ruleHasArgument(rule) {
    return rule.indexOf(this.state.argumentSeperator) >= 0;
  }
 
  testForValidation(field, value) {
    const fieldRequirements = this.state.validations[field];
 
    // combine both the built in rules and custom rules
    const combinedValidationRules = _.merge({}, validationRules, this.props.rules);
 
    return fieldRequirements && fieldRequirements.map(rule => {
      if (this.ruleHasArgument(rule)) {
        const [funcName, arg] = rule.split(this.state.argumentSeperator);
        return (
          combinedValidationRules[funcName] &&
          !combinedValidationRules[funcName].test(arg)(value) &&
          combinedValidationRules[funcName].message(arg)(field, value)
        );
      }
      return (
        combinedValidationRules[rule] &&
        !combinedValidationRules[rule].test(value) &&
        combinedValidationRules[rule].message(field, value)
      );
    }).filter(val => val);
  }
 
  renderChildren() {
    return this.props.children({
      validate: this.handleValidate,
      errorMessages: this.state.errorMessages,
      allValid: this.state.allValid,
      errorCount: this.state.errorCount,
    });
  }
 
  render() {
    return this.renderChildren();
  }
}
 
Validate.propTypes = {
  children: PropTypes.func.isRequired,
  validations: PropTypes.objectOf(PropTypes.array),
  rules: PropTypes.shape({
    test: PropTypes.func,
    message: PropTypes.func,
  }),
};
 
Validate.defaultProps = {
  validations: {},
  rules: {},
};
 
export default Validate;