iegik
10/13/2017 - 2:48 PM

Form decorator

Form validation

import Example from './example';
import Form from './Form';
import {sendMessage} from './api'; // Promise

@Form
class ExampleForm extends Component {
  render() {
    // {errors, values} = this.state
    return Example({...this.props, ...this.state});
  }
}
ExampleForm.defaultProps = { onSubmit: sendMessage }

export default ExampleForm;
import {isEmail, isRequired, isPhone, createValidation} from './validation';

describe('validators', () => {
    it('should validate isRequired', () => {
        expect(isRequired('')).toBeFalsy();
        expect(isRequired('1')).toBeTruthy();
    });
    it('should validate isEmail', () => {
        expect(isEmail('1')).toBeFalsy();
        expect(isEmail('i@g.cn')).toBeTruthy();
    });
    it('should validate isMobilePhone', () => {
        expect(isPhone('1')).toBeFalsy();
        expect(isPhone('+79218888888')).toBeTruthy();
    });
});

describe('validate', () => {
    let validate = createValidation([[isRequired,3], [isEmail, 5]]);
    it('should return all mached messages', () => {
        expect(validate('')).toEqual([3,5])
    });
    it('should return one mached message', () => {
        expect(validate('1')).toEqual([5])
    });
    it('should not return any message', () => {
        expect(validate('i@g.cn')).toEqual([])
    });
});

import {reduce} from  'lodash';
import {isEmpty, isMobilePhone} from  'validator';

export * from  'validator';

export const isRequired = value => !isEmpty(value);
export const isPhone = value => isMobilePhone(value, 'ru-RU');

export const createValidation = (checks = []) => {
    return value => reduce(checks, (errors, check) => {
        if (!check[0](value)) errors.push(check[1]);
        return errors;
    }, [])
};
import React from  'react';
import {reduce} from  'lodash';
import {createValidation, isRequired, isPhone, isEmail} from  '../../../utils/validation';

export default (props = {}) => {
    let {
        values,
        errors,
        onChangeField,
        onSend,
        isSubmitted,
        isFresh,
    } = props;
    let hasErrors = !!reduce(errors, (has, errors) => {
        if (errors && errors.length) {
            has++
        }
        return has;
    }, 0);
    return (
        <form onSubmit={onSend}>
            <div>
                <label htmlFor="bid-id">ID</label>
                <input type="text" autoComplete="off" id="bid-id" value={values.id}
                       onChange={e => onChangeField('id', e.target.value, createValidation([]))}
                       onBlur={e => onChangeField('id', e.target.value, createValidation([[isRequired,'Is required']]))}
                />
                { errors.id && errors.id.length ? (
                    <div>{errors.id[0]}</div>
                ) : (
                    <div>{'Some description'}</div>
                ) }
            </div>
            <div>
                <label htmlFor="bid-phone">Телефон</label>
                <input type="text" id="bid-phone" value={values.phone}
                       onChange={e => onChangeField('phone', e.target.value, createValidation([[isPhone, 'Should be a valid mobile phone']]))}
                       onBlur={e => onChangeField('phone', e.target.value, createValidation([[isRequired,'Is required']]))}
                />
                { errors.phone && errors.phone.length ? (
                    <div>{errors.phone[0]}</div>
                ) : (
                    <div>{'Some description'}</div>
                ) }
            </div>
            <div>
                <label htmlFor="bid-email">email</label>
                <input type="text" id="bid-email" value={values.email}
                       onChange={e => onChangeField('email', e.target.value, createValidation([[isEmail, 'Should be a valid email']]))}
                       onBlur={e => onChangeField('email', e.target.value, createValidation([[isRequired,'Is required']]))}
                />
                { errors.email && errors.email.length ? (
                    <div>{errors.email[0]}</div>
                ) : (
                    <div>{'Some description'}</div>
                ) }
            </div>
            <div>
                <input type="submit" disabled={isSubmitted || hasErrors || isFresh} />
            </div>
        </form>
    )
}
export const sendMessage = () => new Promise((res, rej) => {
  setTimeout(res, 1000, true)
});
import React, { Component } from 'react';
import { reduce } from 'lodash';

export default View => class extends Component {
  state = {
    values: {},
    errors: {},
    isSubmitted: false,
    isFresh: true,
    onChangeField: (field, value, validation) => {
      let { values, errors } = this.state;
      this.setState({
        values: { ...values, [field]: value },
        errors: { ...errors, [field]: validation(value) },
        isFresh: false,
      });
    },
    onSend: async (e) => {
      let { values, errors, isSubmitted } = this.state;
      e.preventDefault();
      if (!reduce(errors, (p, e) => p + e.length, 0) || !isSubmitted) {
        this.setState({ isSubmitted: true });
        try {
          this.setState({ status: await this.props.onSubmit(values) })
        } catch (e) {
          this.setState({ errors: { ...errors, status: e.message } })
        }
      }
    },
  };
  render(){
    return <View {...{...this.props,...this.state}} />;
  }
}