react-livr-validation
Version:
react validation component with LIVR as validation engine
174 lines (146 loc) • 4.42 kB
JavaScript
// @flow
import { Component } from 'react';
import LIVR from 'livr';
import pick from 'ramda/src/pick';
import omit from 'ramda/src/omit';
import merge from 'ramda/src/merge';
import __ from 'ramda/src/__';
import keys from 'ramda/src/keys';
import compose from 'ramda/src/compose';
import prop from 'ramda/src/prop';
import and from 'ramda/src/and';
import not from 'ramda/src/not';
import equals from 'ramda/src/equals';
import isEmpty from 'ramda/src/isEmpty';
import when from 'ramda/src/when';
import pipe from 'ramda/src/pipe';
import ContextTypes from './types/context-types';
const HAS_ERRORS = 'HAS_ERRORS';
type AliasedRule = {
name: string,
rules: Object,
error: string
};
type Props = {
data: Object,
schema: Object,
rules: Object,
aliasedRules: Array<AliasedRule>,
className: string,
style: Object,
errorCodes: Object,
children?: any
};
type State = {
errors: Object
};
type DataChunk = {
name: string,
value: any
};
LIVR.Validator.defaultAutoTrim(true);
export default class Validation extends Component {
static childContextTypes = ContextTypes;
static defaultProps = {
rules: {},
aliasedRules: [],
className: '',
style: {},
errorCodes: {},
data: {},
schema: {}
};
state: State = {
errors: {}
};
getChildContext() {
return {
setData: ({ name, value }: DataChunk) => {
const { data } = this;
data[name] = value;
this.validateData(data, name);
},
getError: (name: string) => this.state.errors[name],
getErrors: () => this.state.errors,
className: this.props.className,
style: this.props.style,
errorCodes: this.props.errorCodes
};
}
componentDidMount() {
const { schema } = this.props;
this.createValidator(schema);
this.initialValidate();
}
componentWillReceiveProps({ schema: nextSchema, data: nextData }: Props) {
const { schema, data } = this.props;
const equalsSchema = equals(schema, nextSchema);
const equalsData = equals(data, nextData);
if (!equalsSchema) {
this.createValidator(nextSchema);
}
if (!equalsData) {
this.data = nextData;
}
if (!equalsSchema || !equalsData) {
this.validateData(this.data, '');
}
}
createValidator(schema: Object) {
const { rules, aliasedRules } = this.props;
this.validator = new LIVR.Validator(schema);
this.validator.registerRules(rules);
aliasedRules.forEach((rule: AliasedRule) => {
this.validator.registerAliasedRule(rule);
});
}
getNextErrors = (name: string, error: Object, errors: Object) => {
const { errors: stateErrors } = this.state;
const nextErrorsPipe = pipe(
when(() => !isEmpty(error), merge(__, error)),
when(compose(and(isEmpty(error)), prop(name)), omit([name])),
when(compose(and(!errors), prop(HAS_ERRORS)), omit([HAS_ERRORS])),
when(
compose(and(errors), not, prop(HAS_ERRORS)),
merge(__, { [HAS_ERRORS]: 1 })
)
);
return nextErrorsPipe(stateErrors);
};
validateData(data: Object, name: string) {
const { validator, getNextErrors } = this;
validator.validate(data);
const errors = validator.getErrors();
const error = pick([name], errors || {});
this.setState({
errors: {
...getNextErrors(name, error, errors)
}
});
}
initialValidate() {
const { validator } = this;
const { data, schema } = this.props;
const validationData = keys(
schema
).reduce((acc: Object, key: string) => {
acc[key] = data[key];
return acc;
}, {});
validator.validate(validationData);
const errors = validator.getErrors();
if (errors) {
this.setState({
errors: {
[HAS_ERRORS]: 1
}
});
}
}
validator: Object;
props: Props;
data = this.props.data;
render() {
return this.props.children;
}
}