Skip to content
This repository was archived by the owner on Aug 23, 2022. It is now read-only.

Commit 6a242cf

Browse files
committed
Simplifying batch fields validation action + unit tests
1 parent d349ffa commit 6a242cf

File tree

4 files changed

+86
-45
lines changed

4 files changed

+86
-45
lines changed

src/action-types.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const actionTypes = {
1515
SET_TOUCHED: 'rrf/setTouched',
1616
SET_UNTOUCHED: 'rrf/setUntouched',
1717
SET_VALIDITY: 'rrf/setValidity',
18+
SET_FIELDS_VALIDITY: 'rrf/setFieldsValidity',
1819
SET_VIEW_VALUE: 'rrf/setViewValue',
1920
RESET_VALIDITY: 'rrf/resetValidity',
2021
BATCH: 'rrf/batch',

src/actions/field-actions.js

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import _get from 'lodash/get';
2-
import map from 'lodash/map';
2+
import mapValues from 'lodash/mapValues';
33

4-
import batchActions from './batch-actions';
54
import actionTypes from '../action-types';
6-
import { getValidity, getForm } from '../utils';
5+
import { getValidity, getForm, isValid } from '../utils';
76

87
const focus = model => ({
98
type: actionTypes.FOCUS,
@@ -42,6 +41,12 @@ const setValidity = (model, validity) => ({
4241
validity,
4342
});
4443

44+
const setFieldsValidity = (model, fieldsValidity) => ({
45+
type: actionTypes.SET_FIELDS_VALIDITY,
46+
model,
47+
fieldsValidity,
48+
});
49+
4550
const setErrors = (model, errors) => ({
4651
type: actionTypes.SET_ERRORS,
4752
model,
@@ -129,30 +134,26 @@ const validateErrors = (model, errorValidators) => (dispatch, getState) => {
129134
const validateFields = (model, fieldValidators, callback) => (dispatch, getState) => {
130135
const value = _get(getState(), model);
131136

132-
const validationActions = map(fieldValidators, (validator, field) => {
133-
const fieldModel = field
134-
? [model, field].join('.')
135-
: model;
137+
const fieldsValidity = mapValues(fieldValidators, (validator, field) => {
136138
const fieldValue = field
137139
? _get(value, field)
138140
: value;
139141

140142
const fieldValidity = getValidity(validator, fieldValue);
141143

142-
return setValidity(fieldModel, fieldValidity);
144+
return fieldValidity;
143145
});
144146

145147
if (callback) {
146-
validationActions.push((_, _getState) => {
147-
const form = getForm(_getState(), model);
148+
const form = getForm(getState(), model);
149+
const formValid = form ? form.valid : true;
148150

149-
if (form && form.valid) {
150-
callback();
151-
}
152-
});
151+
if (formValid && isValid(fieldsValidity)) {
152+
callback();
153+
}
153154
}
154155

155-
dispatch(batchActions.batch(model, validationActions));
156+
dispatch(setFieldsValidity(model, fieldsValidity));
156157
};
157158

158159
export default {

src/reducers/form-reducer.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import icepick from 'icepick';
44
import isBoolean from 'lodash/isBoolean';
55
import isEqual from 'lodash/isEqual';
66
import isPlainObject from 'lodash/isPlainObject';
7+
import map from 'lodash/map';
78
import mapValues from 'lodash/mapValues';
89
import toPath from 'lodash/toPath';
910
import startsWith from 'lodash/startsWith';
@@ -258,6 +259,13 @@ function _createFormReducer(model, initialState) {
258259
});
259260
}
260261

262+
case actionTypes.SET_FIELDS_VALIDITY:
263+
return map(action.fieldsValidity, (fieldValidity, field) => ({
264+
type: actionTypes.SET_VALIDITY,
265+
model: `${model}.${field}`,
266+
validity: fieldValidity,
267+
})).reduce(formReducer, state);
268+
261269
case actionTypes.SET_ERRORS: {
262270
if (isPlainObject(action.errors)) {
263271
validity = {

test/field-actions-spec.js

Lines changed: 61 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { assert } from 'chai';
22
import configureMockStore from 'redux-mock-store';
33
import thunk from 'redux-thunk';
4+
import sinon from 'sinon';
45

56
import { actions, actionTypes, formReducer, initialFieldState } from '../src';
67
import { initialFormState } from '../src/reducers/form-reducer';
@@ -1282,40 +1283,22 @@ describe('field actions', () => {
12821283
it('should set the validity of multiple fields in the same form', (done) => {
12831284
const store = mockStore(
12841285
() => ({ test: { foo: 'bar' } }),
1285-
[{
1286-
actions: [
1287-
{
1288-
model: 'test',
1289-
type: 'rrf/setValidity',
1290-
validity: true,
1291-
},
1292-
{
1293-
model: 'test.foo',
1294-
type: 'rrf/setValidity',
1295-
validity: true,
1296-
},
1297-
{
1298-
model: 'test.foo_valid',
1299-
type: 'rrf/setValidity',
1300-
validity: true,
1301-
},
1302-
{
1303-
model: 'test.foo_invalid',
1304-
type: 'rrf/setValidity',
1305-
validity: false,
1306-
},
1307-
{
1308-
model: 'test.with_keys',
1309-
type: 'rrf/setValidity',
1310-
validity: {
1286+
[
1287+
{
1288+
fieldsValidity: {
1289+
'': true,
1290+
foo: true,
1291+
foo_invalid: false,
1292+
foo_valid: true,
1293+
with_keys: {
13111294
key_invalid: false,
13121295
key_valid: true,
13131296
},
13141297
},
1315-
],
1316-
model: 'test',
1317-
type: 'rrf/batch',
1318-
}],
1298+
model: 'test',
1299+
type: actionTypes.SET_FIELDS_VALIDITY,
1300+
},
1301+
],
13191302
done);
13201303

13211304
const action = actions.validateFields('test', {
@@ -1331,5 +1314,53 @@ describe('field actions', () => {
13311314

13321315
store.dispatch(action);
13331316
});
1317+
1318+
it('should call a callback if validation passes', (done) => {
1319+
const callback = sinon.spy((val) => console.log('hey', val));
1320+
1321+
const store = mockStore(
1322+
() => ({ test: { foo: 'bar' } }),
1323+
[{
1324+
model: 'test',
1325+
type: actionTypes.SET_FIELDS_VALIDITY,
1326+
fieldsValidity: {
1327+
foo: true,
1328+
},
1329+
}],
1330+
() => {
1331+
assert.isTrue(callback.calledOnce);
1332+
done();
1333+
});
1334+
1335+
const action = actions.validateFields('test', {
1336+
foo: (val) => val === 'bar',
1337+
}, callback);
1338+
1339+
store.dispatch(action);
1340+
});
1341+
1342+
it('should NOT call a callback if validation fails', (done) => {
1343+
const callback = sinon.spy((val) => console.log('hey', val));
1344+
1345+
const store = mockStore(
1346+
() => ({ test: { foo: 'bar' } }),
1347+
[{
1348+
model: 'test',
1349+
type: actionTypes.SET_FIELDS_VALIDITY,
1350+
fieldsValidity: {
1351+
foo: false,
1352+
},
1353+
}],
1354+
() => {
1355+
assert.isTrue(callback.notCalled);
1356+
done();
1357+
});
1358+
1359+
const action = actions.validateFields('test', {
1360+
foo: (val) => val === 'invalid',
1361+
}, callback);
1362+
1363+
store.dispatch(action);
1364+
});
13341365
});
13351366
});

0 commit comments

Comments
 (0)