react-jsonschema-form-validation
Version:
Simple form validation using JSON Schema and AJV
281 lines (236 loc) • 8.97 kB
JavaScript
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck";
import _createClass from "@babel/runtime/helpers/esm/createClass";
import _possibleConstructorReturn from "@babel/runtime/helpers/esm/possibleConstructorReturn";
import _getPrototypeOf from "@babel/runtime/helpers/esm/getPrototypeOf";
import _inherits from "@babel/runtime/helpers/esm/inherits";
import Ajv from 'ajv';
import classnames from 'classnames';
import throttle from 'lodash.throttle';
import memoize from 'memoize-one';
import React, { PureComponent } from 'react';
import scrollToElement from 'scroll-to-element';
import FormContext from './Context';
import { createAjv, filterByFieldNameWithWildcard, formatData, formatErrors, updateDataFromEvents } from './helpers'; // eslint-disable-next-line react/jsx-props-no-spreading
var DefaultFormComponent = function DefaultFormComponent(props) {
return React.createElement("form", Object.assign({
noValidate: true
}, props));
};
var initialState = {
errors: [],
isSubmitted: false,
touchedFields: [],
valid: true
};
var Form =
/*#__PURE__*/
function (_PureComponent) {
_inherits(Form, _PureComponent);
function Form() {
var _getPrototypeOf2;
var _this;
_classCallCheck(this, Form);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _possibleConstructorReturn(this, (_getPrototypeOf2 = _getPrototypeOf(Form)).call.apply(_getPrototypeOf2, [this].concat(args)));
_this.state = _objectSpread({}, initialState);
_this.memoGetClassnames = memoize(function (className, isSubmitted) {
return classnames('Jfv_Form', className, {
isSubmitted: isSubmitted
});
});
_this.memoGetContext = memoize(function (state, errorMessages) {
return _objectSpread({}, state, {
errorMessages: errorMessages,
getFieldErrors: _this.getFieldErrors,
handleFieldChange: _this.handleFieldChange,
isFieldTouched: _this.isFieldTouched,
isFieldInvalid: _this.isFieldInvalid,
isTouched: _this.isTouched,
touch: _this.touch
});
});
_this.memoGetValidator = memoize(function (ajv, schema, throttleDuration) {
var validate = ajv.compile(schema);
var validator = function validator(data) {
var formattedData = formatData(data);
var valid = validate(formattedData);
var errors = formatErrors(validate.errors);
_this.setState({
valid: valid,
errors: errors
});
};
if (_this.throttledValidator) _this.throttledValidator.cancel();
_this.throttledValidator = throttle(validator, throttleDuration);
return memoize(_this.throttledValidator);
});
_this.getClassnames = function () {
var className = _this.props.className;
var isSubmitted = _this.state.isSubmitted;
return _this.memoGetClassnames(className, isSubmitted);
};
_this.getContext = function () {
var errorMessages = _this.props.errorMessages;
return _this.memoGetContext(_this.state, errorMessages);
};
_this.getFieldErrors = function (fieldNames) {
fieldNames = Array.isArray(fieldNames) ? fieldNames : [fieldNames];
var errors = _this.state.errors;
return fieldNames.reduce(function (fieldsErrors, fieldName) {
return [].concat(_toConsumableArray(fieldsErrors), _toConsumableArray(filterByFieldNameWithWildcard(errors, fieldName)));
}, []);
};
_this.getValidator = function () {
var _this$props = _this.props,
ajv = _this$props.ajv,
schema = _this$props.schema,
throttleDuration = _this$props.throttleDuration;
return _this.memoGetValidator(ajv, schema, throttleDuration);
};
_this.handleFieldChange = function (event, value) {
var _this$props2 = _this.props,
data = _this$props2.data,
onChange = _this$props2.onChange;
if (onChange) {
if (typeof event === 'string') {
event = {
target: {
name: event,
value: value
}
};
}
var newData = updateDataFromEvents(data, event);
onChange(newData, event);
}
};
_this.handleSubmit = function (event) {
event.preventDefault();
_this.submit(event);
};
_this.handleSubmitError = function () {
var scrollToError = _this.props.scrollToError;
var errors = _this.state.errors;
if (process.env.REACT_APP_JFV_DEBUG === 'true') console.log(errors); // eslint-disable-line no-console
if (scrollToError) _this.scrollToFirstError();
};
_this.handleSubmitSuccess = function (event) {
var onSubmit = _this.props.onSubmit;
_this.reset();
onSubmit(event);
};
_this.isFieldInvalid = function (fieldNames) {
var _this2;
fieldNames = Array.isArray(fieldNames) ? fieldNames : [fieldNames];
return (_this2 = _this).getFieldErrors.apply(_this2, _toConsumableArray(fieldNames)).length > 0;
};
_this.isFieldTouched = function (fieldNames) {
fieldNames = Array.isArray(fieldNames) ? fieldNames : [fieldNames];
var touchedFields = _this.state.touchedFields;
return !!fieldNames.find(function (fieldName) {
return filterByFieldNameWithWildcard(touchedFields.map(function (field) {
return {
field: field
};
}), fieldName).length > 0;
});
};
_this.isTouched = function () {
var touchedFields = _this.state.touchedFields;
return !!touchedFields.length;
};
_this.reset = function () {
return _this.setState(initialState);
};
_this.scrollToFirstError = function () {
var scrollOptions = _this.props.scrollOptions;
var errors = _this.state.errors;
var firstError = errors[0];
var element = document.getElementsByName(firstError.field)[0];
scrollToElement(element, scrollOptions);
};
_this.submit = function (event) {
var valid = _this.state.valid;
_this.setState({
isSubmitted: true
});
if (valid) _this.handleSubmitSuccess(event);else _this.handleSubmitError();
};
_this.touch = function (fieldNames) {
fieldNames = Array.isArray(fieldNames) ? fieldNames : [fieldNames];
var touchedFields = _this.state.touchedFields;
_this.setState({
touchedFields: _toConsumableArray(new Set([].concat(_toConsumableArray(touchedFields), _toConsumableArray(fieldNames))))
});
};
_this.validate = function () {
var data = _this.props.data;
var validate = _this.getValidator();
validate(data);
};
return _this;
}
_createClass(Form, [{
key: "componentDidMount",
value: function componentDidMount() {
this.validate();
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate() {
this.validate();
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
if (this.throttledValidator) this.throttledValidator.cancel();
}
}, {
key: "render",
value: function render() {
var _this$props3 = this.props,
ajv = _this$props3.ajv,
children = _this$props3.children,
className = _this$props3.className,
FormComponent = _this$props3.component,
data = _this$props3.data,
throttleDuration = _this$props3.throttleDuration,
errorMessages = _this$props3.errorMessages,
onChange = _this$props3.onChange,
onSubmit = _this$props3.onSubmit,
schema = _this$props3.schema,
scrollOptions = _this$props3.scrollOptions,
scrollToError = _this$props3.scrollToError,
props = _objectWithoutProperties(_this$props3, ["ajv", "children", "className", "component", "data", "throttleDuration", "errorMessages", "onChange", "onSubmit", "schema", "scrollOptions", "scrollToError"]);
return React.createElement(FormContext.Provider, {
value: this.getContext()
}, React.createElement(FormComponent, Object.assign({
className: this.getClassnames(),
onSubmit: this.handleSubmit // eslint-disable-next-line react/jsx-props-no-spreading
}, props), children));
}
}]);
return Form;
}(PureComponent);
Form.defaultProps = {
ajv: createAjv(),
children: null,
className: '',
component: DefaultFormComponent,
data: {},
errorMessages: {},
onChange: null,
scrollToError: true,
scrollOptions: {
offset: 0,
align: 'middle',
duration: 900
},
throttleDuration: 200
};
export default Form;