From ae512b515673eca319c9fedbdf569dc049a83061 Mon Sep 17 00:00:00 2001 From: Pierre Besson Date: Wed, 15 Jun 2016 16:12:06 +0200 Subject: [PATCH 01/13] [test] Fix middleware. --- README.md | 5 +++++ src/middlewares/__tests__/form-middleware-test.js | 8 +++++++- .../__tests__/form-middleware-validation.js | 4 ++-- src/middlewares/field.js | 10 +++++++--- src/middlewares/form.js | 11 +++++++++-- src/middlewares/validations.js | 11 ++++------- 6 files changed, 34 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 7ec366f..f300efb 100644 --- a/README.md +++ b/README.md @@ -56,3 +56,8 @@ export default connectToDomains(MyChildComponentWhoNeedsInformationsFromTheDomai ## Explainations Provider(informationsToPassToTheComponentsTree) => Tree => connectToInformations(Child) => The child gets this information in its props. + + +// todo: + +- [] Check the Provider chain presence (form needs metadata) diff --git a/src/middlewares/__tests__/form-middleware-test.js b/src/middlewares/__tests__/form-middleware-test.js index 13bb104..c75b721 100644 --- a/src/middlewares/__tests__/form-middleware-test.js +++ b/src/middlewares/__tests__/form-middleware-test.js @@ -40,12 +40,18 @@ describe('The form middleware', () => { nextSpy.reset(); dispatchSpy.reset(); }) + describe('when no store is given tho the middleware ', ()=>{ + it('should throw an error', () =>{ + expect(() => formMiddleware()(nextSpy)({type: 'lol'})) + .to.throw('FORM_MIDDLEWARE: You middleware needs a redux store.'); + }) + }); describe('when a random action is passed', () => { const randomAction = { type: 'LOL' }; it('should just pass the action to next', () => { - formMiddleware(null)(nextSpy)(randomAction); + formMiddleware(store)(nextSpy)(randomAction); expect(nextSpy).to.have.been.callCount(1); expect(nextSpy).to.have.been.calledWith(randomAction); }); diff --git a/src/middlewares/__tests__/form-middleware-validation.js b/src/middlewares/__tests__/form-middleware-validation.js index e62466b..2b949cb 100644 --- a/src/middlewares/__tests__/form-middleware-validation.js +++ b/src/middlewares/__tests__/form-middleware-validation.js @@ -4,7 +4,7 @@ import {filterNonValidatedFields} from '../validations'; const FIELDS_CONFIG = JSON.parse('{"fields":[{"valid":true,"error":false,"active":true,"dirty":false,"loading":false,"saving":false,"name":"city","entityPath":"address","dataSetValue":"Kubland","rawInputValue":"Kubland","formattedInputValue":"Kubland - formaté"},{"valid":true,"error":false,"active":true,"dirty":false,"loading":false,"saving":false,"name":"uuid","entityPath":"user","dataSetValue":"1234","rawInputValue":"1234","formattedInputValue":"1234"},{"valid":true,"error":false,"active":true,"dirty":false,"loading":false,"saving":false,"name":"lastName","entityPath":"user","dataSetValue":"De Libercourt","rawInputValue":"De Libercourt","formattedInputValue":"De Libercourt - formaté"},{"valid":true,"error":false,"active":true,"dirty":false,"loading":false,"saving":false,"name":"childs","entityPath":"user","dataSetValue":[{"firstName":"FirstChildOne","lastName":"LastChildOne"},{"firstName":"FirstChildTwo","lastName":"LastChildTwo"}],"rawInputValue":[{"firstName":"FirstChildOne","lastName":"LastChildOne"},{"firstName":"FirstChildTwo","lastName":"LastChildTwo"}],"formattedInputValue":[{"firstName":"FirstChildOne","lastName":"LastChildOne"},{"firstName":"FirstChildTwo","lastName":"LastChildTwo"}],"redirectEntityPath":"child"},{"valid":true,"error":false,"active":true,"dirty":false,"loading":false,"saving":false,"name":"uuid","entityPath":"address","dataSetValue":"1234","rawInputValue":"1234","formattedInputValue":"1234"}],"nonValidatedFields":["user.firstName",{"user.childs":["firstName"]}]}') -describe.only('Form: validation', () => { +describe('Form: validation', () => { it('filterNonValidatedInListField', () => { const FIELD_TO_VALIDATE = [ {"valid":true,"error":false,"active":true,"dirty":false,"loading":false,"saving":false,"name":"city","entityPath":"address","dataSetValue":"Kubland","rawInputValue":"Kubland","formattedInputValue":"Kubland - formaté"}, @@ -24,7 +24,7 @@ describe.only('Form: validation', () => { filterNonValidatedInListField(FIELDS_CONFIG.fields,FIELDS_CONFIG.nonValidatedFields ) ).to.deep.equal(FIELD_TO_VALIDATE)*/ const CANDIDATE_FIELD_TO_VALIDATE = filterNonValidatedFields(FIELDS_CONFIG.fields,FIELDS_CONFIG.nonValidatedFields ); - console.log(CANDIDATE_FIELD_TO_VALIDATE); + //console.log(CANDIDATE_FIELD_TO_VALIDATE); expect( CANDIDATE_FIELD_TO_VALIDATE ).to.deep.equal(FIELD_TO_VALIDATE) diff --git a/src/middlewares/field.js b/src/middlewares/field.js index 073053b..e5407cd 100644 --- a/src/middlewares/field.js +++ b/src/middlewares/field.js @@ -34,11 +34,15 @@ const fieldMiddleware = store => next => action => { case CREATE_FORM: next({ ...action, - fields: action.fields.map(field => ({ + fields: action.fields.map(field => { + const redirectEntityPath = getRedirectEntityPath(field.dataSetValue, field.entityPath, field.name, definitions, domains); + const _rentityPath = redirectEntityPath ? {redirectEntityPath} : {}; + return { ...field, formattedInputValue: formatValue(field.dataSetValue, field.entityPath, field.name, definitions, domains), - redirectEntityPath : getRedirectEntityPath(field.dataSetValue, field.entityPath, field.name, definitions, domains) - })) + ...redirectEntityPath + } + }) }); break; default: diff --git a/src/middlewares/form.js b/src/middlewares/form.js index cbc1088..288ada4 100644 --- a/src/middlewares/form.js +++ b/src/middlewares/form.js @@ -1,13 +1,17 @@ import {CREATE_FORM, SYNC_FORM_ENTITIES, VALIDATE_FORM} from '../actions/form'; import {SUCCESS} from '../actions/entity-actions-builder'; -import {__fake_focus_core_validation_function__, filterNonValidatedFields, validateField, validateFieldArray, formatValue,getRedirectEntityPath} from './validations' +import {__fake_focus_core_validation_function__, filterNonValidatedFields, validateField, validateFieldArray, formatValue} from './validations' import {syncFormsEntity, toggleFormEditing, setFormToSaving} from '../actions/form'; import get from 'lodash/get'; import map from 'lodash/map'; import find from 'lodash/find'; - +const FORM_MIDDLEWARE = 'FORM_MIDDLEWARE'; const formMiddleware = store => next => action => { + + if(!store || !store.getState){ throw new Error(`${FORM_MIDDLEWARE}: You middleware needs a redux store.`)} + if(action.syncForm) { + // The action requires the forms to sync themselves with the dataset, let's do it // Grab the new state, to have the updates on the dataset const newState = next(action); @@ -45,6 +49,9 @@ const formMiddleware = store => next => action => { // Continue with the rest of the redux flow return newState; }else { + if(store === null){ + throw new Error('Store not defined') + } const {dataset, forms, definitions, domains} = store.getState(); const {formKey, nonValidatedFields,entityPathArray } = action; switch(action.type) { diff --git a/src/middlewares/validations.js b/src/middlewares/validations.js index 6f00c42..02e8900 100644 --- a/src/middlewares/validations.js +++ b/src/middlewares/validations.js @@ -1,8 +1,6 @@ import identity from 'lodash/identity'; -import {INPUT_CHANGE, INPUT_BLUR, INPUT_BLUR_LIST} from '../actions/input'; -import {inputError, inputErrorList} from '../actions/input'; -import {CREATE_FORM, VALIDATE_FORM, SYNC_FORMS_ENTITY, SYNC_FORM_ENTITIES} from '../actions/form'; -import {setFormToSaving} from '../actions/form'; +import {INPUT_CHANGE, INPUT_BLUR, INPUT_BLUR_LIST,inputError, inputErrorList} from '../actions/input'; +import {CREATE_FORM, VALIDATE_FORM, SYNC_FORMS_ENTITY, SYNC_FORM_ENTITIES, setFormToSaving} from '../actions/form'; import {PENDING} from '../actions/entity-actions-builder'; import find from 'lodash/find'; import isUndefined from 'lodash/isUndefined'; @@ -207,8 +205,7 @@ export const formatValue = (value, entityPath, fieldName, definitions, domains) export const getRedirectEntityPath = (value, entityPath, fieldName, definitions, domains) => { - if(definitions[entityPath][fieldName].redirect){ + if(definitions && definitions[entityPath] && definitions[entityPath][fieldName].redirect){ return definitions[entityPath][fieldName].redirect; - }else return; - + } return; } From 2ddf12843c696c466a51c84d6203da8d5756c9a6 Mon Sep 17 00:00:00 2001 From: Pierre Besson Date: Thu, 16 Jun 2016 09:37:21 +0200 Subject: [PATCH 02/13] [validation] Improve focus core fake function. (no longer random ...) --- src/middlewares/_fake-validate.js | 107 ++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 src/middlewares/_fake-validate.js diff --git a/src/middlewares/_fake-validate.js b/src/middlewares/_fake-validate.js new file mode 100644 index 0000000..29330dd --- /dev/null +++ b/src/middlewares/_fake-validate.js @@ -0,0 +1,107 @@ +import isNull from 'lodash/isNull'; +import isUndefined from 'lodash/isUndefined'; +//Dependency + +const translate = (str, params) => `${str} ${JSON.stringify(params)}`; + +//Focus validators +const emailValidation = v => typeof v === 'string'; +const numberValidation = v => typeof v === 'number'; +const stringLength = v => typeof v === 'string'; +const dateValidation = v => typeof v === 'string'; + +/** +* Validae a property given validators. +* @param {object} property - Property to validate which should be as follows: `{name: "field_name",value: "field_value", validators: [{...}] }`. +* @param {array} validators - The validators to apply on the property. +* @return {object} - The validation status. +*/ +function validate(property, validators) { + //console.log("validate", property, validators); + let errors = [], res, validator; + if (validators) { + for (let i = 0, _len = validators.length; i < _len; i++) { + validator = validators[i]; + res = validateProperty(property, validator); + if (!isNull(res) && !isUndefined(res)) { + errors.push(res); + } + } + } + //Check what's the good type to return. + return { + name: property.name, + value: property.value, + isValid: 0 === errors.length, + errors: errors + }; +} + +/** +* Validate a property. +* @param {object} property - The property to validate. +* @param {function} validator - The validator to apply. +* @return {object} - The property validation status. +*/ +function validateProperty(property, validator) { + let isValid; + if (!validator) { + return void 0; + } + if (!property) { + return void 0; + } + const {value} = property; + const {options} = validator; + const isValueNullOrUndefined = isNull(value) || isUndefined(value ); + isValid = (() => { + switch (validator.type) { + case 'required': + const prevalidString = '' === property.value ? false : true; + const prevalidDate = true; + return true === validator.value ? (!isNull(value) && !isUndefined(value) && prevalidString && prevalidDate) : true; + case 'regex': + if (isValueNullOrUndefined) { + return true; + } + return validator.value.test(value); + case 'email': + if (isValueNullOrUndefined) { + return true; + } + return emailValidation(value, options); + case 'number': + return numberValidation(value, options); + case 'string': + const stringToValidate = value || ''; + return stringLength(stringToValidate, options); + case 'date': + return dateValidation(value, options); + case 'function': + return validator.value(value, options); + default: + return void 0; + } + })(); + if (isUndefined(isValid) || isNull(isValid)) { + console.warn(`The validator of type: ${validator.tye} is not defined`); + } else if (false === isValid) { + //Add the name of the property. + return getErrorLabel(validator.type, property.modelName + '.' + property.name, options); //"The property " + property.name + " is invalid."; + } +} +/** + * Get the error label from a type and a field name. + * @param {string} type - The type name. + * @param {string} fieldName - The field name. + * @param {object} options - The options to put such as the translationKey which could be defined in the domain. + * @return {string} The formatted error. + */ +function getErrorLabel(type, fieldName, options = {}) { + options = options || {}; + const translationKey = options.translationKey ? options.translationKey : `domain.validation.${type}`; + const opts = {fieldName: translate(fieldName), ...options}; + return translate(translationKey, opts); +} + +export default validate; From 4e3334db954224a7ba3d1326ba7597cfb530e8de Mon Sep 17 00:00:00 2001 From: Pierre Besson Date: Thu, 16 Jun 2016 17:02:23 +0200 Subject: [PATCH 03/13] [validation] Add test also change the redirect API. --- index.js | 2 +- package.json | 4 +- src/example/config.js | 9 +-- .../__tests__/field-middleware-test.js | 62 +++++++++++++++++ .../__tests__/form-middleware-validation.js | 67 ++++++++++++++++--- src/middlewares/field.js | 7 +- src/middlewares/validations.js | 34 +++++++--- 7 files changed, 157 insertions(+), 28 deletions(-) create mode 100644 src/middlewares/__tests__/field-middleware-test.js diff --git a/index.js b/index.js index ea9d306..ba3cd4e 100644 --- a/index.js +++ b/index.js @@ -9,4 +9,4 @@ exports.default = function () { }; module.exports = exports['default']; -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImJ1aWxkZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O2tCQUFlLFlBQU07QUFDbkIsVUFBUSxHQUFSLGlDQURtQjtDQUFOIiwiZmlsZSI6ImJ1aWxkZXIuanMiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZGVmYXVsdCAoKSA9PiB7XG4gIGNvbnNvbGUubG9nKGBGb2N1cyByZWR1eC4uLi4gc28gZ3JlYXQuLi4uYClcbn1cbiJdfQ== \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImJ1aWxkZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O2tCQUFlLFlBQU07QUFDbkIsVUFBUSxHQUFSO0FBQ0QsQyIsImZpbGUiOiJidWlsZGVyLmpzIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgKCkgPT4ge1xuICBjb25zb2xlLmxvZyhgRm9jdXMgcmVkdXguLi4uIHNvIGdyZWF0Li4uLmApXG59XG4iXX0= \ No newline at end of file diff --git a/package.json b/package.json index 7400164..6ce3aa6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "focus-redux", - "version": "0.1.1", + "version": "0.2.0-beta.2", "description": "Form, data behaviours", "main": "src/index.js", "files": [ @@ -12,7 +12,7 @@ "start": "better-npm-run dev-server", "build:node": "better-npm-run babelify", "build": "npm run build:node", - "publish": "npm run build" + "prepublish": "npm run build" }, "betterScripts": { diff --git a/src/example/config.js b/src/example/config.js index 648617e..e1ba91f 100644 --- a/src/example/config.js +++ b/src/example/config.js @@ -9,7 +9,8 @@ export const definitions = { lastName: { domain: 'DO_DON_DIEGO', isRequired: true}, date: { domain: 'DO_DATE', isRequired: false}, civility: { domain: 'DO_CIVILITE', isRequired: true}, - childs : {redirect: 'child'} + // TODO: ['childs'] ? + childs : {redirect: ['child']} }, child : { firstName : { domain: 'DO_RODRIGO', isRequired: false}, @@ -25,7 +26,7 @@ export const definitions = { export const domains = { DO_RODRIGO: { type: 'text', - validator: [{ + validators: [{ type: 'string', options: { maxLength: 50 @@ -35,7 +36,7 @@ export const domains = { }, DO_DON_DIEGO: { type: 'text', - validator: [{ + validators: [{ type: 'string', options: { maxLength: 200 @@ -48,7 +49,7 @@ export const domains = { }, DO_CIVILITE: { type: 'text', - validator: [{ + validators: [{ type: 'string', options: { maxLength: 200 diff --git a/src/middlewares/__tests__/field-middleware-test.js b/src/middlewares/__tests__/field-middleware-test.js new file mode 100644 index 0000000..9f64e5d --- /dev/null +++ b/src/middlewares/__tests__/field-middleware-test.js @@ -0,0 +1,62 @@ +import fieldMiddleware from '../field'; +import { + INPUT_CHANGE, + INPUT_BLUR, + INPUT_ERROR, + INPUT_BLUR_LIST, + INPUT_ERROR_LIST +} from '../../actions/input' +import {definitions, domains} from '../../example/config'; +describe('The field middleware', () => { + const getStateSpy = sinon.spy(); + const nextSpy = sinon.spy(); + const dispatchSpy = sinon.spy(); + const store = { + getState: () => { + getStateSpy(); + return { + dataset: { + user: { + data: { + firstName: 'Joe', + lastName: 'Lopez' + }, + loading: true, + saving: false + } + }, + definitions, + domains, + forms: [{ + formKey: 'formKey', + entityPathArray: ['user'], + fields: [{ + name: 'firstName', + dataSetValue: 'Joe', + entityPath: 'user', + loading: false, + saving: false + }] + }] + } + }, + dispatch: dispatchSpy + }; + beforeEach(() => { + getStateSpy.reset(); + nextSpy.reset(); + dispatchSpy.reset(); + }); + describe('when no store is given tho the middleware ', ()=>{ + it('should throw an error', () =>{ + expect(() => fieldMiddleware()(nextSpy)({type: 'lol'})) + .to.throw('FIELD_MIDDLEWARE: Your middleware needs a redux store.'); + }); + it('when an INPUT_BLUR action is passed', () => { + fieldMiddleware(store)(nextSpy)({type: INPUT_BLUR}) + }); + it('when an INPUT_BLUR_LIST action is passed', () => {}); + it('when an INPUT_CHANGE action is passed', () => {}); + it('when an SYNC_FORM_ENTITIES action is passed', () => {}); + }); +}); diff --git a/src/middlewares/__tests__/form-middleware-validation.js b/src/middlewares/__tests__/form-middleware-validation.js index 2b949cb..652abdb 100644 --- a/src/middlewares/__tests__/form-middleware-validation.js +++ b/src/middlewares/__tests__/form-middleware-validation.js @@ -1,10 +1,64 @@ -import {filterNonValidatedFields} from '../validations'; - +import {filterNonValidatedFields, validateField} from '../validations'; +import {definitions, domains} from '../../example/config'; +import {INPUT_ERROR} from '../../actions/input'; const FIELDS_CONFIG = JSON.parse('{"fields":[{"valid":true,"error":false,"active":true,"dirty":false,"loading":false,"saving":false,"name":"city","entityPath":"address","dataSetValue":"Kubland","rawInputValue":"Kubland","formattedInputValue":"Kubland - formaté"},{"valid":true,"error":false,"active":true,"dirty":false,"loading":false,"saving":false,"name":"uuid","entityPath":"user","dataSetValue":"1234","rawInputValue":"1234","formattedInputValue":"1234"},{"valid":true,"error":false,"active":true,"dirty":false,"loading":false,"saving":false,"name":"lastName","entityPath":"user","dataSetValue":"De Libercourt","rawInputValue":"De Libercourt","formattedInputValue":"De Libercourt - formaté"},{"valid":true,"error":false,"active":true,"dirty":false,"loading":false,"saving":false,"name":"childs","entityPath":"user","dataSetValue":[{"firstName":"FirstChildOne","lastName":"LastChildOne"},{"firstName":"FirstChildTwo","lastName":"LastChildTwo"}],"rawInputValue":[{"firstName":"FirstChildOne","lastName":"LastChildOne"},{"firstName":"FirstChildTwo","lastName":"LastChildTwo"}],"formattedInputValue":[{"firstName":"FirstChildOne","lastName":"LastChildOne"},{"firstName":"FirstChildTwo","lastName":"LastChildTwo"}],"redirectEntityPath":"child"},{"valid":true,"error":false,"active":true,"dirty":false,"loading":false,"saving":false,"name":"uuid","entityPath":"address","dataSetValue":"1234","rawInputValue":"1234","formattedInputValue":"1234"}],"nonValidatedFields":["user.firstName",{"user.childs":["firstName"]}]}') -describe('Form: validation', () => { +describe.only('Form: validation', () => { + describe('when the validateField is called', ()=> { + const dispatchSpy = sinon.spy(); + beforeEach(() => { + dispatchSpy.reset(); + }) + describe('with a simple field', ()=> { + it('should validate the name given correct defintion and value' , ()=>{ + const validationResult = validateField(definitions, domains, 'formBalec', 'user', 'firstName', 'NewName', dispatchSpy); + expect(validationResult).to.be.true; + expect(dispatchSpy).to.have.callCount(0); + }); + it('should dispatch an invalid the name given correct defintion and value' , ()=>{ + const validationResult = validateField(definitions, domains, 'formBalec', 'user', 'firstName', 12, dispatchSpy); + expect(validationResult).to.be.false; + expect(dispatchSpy).to.have.callCount(1); + const dispatchArgs = dispatchSpy.lastCall.args[0]; + expect(dispatchArgs.type = INPUT_ERROR); + expect(dispatchArgs.formKey = 'formBalec'); + expect(dispatchArgs.entityPath = 'user'); + expect(dispatchArgs.fieldName = 'firstName'); + expect(dispatchArgs.error.length > 0 ); + }); + }); + describe('with a list field', ()=> { + it('should validate the name given correct defintion and value' , ()=>{ + const newListValue = [ + {firstName:'FirstChildOne',lastName:'LastChildOne'}, + {firstName:'FirstChildTwo',lastName:'LastChildTwo'} + ]; + const validationResult = validateField(definitions, domains, 'formBalec', 'user', 'childs', newListValue, dispatchSpy); + expect(validationResult).to.be.true; + expect(dispatchSpy).to.have.callCount(0); + }); + it('should throw an error when the data type is not an array in case of a redirect', () => { + const WRONG_DEFINITION_ERROR = ` MIDDLEWARES_FIELD_VALIDATION: Your field childs in the entity user don't have a domain, you may have an array field which have a **redirect** property in it`; + const wrongDefinition = () => validateField(definitions, domains, 'formBalec', 'user', 'childs', 12, dispatchSpy); + expect(wrongDefinition).to.throw('') + }); + it('should throw an error when the redirect is incorrect'); + it('should dispatch an invalid the name given correct defintion and value' , ()=>{ + const validationResult = validateField(definitions, domains, 'formBalec', 'user', 'firstName', 12, dispatchSpy); + expect(validationResult).to.be.false; + expect(dispatchSpy).to.have.callCount(1); + const dispatchArgs = dispatchSpy.lastCall.args[0]; + expect(dispatchArgs.type = INPUT_ERROR); + expect(dispatchArgs.formKey = 'formBalec'); + expect(dispatchArgs.entityPath = 'user'); + expect(dispatchArgs.fieldName = 'firstName'); + expect(dispatchArgs.error.length > 0 ); + }); + }); + }) + it('filterNonValidatedInListField', () => { const FIELD_TO_VALIDATE = [ {"valid":true,"error":false,"active":true,"dirty":false,"loading":false,"saving":false,"name":"city","entityPath":"address","dataSetValue":"Kubland","rawInputValue":"Kubland","formattedInputValue":"Kubland - formaté"}, @@ -17,14 +71,7 @@ describe('Form: validation', () => { {"valid":true,"error":false,"active":true,"dirty":false,"loading":false,"saving":false,"name":"uuid","entityPath":"address","dataSetValue":"1234","rawInputValue":"1234","formattedInputValue":"1234"} ]; - - - - /*expect( - filterNonValidatedInListField(FIELDS_CONFIG.fields,FIELDS_CONFIG.nonValidatedFields ) - ).to.deep.equal(FIELD_TO_VALIDATE)*/ const CANDIDATE_FIELD_TO_VALIDATE = filterNonValidatedFields(FIELDS_CONFIG.fields,FIELDS_CONFIG.nonValidatedFields ); - //console.log(CANDIDATE_FIELD_TO_VALIDATE); expect( CANDIDATE_FIELD_TO_VALIDATE ).to.deep.equal(FIELD_TO_VALIDATE) diff --git a/src/middlewares/field.js b/src/middlewares/field.js index e5407cd..4a35fec 100644 --- a/src/middlewares/field.js +++ b/src/middlewares/field.js @@ -10,8 +10,9 @@ import isNull from 'lodash/isNull'; import isEmpty from 'lodash/isEmpty'; import mapKeys from 'lodash/mapKeys'; import isArray from 'lodash/lang'; - +const FIELD_MIDDLEWARE = 'FIELD_MIDDLEWARE'; const fieldMiddleware = store => next => action => { + if(!store || !store.getState){ throw new Error(`${FIELD_MIDDLEWARE}: Your middleware needs a redux store.`)} const {forms, definitions, domains} = store.getState(); switch(action.type) { case INPUT_BLUR: @@ -36,11 +37,11 @@ const fieldMiddleware = store => next => action => { ...action, fields: action.fields.map(field => { const redirectEntityPath = getRedirectEntityPath(field.dataSetValue, field.entityPath, field.name, definitions, domains); - const _rentityPath = redirectEntityPath ? {redirectEntityPath} : {}; + const _redirectEntityPath = redirectEntityPath ? {redirectEntityPath} : {}; return { ...field, formattedInputValue: formatValue(field.dataSetValue, field.entityPath, field.name, definitions, domains), - ...redirectEntityPath + ..._redirectEntityPath } }) }); diff --git a/src/middlewares/validations.js b/src/middlewares/validations.js index 02e8900..b308d2a 100644 --- a/src/middlewares/validations.js +++ b/src/middlewares/validations.js @@ -11,7 +11,7 @@ import isArray from 'lodash/isArray'; import isString from 'lodash/isString'; import omit from 'lodash/omit'; import map from 'lodash/map'; - +import _fakeValidate from './_fake-validate'; /** * Default field formatter. Defaults to the identity function * @type {function} @@ -25,9 +25,13 @@ const MIDDLEWARES_FORM_VALIDATION = 'MIDDLEWARES_FORM_VALIDATION'; // THIS IS A MOCK FUNCTION THAT MUST BE REPLACED BY THE FOCUS CORE VALIDATION // TODO : replace this with the focus core function export const __fake_focus_core_validation_function__ = (isRequired = false, validators = [], name, rawValue) => { - const rand = Math.random(); - const isValid = rand > 0.5; - const error = isRequired && (isUndefined(rawValue) || isNull(rawValue) || isEmpty(rawValue)) ? `${name} is required` : isValid ? false : 'Random error set by a fake function'; + + const validationResult = _fakeValidate({name, value: rawValue}, validators); + const isValid = validationResult.isValid; + + //const rand = Math.random(); + //const isValid = rand > 0.5; + const error = isRequired && (isUndefined(rawValue) || isNull(rawValue) || isEmpty(rawValue)) ? `${name} is required` : isValid ? false : validationResult.errors.join(' '); return { name, value: rawValue, @@ -80,16 +84,26 @@ export const filterNonValidatedFields = (fields, nonValidatedFields) => { * @param {function} dispatch redux dispatch function * @return {boolean} the field validation status */ -export const validateField = (definitions, domains, formKey, entityPath, fieldName, value, dispatch) => { +export const validateField = (definitions, domains , formKey, entityPath, fieldName, value, dispatch) => { let {isRequired, domain: domainName, redirect} = definitions[entityPath][fieldName]; let validationResult= {}; // Redirect use to have the information of a list field if(isArray(value)){ + // If it is a list field it should redirect to a line entity definition. if(redirect){ - domainName = definitions[redirect] - value.map((element, index)=>{ + if(!isArray(redirect)){ + throw new Error(`${MIDDLEWARES_FIELD_VALIDATION}: `) + } + //TODO: feature redirect array + const FAKE_REDIRECT_INDEX = 0; + if(redirect.length > 1){ + console.warn(`${MIDDLEWARES_FIELD_VALIDATION}: This feature is not yet supported. It will be done soon.`) + } + const redirectDefinition = definitions[redirect.length === 1 ? redirect[0]: redirect[FAKE_REDIRECT_INDEX]]; + // The value is an array and we iterate over it. + value.map((element, index) => { mapKeys(element, (value, propertyNameLine) => { - const domain = domains[domainName[propertyNameLine].domain]; + const domain = domains[redirectDefinition[propertyNameLine].domain]; const fieldValid = validateFieldForList(definitions, domain , propertyNameLine, formKey, value, dispatch,index, entityPath, fieldName ); }) }) @@ -99,7 +113,11 @@ export const validateField = (definitions, domains, formKey, entityPath, fieldNa validationResult = {isValid : true} }else { + //TODO: Maybe it should be entityName + fieldName. const domain = domains[domainName]; + if(!domain){ + throw new Error(`${MIDDLEWARES_FIELD_VALIDATION}: Your field ${fieldName} in the entity ${entityPath} don't have a domain, you may have an array field which have a **redirect** property in it.`) + } validationResult = __fake_focus_core_validation_function__(isRequired, domain.validators, fieldName, value); } From eab59601c89b7d44501244f0b7e2a42da420eb7d Mon Sep 17 00:00:00 2001 From: Pierre Besson Date: Fri, 17 Jun 2016 14:36:25 +0200 Subject: [PATCH 04/13] [middleware] Add test on fields. --- .../__tests__/field-middleware-test.js | 4 +- .../__tests__/form-middleware-validation.js | 37 ++++++++++++------- src/middlewares/field.js | 9 +++-- src/middlewares/validations.js | 31 ++++++++++------ 4 files changed, 49 insertions(+), 32 deletions(-) diff --git a/src/middlewares/__tests__/field-middleware-test.js b/src/middlewares/__tests__/field-middleware-test.js index 9f64e5d..1aa8417 100644 --- a/src/middlewares/__tests__/field-middleware-test.js +++ b/src/middlewares/__tests__/field-middleware-test.js @@ -52,8 +52,8 @@ describe('The field middleware', () => { expect(() => fieldMiddleware()(nextSpy)({type: 'lol'})) .to.throw('FIELD_MIDDLEWARE: Your middleware needs a redux store.'); }); - it('when an INPUT_BLUR action is passed', () => { - fieldMiddleware(store)(nextSpy)({type: INPUT_BLUR}) + it.only('when an INPUT_BLUR action is passed', () => { + fieldMiddleware(store)(nextSpy)({type: INPUT_BLUR, entityPath: 'user', fieldName: 'firstName'}) }); it('when an INPUT_BLUR_LIST action is passed', () => {}); it('when an INPUT_CHANGE action is passed', () => {}); diff --git a/src/middlewares/__tests__/form-middleware-validation.js b/src/middlewares/__tests__/form-middleware-validation.js index 652abdb..87af436 100644 --- a/src/middlewares/__tests__/form-middleware-validation.js +++ b/src/middlewares/__tests__/form-middleware-validation.js @@ -5,7 +5,7 @@ import {INPUT_ERROR} from '../../actions/input'; const FIELDS_CONFIG = JSON.parse('{"fields":[{"valid":true,"error":false,"active":true,"dirty":false,"loading":false,"saving":false,"name":"city","entityPath":"address","dataSetValue":"Kubland","rawInputValue":"Kubland","formattedInputValue":"Kubland - formaté"},{"valid":true,"error":false,"active":true,"dirty":false,"loading":false,"saving":false,"name":"uuid","entityPath":"user","dataSetValue":"1234","rawInputValue":"1234","formattedInputValue":"1234"},{"valid":true,"error":false,"active":true,"dirty":false,"loading":false,"saving":false,"name":"lastName","entityPath":"user","dataSetValue":"De Libercourt","rawInputValue":"De Libercourt","formattedInputValue":"De Libercourt - formaté"},{"valid":true,"error":false,"active":true,"dirty":false,"loading":false,"saving":false,"name":"childs","entityPath":"user","dataSetValue":[{"firstName":"FirstChildOne","lastName":"LastChildOne"},{"firstName":"FirstChildTwo","lastName":"LastChildTwo"}],"rawInputValue":[{"firstName":"FirstChildOne","lastName":"LastChildOne"},{"firstName":"FirstChildTwo","lastName":"LastChildTwo"}],"formattedInputValue":[{"firstName":"FirstChildOne","lastName":"LastChildOne"},{"firstName":"FirstChildTwo","lastName":"LastChildTwo"}],"redirectEntityPath":"child"},{"valid":true,"error":false,"active":true,"dirty":false,"loading":false,"saving":false,"name":"uuid","entityPath":"address","dataSetValue":"1234","rawInputValue":"1234","formattedInputValue":"1234"}],"nonValidatedFields":["user.firstName",{"user.childs":["firstName"]}]}') -describe.only('Form: validation', () => { +describe('Form: validation', () => { describe('when the validateField is called', ()=> { const dispatchSpy = sinon.spy(); beforeEach(() => { @@ -23,9 +23,9 @@ describe.only('Form: validation', () => { expect(dispatchSpy).to.have.callCount(1); const dispatchArgs = dispatchSpy.lastCall.args[0]; expect(dispatchArgs.type = INPUT_ERROR); - expect(dispatchArgs.formKey = 'formBalec'); - expect(dispatchArgs.entityPath = 'user'); - expect(dispatchArgs.fieldName = 'firstName'); + expect(dispatchArgs.formKey === 'formBalec'); + expect(dispatchArgs.entityPath === 'user'); + expect(dispatchArgs.fieldName === 'firstName'); expect(dispatchArgs.error.length > 0 ); }); }); @@ -40,21 +40,30 @@ describe.only('Form: validation', () => { expect(dispatchSpy).to.have.callCount(0); }); it('should throw an error when the data type is not an array in case of a redirect', () => { - const WRONG_DEFINITION_ERROR = ` MIDDLEWARES_FIELD_VALIDATION: Your field childs in the entity user don't have a domain, you may have an array field which have a **redirect** property in it`; + const WRONG_DEFINITION_ERROR = `MIDDLEWARES_FIELD_VALIDATION: Your field childs in the entity user don't have a domain, you may have an array field which have a **redirect** property in it`; const wrongDefinition = () => validateField(definitions, domains, 'formBalec', 'user', 'childs', 12, dispatchSpy); - expect(wrongDefinition).to.throw('') + expect(wrongDefinition).to.throw(WRONG_DEFINITION_ERROR) }); - it('should throw an error when the redirect is incorrect'); - it('should dispatch an invalid the name given correct defintion and value' , ()=>{ - const validationResult = validateField(definitions, domains, 'formBalec', 'user', 'firstName', 12, dispatchSpy); + it('should throw an error when the redirect is incorrect', () => { + const WRONG_REDIRECT_ERROR = `MIDDLEWARES_FIELD_VALIDATION: Your field childs in the entity user don't have a domain, you may have an array field which have a **redirect** property in it.` + const def = {...definitions, user: {...definitions.user, redirect: 12}}; + const wrongDefinition = () => validateField(definitions, domains, 'formBalec', 'user', 'childs', 12, dispatchSpy); + expect(wrongDefinition).to.throw(WRONG_REDIRECT_ERROR) + }); + it('should dispatch an invalid the name given correct defintion and an incorrect value' , ()=>{ + const newListValue = [ + {firstName:1,lastName:'LastChildOne'}, + {firstName:'FirstChildTwo',lastName: 'test'} + ]; + const validationResult = validateField(definitions, domains, 'formBalec', 'user', 'childs', newListValue, dispatchSpy); expect(validationResult).to.be.false; - expect(dispatchSpy).to.have.callCount(1); + expect(dispatchSpy).to.have.callCount(2); const dispatchArgs = dispatchSpy.lastCall.args[0]; expect(dispatchArgs.type = INPUT_ERROR); - expect(dispatchArgs.formKey = 'formBalec'); - expect(dispatchArgs.entityPath = 'user'); - expect(dispatchArgs.fieldName = 'firstName'); - expect(dispatchArgs.error.length > 0 ); + expect(dispatchArgs.formKey === 'formBalec'); + expect(dispatchArgs.entityPath === 'user'); + expect(dispatchArgs.fieldName === 'firstName'); + // expect(dispatchArgs.error.length > 0 ); }); }); }) diff --git a/src/middlewares/field.js b/src/middlewares/field.js index 4a35fec..b458866 100644 --- a/src/middlewares/field.js +++ b/src/middlewares/field.js @@ -11,10 +11,12 @@ import isEmpty from 'lodash/isEmpty'; import mapKeys from 'lodash/mapKeys'; import isArray from 'lodash/lang'; const FIELD_MIDDLEWARE = 'FIELD_MIDDLEWARE'; -const fieldMiddleware = store => next => action => { + +const fieldMiddleware = store => next => (action) => { if(!store || !store.getState){ throw new Error(`${FIELD_MIDDLEWARE}: Your middleware needs a redux store.`)} const {forms, definitions, domains} = store.getState(); switch(action.type) { + // Middleware post state processing case INPUT_BLUR: // On input blur action, validate the provided field validateField(definitions, domains, action.formKey, action.entityPath, action.fieldName, action.rawValue, store.dispatch); @@ -22,12 +24,11 @@ const fieldMiddleware = store => next => action => { case INPUT_BLUR_LIST: validateFieldArray(definitions, domains, action.formKey, action.entityPath, action.fieldName, action.rawValue, action.propertyNameLine, action.index,store.dispatch); break; - + // Middleware pre state processing case INPUT_CHANGE: next({ ...action, - formattedValue: formatValue(action.rawValue, action.entityPath, action.fieldName, definitions, domains), - redirectEntityPath : 'child' + formattedValue: formatValue(action.rawValue, action.entityPath, action.fieldName, definitions, domains) }); break; case SYNC_FORM_ENTITIES: diff --git a/src/middlewares/validations.js b/src/middlewares/validations.js index b308d2a..acf36af 100644 --- a/src/middlewares/validations.js +++ b/src/middlewares/validations.js @@ -68,7 +68,13 @@ export const filterNonValidatedFields = (fields, nonValidatedFields) => { } - +const _getRedirectDefinition = (redirect: Array, definitions: Object) => { + if(redirect.length > 1){ + console.warn(`${MIDDLEWARES_FIELD_VALIDATION}: This feature is not yet supported. It will be done soon.`) + } + const FAKE_REDIRECT_INDEX = 0; + return definitions[redirect.length === 1 ? redirect[0]: redirect[FAKE_REDIRECT_INDEX]]; +} /** @@ -92,26 +98,25 @@ export const validateField = (definitions, domains , formKey, entityPath, fieldN // If it is a list field it should redirect to a line entity definition. if(redirect){ if(!isArray(redirect)){ - throw new Error(`${MIDDLEWARES_FIELD_VALIDATION}: `) + throw new Error(`${MIDDLEWARES_FIELD_VALIDATION}: your redirect property should be an array in the definition of ${entityPath}.${fieldName}.`) } //TODO: feature redirect array - const FAKE_REDIRECT_INDEX = 0; - if(redirect.length > 1){ - console.warn(`${MIDDLEWARES_FIELD_VALIDATION}: This feature is not yet supported. It will be done soon.`) - } - const redirectDefinition = definitions[redirect.length === 1 ? redirect[0]: redirect[FAKE_REDIRECT_INDEX]]; + const redirectDefinition = _getRedirectDefinition(redirect, definitions); // The value is an array and we iterate over it. + validationResult = {isValid : true}; value.map((element, index) => { mapKeys(element, (value, propertyNameLine) => { const domain = domains[redirectDefinition[propertyNameLine].domain]; const fieldValid = validateFieldForList(definitions, domain , propertyNameLine, formKey, value, dispatch,index, entityPath, fieldName ); + if(fieldValid === false){ + validationResult.isValid = false; + } }) }) }else { - throw new Error(`${MIDDLEWARES_FIELD_VALIDATION} : You must provide a "redirect" defintions to your list field : ${fieldName}`) + throw new Error(`${MIDDLEWARES_FIELD_VALIDATION} : You must provide a "redirect" defintions to your list field : ${entityPath}.${fieldName}`) } - validationResult = {isValid : true} }else { //TODO: Maybe it should be entityName + fieldName. const domain = domains[domainName]; @@ -143,9 +148,11 @@ export const validateField = (definitions, domains , formKey, entityPath, fieldN * @return {boolean} the field validation status */ export const validateFieldForList = (definitions, domain, propertyNameLine, formKey, value, dispatch,index, entityPath, fieldNameList ) => { +// if(value === 1) throw new Error(JSON.stringify({ domain, propertyNameLine, formKey, value, index, entityPath, fieldNameList})) let validationResult= {}; - let {isRequired} = definitions[entityPath][fieldNameList]; + const {isRequired} = definitions[entityPath][fieldNameList]; validationResult = __fake_focus_core_validation_function__(isRequired, domain.validators, fieldNameList, value); + //if(value === 1) throw new Error(JSON.stringify(validationResult)); if (!validationResult.isValid ){ dispatch(inputErrorList(formKey, fieldNameList , entityPath, validationResult.error, propertyNameLine , index)); return false; @@ -203,11 +210,11 @@ export const formatValue = (value, entityPath, fieldName, definitions, domains) const entityDefinition = definitions[entityPath] || {}; const {domain: domainName = {} , redirect} = entityDefinition[fieldName] || {}; if(redirect){ - const domainName = definitions[redirect] + const redirectDefinition = _getRedirectDefinition(redirect, definitions); value = value.map((element, index)=>{ const newElement ={}; Object.keys(element).map((propertyNameLine)=> { - const domain = domains[domainName[propertyNameLine].domain]; + const domain = domains[redirectDefinition[propertyNameLine].domain]; const {formatter = defaultFormatter} = domain || {}; newElement[propertyNameLine] = formatter(element[propertyNameLine]); }) From 19b1a2545f82d815b50539f1265768df883c67dd Mon Sep 17 00:00:00 2001 From: Pierre Besson Date: Fri, 17 Jun 2016 14:40:09 +0200 Subject: [PATCH 05/13] [test] --- src/middlewares/__tests__/field-middleware-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middlewares/__tests__/field-middleware-test.js b/src/middlewares/__tests__/field-middleware-test.js index 1aa8417..23f856e 100644 --- a/src/middlewares/__tests__/field-middleware-test.js +++ b/src/middlewares/__tests__/field-middleware-test.js @@ -52,7 +52,7 @@ describe('The field middleware', () => { expect(() => fieldMiddleware()(nextSpy)({type: 'lol'})) .to.throw('FIELD_MIDDLEWARE: Your middleware needs a redux store.'); }); - it.only('when an INPUT_BLUR action is passed', () => { + it('when an INPUT_BLUR action is passed', () => { fieldMiddleware(store)(nextSpy)({type: INPUT_BLUR, entityPath: 'user', fieldName: 'firstName'}) }); it('when an INPUT_BLUR_LIST action is passed', () => {}); From 977d94fa59b1a023bb1daef3ac7fa509e68d8662 Mon Sep 17 00:00:00 2001 From: Pierre Besson Date: Fri, 17 Jun 2016 16:42:41 +0200 Subject: [PATCH 06/13] [list and line] --- src/components/line.js | 5 ++--- src/components/list.js | 7 +++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/components/line.js b/src/components/line.js index d6ccc93..4daaa41 100644 --- a/src/components/line.js +++ b/src/components/line.js @@ -1,6 +1,5 @@ import React, {PropTypes} from 'react'; - -function Line({onClick, children, fieldForLine, options,index, ...otherProps}) { +const Line = ({onClick, children, fieldForLine, options,index, ...otherProps}) => { return (
{fieldForLine('firstName', {entityPath: 'child'}, index)}
@@ -12,7 +11,7 @@ function Line({onClick, children, fieldForLine, options,index, ...otherProps}) Line.displayName = 'Line'; Line.propTypes = { - onClick: PropTypes.func.isRequired, + onClick: PropTypes.func, options: PropTypes.arrayOf(PropTypes.string) }; Line.defaultProps = { diff --git a/src/components/list.js b/src/components/list.js index 533debb..9e6b007 100644 --- a/src/components/list.js +++ b/src/components/list.js @@ -1,11 +1,10 @@ import React, {PropTypes} from 'react'; -import DefaultLineComponent from './line' -function List({onClick, fieldForLine, LineComponent = DefaultLineComponent, children, options, error, values, ...otherProps}) { +function List({onClick, fieldForLine, LineComponent, children, options, error, values, ...otherProps}) { const renderLine = () => { return (values ? values.map((element, index) => { - return + return }):
) // todo: null ? } return ( @@ -16,7 +15,7 @@ function List({onClick, fieldForLine, LineComponent = DefaultLineComponent, chil List.displayName = 'List'; List.propTypes = { - LineComponent: PropTypes.element.isRequired, + LineComponent: PropTypes.func.isRequired, fieldForLine: PropTypes.func.isRequired, onClick: PropTypes.func, options: PropTypes.arrayOf(PropTypes.string) From ff6a6b366111efc73a0d6dbc12e59f55e170bae4 Mon Sep 17 00:00:00 2001 From: Pierre Besson Date: Fri, 17 Jun 2016 18:41:57 +0200 Subject: [PATCH 07/13] [listFor] The list helper is now named listFor --- src/behaviours/field.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/behaviours/field.js b/src/behaviours/field.js index 15a719f..2ac3e20 100644 --- a/src/behaviours/field.js +++ b/src/behaviours/field.js @@ -79,9 +79,9 @@ export function connect() { function FieldConnectedComponent({_behaviours, ...otherProps}, {fieldHelpers}) { const fieldFor = fieldHelpers.fieldForBuilder(otherProps); const selectFor = fieldHelpers.fieldForBuilder(otherProps, true); - const list = fieldHelpers.fieldForBuilder( otherProps, false, true, fieldHelpers.fieldForListBuilder); + const listFor = fieldHelpers.fieldForBuilder( otherProps, false, true, fieldHelpers.fieldForListBuilder); const behaviours = {connectedToFieldHelpers: true, ..._behaviours}; - return ; + return ; } FieldConnectedComponent.displayName = `${ComponentToConnect.displayName}FieldConnected`; FieldConnectedComponent.contextTypes = FIELD_CONTEXT_TYPE; From 37b3cd76d260868a742db6a0da2cb9a79ecab742 Mon Sep 17 00:00:00 2001 From: Pierre Besson Date: Fri, 17 Jun 2016 18:43:29 +0200 Subject: [PATCH 08/13] [components] The list does a closure on the index for the line onChange. --- src/components/line.js | 4 ++-- src/components/list.js | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/line.js b/src/components/line.js index 4daaa41..47f1f30 100644 --- a/src/components/line.js +++ b/src/components/line.js @@ -2,8 +2,8 @@ import React, {PropTypes} from 'react'; const Line = ({onClick, children, fieldForLine, options,index, ...otherProps}) => { return (
-
{fieldForLine('firstName', {entityPath: 'child'}, index)}
-
{fieldForLine('lastName', {entityPath: 'child'}, index)}
+
{fieldForLine('firstName', {entityPath: 'child'})}
+
{fieldForLine('lastName', {entityPath: 'child'})}
); } diff --git a/src/components/list.js b/src/components/list.js index 9e6b007..808e562 100644 --- a/src/components/list.js +++ b/src/components/list.js @@ -4,7 +4,9 @@ import React, {PropTypes} from 'react'; function List({onClick, fieldForLine, LineComponent, children, options, error, values, ...otherProps}) { const renderLine = () => { return (values ? values.map((element, index) => { - return + // fieldFor which wrapp the index. + const lineFieldFor = (linePropertyName, lineOptions) => fieldForLine(linePropertyName, lineOptions, index) + return }):
) // todo: null ? } return ( From 702caec259fb1562ba18047a02fe05ba11cb4a88 Mon Sep 17 00:00:00 2001 From: Pierre Besson Date: Fri, 17 Jun 2016 18:44:58 +0200 Subject: [PATCH 09/13] [field middleware] Add check on field in data vs definition. --- src/middlewares/field.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/middlewares/field.js b/src/middlewares/field.js index b458866..da36b55 100644 --- a/src/middlewares/field.js +++ b/src/middlewares/field.js @@ -12,6 +12,22 @@ import mapKeys from 'lodash/mapKeys'; import isArray from 'lodash/lang'; const FIELD_MIDDLEWARE = 'FIELD_MIDDLEWARE'; +// Check the definitions givent the data. +export const _checkFieldDefinition = (fieldName: string, entityPath: string, definitions: object)=> { + if(!definitions){ + return console.warn(`${FIELD_MIDDLEWARE}: You need to provide definitions.`) + } + if(!entityPath || !fieldName){ + return console.warn(`${FIELD_MIDDLEWARE}: You need an entityPath and a fieldName.`) + } + if(!definitions[entityPath]){ + return console.warn(`${FIELD_MIDDLEWARE}: your entityPath ${entityPath} is not in your definitions`, definitions); + } + if(!definitions[entityPath][fieldName]){ + return console.warn(`${FIELD_MIDDLEWARE}: your field ${fieldName} is not in the definitions of ${entityPath}, please check the data in your store. Maybe your server response is not what you think it is.`, definitions[entityPath]); + } +} + const fieldMiddleware = store => next => (action) => { if(!store || !store.getState){ throw new Error(`${FIELD_MIDDLEWARE}: Your middleware needs a redux store.`)} const {forms, definitions, domains} = store.getState(); @@ -31,12 +47,13 @@ const fieldMiddleware = store => next => (action) => { formattedValue: formatValue(action.rawValue, action.entityPath, action.fieldName, definitions, domains) }); break; + case CREATE_FORM: case SYNC_FORM_ENTITIES: case SYNC_FORMS_ENTITY: - case CREATE_FORM: next({ ...action, fields: action.fields.map(field => { + _checkFieldDefinition(field.name, field.entityPath, definitions); const redirectEntityPath = getRedirectEntityPath(field.dataSetValue, field.entityPath, field.name, definitions, domains); const _redirectEntityPath = redirectEntityPath ? {redirectEntityPath} : {}; return { From 9fc256ccb0343bd5b5bd77aa6c3db84fdccd43b5 Mon Sep 17 00:00:00 2001 From: Pierre Besson Date: Fri, 17 Jun 2016 18:47:16 +0200 Subject: [PATCH 10/13] [middleware validation] Check redirect type --- src/middlewares/validations.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/middlewares/validations.js b/src/middlewares/validations.js index acf36af..ebd46fd 100644 --- a/src/middlewares/validations.js +++ b/src/middlewares/validations.js @@ -69,6 +69,9 @@ export const filterNonValidatedFields = (fields, nonValidatedFields) => { const _getRedirectDefinition = (redirect: Array, definitions: Object) => { + if(!isArray(redirect)){ + throw new Error(`${MIDDLEWARES_FIELD_VALIDATION}: The redirect property must be an array`); + } if(redirect.length > 1){ console.warn(`${MIDDLEWARES_FIELD_VALIDATION}: This feature is not yet supported. It will be done soon.`) } @@ -230,7 +233,7 @@ export const formatValue = (value, entityPath, fieldName, definitions, domains) export const getRedirectEntityPath = (value, entityPath, fieldName, definitions, domains) => { - if(definitions && definitions[entityPath] && definitions[entityPath][fieldName].redirect){ + if(definitions && definitions[entityPath] && definitions[entityPath][fieldName] && definitions[entityPath][fieldName].redirect){ return definitions[entityPath][fieldName].redirect; } return; } From 1b7707036cb66296881a327b8e927eb4a8790a47 Mon Sep 17 00:00:00 2001 From: Pierre Besson Date: Fri, 17 Jun 2016 18:47:40 +0200 Subject: [PATCH 11/13] [example] Update it with new fieldFor --- src/example/components/user-and-address-form.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/example/components/user-and-address-form.js b/src/example/components/user-and-address-form.js index 98b1780..6a27b8a 100644 --- a/src/example/components/user-and-address-form.js +++ b/src/example/components/user-and-address-form.js @@ -7,7 +7,7 @@ import {loadMixedAction, saveMixedAction} from '../actions/mixed-actions'; import Panel from '../../components/panel'; import compose from 'lodash/flowRight'; - +import LineComponent from '../../components/line' class UserAddressForm extends Component { componentWillMount() { const {id, load, loadMasterData} = this.props; @@ -16,11 +16,11 @@ class UserAddressForm extends Component { } render() { - const {editing, fields, fieldFor, list, selectFor} = this.props; + const {editing, fields, fieldFor, listFor, selectFor} = this.props; return ( {fieldFor('uuid', {onChange: () => {console.log(fields)}, entityPath: 'user'})} - {list('childs', {entityPath : 'user', redirectEntityPath: 'child'})} + {listFor('childs', {LineComponent, entityPath : 'user', redirectEntityPath: 'child'})} ); } From 83460349450f3d265270cc300576fcf2d4bd139d Mon Sep 17 00:00:00 2001 From: Pierre Besson Date: Fri, 17 Jun 2016 18:47:55 +0200 Subject: [PATCH 12/13] [beta] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6ce3aa6..e6b0a29 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "focus-redux", - "version": "0.2.0-beta.2", + "version": "0.2.0-beta.6", "description": "Form, data behaviours", "main": "src/index.js", "files": [ From d0f30ac074ea7b490ff4f86d9440a8fb8af1cc89 Mon Sep 17 00:00:00 2001 From: Pierre Besson Date: Fri, 17 Jun 2016 18:50:49 +0200 Subject: [PATCH 13/13] [0.2.0] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e6b0a29..770f903 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "focus-redux", - "version": "0.2.0-beta.6", + "version": "0.2.0", "description": "Form, data behaviours", "main": "src/index.js", "files": [