Skip to content

Commit

Permalink
merge
Browse files Browse the repository at this point in the history
  • Loading branch information
Mariusz Przodała committed Jan 7, 2019
2 parents 9d5beda + 5116d08 commit 56b220f
Show file tree
Hide file tree
Showing 14 changed files with 381 additions and 163 deletions.
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"env": {
"jest": true
},
"parser": "babel-eslint",
"extends": ["airbnb"],
"rules": {
"indent": [2, 4],
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-components-form",
"version": "3.5.2",
"version": "3.6.0-rc-7",
"description": "React form components",
"main": "main.js",
"jsnext:main": "src/index.js",
Expand Down
7 changes: 4 additions & 3 deletions src/components/AutocompleteField.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import Autocomplete from 'react-autosuggest';
import FieldConnect from './FieldConnect';
import cloneDeep from 'lodash/cloneDeep';
import get from 'lodash/get';
import { FieldConnect } from './FieldConnect';
import ErrorField from './ErrorField';
import { get, cloneArray } from '../helpers';
import { fieldDefaultPropTypes } from '../constants/propTypes';
import { fieldDefaultProps } from '../constants/defaultProps';

Expand Down Expand Up @@ -93,7 +94,7 @@ export class AutocompleteField extends Component {

applySectionFilter(sections, escapedValue, searchKey) {
const { sectionSuggestionsIndex } = this.props;
const copiedSections = cloneArray(sections);
const copiedSections = cloneDeep(sections);
const newSections = [];
copiedSections.forEach((section) => {
const clonedSection = Object.assign({}, section);
Expand Down
2 changes: 1 addition & 1 deletion src/components/CheckboxField.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import FieldConnect from './FieldConnect';
import { FieldConnect } from './FieldConnect';
import ErrorField from './ErrorField';
import { fieldDefaultPropTypes } from '../constants/propTypes';
import { fieldDefaultProps } from '../constants/defaultProps';
Expand Down
2 changes: 1 addition & 1 deletion src/components/DateField.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import FieldConnect from './FieldConnect';
import { FieldConnect } from './FieldConnect';
import ErrorField from './ErrorField';
import { fieldDefaultPropTypes } from '../constants/propTypes';
import { fieldDefaultProps } from '../constants/defaultProps';
Expand Down
98 changes: 66 additions & 32 deletions src/components/FieldConnect.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import React from 'react';
import isEqual from 'lodash/isEqual';
import PropTypes from 'prop-types';

import { cloneArray, cloneObject } from '../helpers';
import cloneDeep from 'lodash/cloneDeep';

export const FieldConnect = (Component) => {
class FieldConnector extends React.Component {
Expand All @@ -11,26 +10,22 @@ export const FieldConnect = (Component) => {
this.listeners = [];
this.fieldValue = null;
this.fieldValidationErrors = null;
this.onChangeData = this.onChangeData.bind(this);
this.submit = this.submit.bind(this);
this.getValidationErrors = this.getValidationErrors.bind(this);
this.getPath = this.getPath.bind(this);
this.hasValidationError = this.hasValidationError.bind(this);
this.getPropsFromSchema = this.getPropsFromSchema.bind(this);
this.getEventsEmitter = this.getEventsEmitter.bind(this);
this.getFieldAttributes = this.getFieldAttributes.bind(this);
this.fieldValidators = [];
}

getChildContext() {
return {
getSchema: this.context.getSchema,
getPath: this.getPath,
setFieldValidator: this.setFieldValidator,
removeFieldValidator: this.removeFieldValidator,
};
}

componentWillMount() {
this.updateModelWithValueOrOptions();
this.registerListeners();
this.registerFieldValidators();
}

shouldComponentUpdate(nextProps) {
Expand All @@ -47,9 +42,10 @@ export const FieldConnect = (Component) => {

componentWillUnmount() {
this.unregisterListeners();
this.unregisterFieldValidators();
}

onChangeData(value) {
onChangeData = (value) => {
const { name, callbacks: { onChange } } = this.props;
const {
setModel,
Expand All @@ -73,16 +69,27 @@ export const FieldConnect = (Component) => {
}
}

getModelPath() {
const seperatedPath = this.getPath().split('.');
return seperatedPath.splice(1, seperatedPath.length)
.map((path) => {
const pathAttributes = path.split('-');
if (pathAttributes.length > 1) {
return pathAttributes[1];
}
return path;
})
.join('.');
}

setFieldValidator = (validator) => {
const modelPath = this.getModelPath();
this.context.setValidator(modelPath, validator);
this.fieldValidators.push(validator);
};

setCurrentFieldValue(value) {
if (Array.isArray(value)) {
this.fieldValue = cloneArray(value);
return;
}
if (typeof value === 'object') {
this.fieldValue = cloneObject(value);
return;
}
this.fieldValue = value;
this.fieldValue = cloneDeep(value);
}

getValue() {
Expand All @@ -99,18 +106,16 @@ export const FieldConnect = (Component) => {
return fieldValue;
}

getPropsFromSchema() {
getPropsFromSchema = () => {
const { name } = this.props;
const { getSchema } = this.context;
if (typeof getSchema !== 'function') return undefined;
return getSchema(name);
}

getEventsEmitter() {
return this.context.eventsEmitter;
}
getEventsEmitter = () => this.context.eventsEmitter;

getValidationErrors() {
getValidationErrors = () => {
const { name, callbacks: { onError } } = this.props;
const { getValidationErrors } = this.context;
let results = [];
Expand All @@ -121,14 +126,14 @@ export const FieldConnect = (Component) => {
return results;
}

getPath() {
getPath = () => {
const { name } = this.props;
const { getPath } = this.context;
if (typeof getPath !== 'function') return name;
return `${getPath()}.${name}`;
}
};

getFieldAttributes() {
getFieldAttributes = () => {
const { validateOnChange, isFormSubmitted } = this.context;
const { fieldAttributes, callbacks: { onFocus, onBlur } } = this.props;
return Object.assign(
Expand Down Expand Up @@ -159,6 +164,31 @@ export const FieldConnect = (Component) => {
}
}

removeFieldValidator = (validator) => {
const index = this.fieldValidators.indexOf(validator);
if (index > -1) {
this.context.removeValidator(validator);
this.fieldValidators.splice(index, 1);
}
};

registerFieldValidators = () => {
if (this.props.validator) {
const { getModel } = this.context;
const fieldValidator = (...attr) => {
const value = typeof getModel === 'function' ? getModel(this.props.name) : '';
return this.props.validator(value, ...attr);
};
this.setFieldValidator(fieldValidator);
}
};

unregisterFieldValidators = () => {
this.fieldValidators.forEach((validator) => {
this.context.removeValidator(validator);
});
};

shouldShowErrors() {
const { hasBeenTouched, validateOnChange, isFormSubmitted } = this.context;
if (!validateOnChange || isFormSubmitted) {
Expand All @@ -167,7 +197,7 @@ export const FieldConnect = (Component) => {
return hasBeenTouched(this.getPath());
}

submit(event) {
submit = (event) => {
const { submitForm } = this.context;
if (typeof submitForm !== 'function') return;
submitForm(event);
Expand Down Expand Up @@ -234,9 +264,7 @@ export const FieldConnect = (Component) => {
}
}

hasValidationError() {
return this.getValidationErrors().length > 0;
}
hasValidationError = () => this.getValidationErrors().length > 0;

render() {
return (<Component
Expand Down Expand Up @@ -272,11 +300,15 @@ export const FieldConnect = (Component) => {
hasBeenTouched: PropTypes.func,
validateOnChange: PropTypes.bool,
isFormSubmitted: PropTypes.bool,
setValidator: PropTypes.func,
removeValidator: PropTypes.func,
};

FieldConnector.childContextTypes = {
getSchema: PropTypes.func,
getPath: PropTypes.func,
setFieldValidator: PropTypes.func,
removeFieldValidator: PropTypes.func,
};

FieldConnector.propTypes = {
Expand Down Expand Up @@ -315,11 +347,13 @@ export const FieldConnect = (Component) => {
onFocus: PropTypes.func,
onBlur: PropTypes.func,
}),
validator: PropTypes.func,
};

FieldConnector.defaultProps = {
fieldAttributes: {},
callbacks: {},
validator: undefined,
};

return FieldConnector;
Expand Down
2 changes: 1 addition & 1 deletion src/components/FieldsRestyle.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import classnames from 'classnames';
import { get } from '../helpers';
import get from 'lodash/get';

const extendStyles = (
styles,
Expand Down
40 changes: 38 additions & 2 deletions src/components/Form.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import cloneDeep from 'lodash/cloneDeep';
import Storage from './Storage';
import { cloneObject } from '../helpers';

class Form extends React.Component {
constructor(props) {
Expand All @@ -17,6 +17,7 @@ class Form extends React.Component {
if (props.controller) {
props.controller.setForm(this);
}
this.fieldsValidators = [];
this.storage = new Storage(this.state.model);
this.eventsEmitter = props.eventsEmitter;
this.setModel = this.setModel.bind(this);
Expand Down Expand Up @@ -53,6 +54,8 @@ class Form extends React.Component {
hasBeenTouched: this.hasBeenTouched,
validateOnChange: this.state.validateOnChange,
isFormSubmitted: this.state.isFormSubmitted,
setValidator: this.setValidator,
removeValidator: this.removeValidator,
};
}

Expand Down Expand Up @@ -100,6 +103,23 @@ class Form extends React.Component {
return this.state.schema.getField(name);
}

setValidator = (path, validator) => {
const index = this.findValidatorIndex(validator);
if (index < 0) {
const schemaValidator = (model, schema) => {
const validationResults = validator(model);
if (validationResults && typeof validationResults === 'boolean') {
return;
}
schema.setModelError(path, validationResults);
};
this.fieldsValidators.push({ path, validator, schemaValidator });
if (typeof this.state.schema.validate === 'function') {
this.state.schema.addValidator(schemaValidator);
}
}
};

getValidationErrors(name) {
return this.state.validationErrors[name] || {};
}
Expand All @@ -112,6 +132,20 @@ class Form extends React.Component {
return this.props.id;
}

removeValidator = (validator) => {
const index = this.findValidatorIndex(validator);
if (index > -1) {
const fieldValidator = this.fieldsValidators[index];
if (typeof this.state.schema.validate === 'function') {
this.state.schema.removeValidator(fieldValidator.schemaValidator);
}
this.fieldsValidators.splice(index, 1);
}
};

findValidatorIndex = validator =>
this.fieldsValidators.findIndex(fieldValidator => fieldValidator.validator === validator);

submitListener() {
return this.submitForm();
}
Expand Down Expand Up @@ -178,7 +212,7 @@ class Form extends React.Component {
}

runSubmit(validationErrors, modelData) {
const model = cloneObject(modelData);
const model = cloneDeep(modelData);
if (Object.keys(validationErrors).length > 0) {
if (this.props.onError) this.props.onError(validationErrors, model);
return undefined;
Expand Down Expand Up @@ -245,6 +279,8 @@ Form.childContextTypes = {
hasBeenTouched: PropTypes.func,
validateOnChange: PropTypes.bool,
isFormSubmitted: PropTypes.bool,
setValidator: PropTypes.func,
removeValidator: PropTypes.func,
};

Form.propTypes = {
Expand Down
11 changes: 7 additions & 4 deletions src/components/ListField.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import isEqual from 'lodash/isEqual';
import cloneDeep from 'lodash/cloneDeep';
import Storage from './Storage';
import FieldConnect from './FieldConnect';

Expand Down Expand Up @@ -43,9 +45,11 @@ export class ListField extends React.Component {
componentWillReceiveProps({ value }) {
let shouldSetState = false;
value.forEach((item, key) => {
if (item !== this.state.model[key].value) shouldSetState = true;
if (!isEqual(item, this.state.model[key].value)) shouldSetState = true;
});
if (shouldSetState) this.storage.setModel(ListField.getModelFromProps({ value }));
if (shouldSetState || value.length !== this.state.model.length) {
this.storage.setModel(ListField.getModelFromProps({ value }));
}
}

componentWillUnmount() {
Expand Down Expand Up @@ -168,9 +172,8 @@ export class ListField extends React.Component {
}

removeListElement(key) {
const model = Array.from(this.state.model);
const model = cloneDeep(this.state.model);
model.splice(key, 1);
this.setState({ model });
this.props.onChange(model.map(item => item.value));
}

Expand Down
Loading

0 comments on commit 56b220f

Please sign in to comment.