109 lines
3.2 KiB
JavaScript
109 lines
3.2 KiB
JavaScript
import React, { Component, PropTypes } from 'react';
|
|
import ImmutablePropTypes from "react-immutable-proptypes";
|
|
import isEqual from 'lodash/isEqual';
|
|
|
|
const truthy = () => ({ error: false });
|
|
|
|
class ControlHOC extends Component {
|
|
|
|
static propTypes = {
|
|
controlComponent: PropTypes.func.isRequired,
|
|
field: ImmutablePropTypes.map.isRequired,
|
|
value: PropTypes.oneOfType([
|
|
PropTypes.node,
|
|
PropTypes.object,
|
|
PropTypes.string,
|
|
PropTypes.bool,
|
|
]),
|
|
metadata: ImmutablePropTypes.map,
|
|
onChange: PropTypes.func.isRequired,
|
|
onValidate: PropTypes.func.isRequired,
|
|
onAddAsset: PropTypes.func.isRequired,
|
|
onRemoveAsset: PropTypes.func.isRequired,
|
|
getAsset: PropTypes.func.isRequired,
|
|
};
|
|
|
|
shouldComponentUpdate(nextProps) {
|
|
return this.props.value !== nextProps.value;
|
|
}
|
|
|
|
processInnerControlRef = (wrappedControl) => {
|
|
if (!wrappedControl) return;
|
|
this.wrappedControlValid = wrappedControl.isValid || truthy;
|
|
};
|
|
|
|
validate = (skipWrapped = false) => {
|
|
const { field, value } = this.props;
|
|
const errors = [];
|
|
const validations = [this.validatePresence, this.validatePattern];
|
|
validations.forEach((func) => {
|
|
const response = func(field, value);
|
|
if (response.error) errors.push(response.error);
|
|
});
|
|
if (skipWrapped) {
|
|
if (skipWrapped.error) errors.push(skipWrapped.error);
|
|
} else {
|
|
const wrappedError = this.validateWrappedControl(field);
|
|
if (wrappedError.error) errors.push(wrappedError.error);
|
|
}
|
|
this.props.onValidate(errors);
|
|
};
|
|
|
|
validatePresence(field, value) {
|
|
const isRequired = field.get('required', true);
|
|
if (isRequired && (
|
|
value === null ||
|
|
value === undefined ||
|
|
(value.hasOwnProperty('length') && value.length === 0) ||
|
|
(value.constructor === Object && Object.keys(value).length === 0)
|
|
)) {
|
|
return { error: true };
|
|
}
|
|
return { error: false };
|
|
}
|
|
|
|
validatePattern(field, value) {
|
|
const pattern = field.get('pattern', false);
|
|
if (pattern && !RegExp(pattern.first()).test(value)) {
|
|
return { error: `${ field.get('label', field.get('name')) } didn't match the pattern: ${ pattern.last() }` };
|
|
}
|
|
return { error: false };
|
|
}
|
|
|
|
validateWrappedControl = (field) => {
|
|
const response = this.wrappedControlValid();
|
|
if (typeof response === "boolean") {
|
|
const isValid = response;
|
|
return { error: (!isValid) };
|
|
} else if (response.hasOwnProperty('error')) {
|
|
return response;
|
|
} else if (response instanceof Promise) {
|
|
response.then(
|
|
() => { this.validate({ error: false }); },
|
|
(error) => {
|
|
this.validate({ error: `${ field.get('label', field.get('name')) } - ${ error }.` });
|
|
}
|
|
);
|
|
return { error: `${ field.get('label', field.get('name')) } is processing.` };
|
|
}
|
|
return { error: false };
|
|
};
|
|
|
|
render() {
|
|
const { controlComponent, field, value, metadata, onChange, onAddAsset, onRemoveAsset, getAsset } = this.props;
|
|
return React.createElement(controlComponent, {
|
|
field,
|
|
value,
|
|
metadata,
|
|
onChange,
|
|
onAddAsset,
|
|
onRemoveAsset,
|
|
getAsset,
|
|
forID: field.get('name'),
|
|
ref: this.processInnerControlRef,
|
|
});
|
|
}
|
|
}
|
|
|
|
export default ControlHOC;
|