From 8208d7917e8a4a423ec35413e32c6b5980767344 Mon Sep 17 00:00:00 2001 From: Wesley Date: Mon, 14 Dec 2020 19:23:29 -0500 Subject: [PATCH] added updateOnPristine option --- src/decorator.js | 48 +++++++++++++--------- src/decorator.test.js | 94 +++++++++++++++++++++++++++++++++++++++++++ src/index.d.ts | 1 + src/types.js.flow | 1 + 4 files changed, 126 insertions(+), 18 deletions(-) diff --git a/src/decorator.js b/src/decorator.js index 2509660..19281ca 100644 --- a/src/decorator.js +++ b/src/decorator.js @@ -49,25 +49,37 @@ const createDecorator = ( } } const fields = form.getRegisteredFields() - calculations.forEach(({ field, isEqual, updates }) => { - if (typeof field === 'string') { - runUpdates(field, isEqual || tripleEquals, updates) - } else { - // field is a either array or regex - const matches = Array.isArray(field) - ? name => - ~field.indexOf(name) || - field.findIndex( - f => f instanceof RegExp && (f: RegExp).test(name) - ) !== -1 - : name => (field: RegExp).test(name) - fields.forEach(fieldName => { - if (matches(fieldName)) { - runUpdates(fieldName, isEqual || tripleEquals, updates) - } - }) + calculations.forEach( + ({ field, isEqual, updates, updateOnPristine = true }) => { + if ( + (updateOnPristine && typeof field === 'string') || + (!updateOnPristine && + typeof field === 'string' && + !form.getFieldState(field).pristine) + ) { + runUpdates(field, isEqual || tripleEquals, updates) + } else if (typeof field !== 'string') { + // field is a either array or regex + const matches = Array.isArray(field) + ? name => + ~field.indexOf(name) || + field.findIndex( + f => f instanceof RegExp && (f: RegExp).test(name) + ) !== -1 + : name => (field: RegExp).test(name) + fields.forEach(fieldName => { + if ( + (updateOnPristine && matches(fieldName)) || + (!updateOnPristine && + matches(fieldName) && + !form.getFieldState(fieldName).pristine) + ) { + runUpdates(fieldName, isEqual || tripleEquals, updates) + } + }) + } } - }) + ) previousValues = values }) }, diff --git a/src/decorator.test.js b/src/decorator.test.js index 5665bd0..85422dc 100644 --- a/src/decorator.test.js +++ b/src/decorator.test.js @@ -3,6 +3,100 @@ import createDecorator from './decorator' const onSubmitMock = () => {} describe('decorator', () => { + it('should not trigger update on initialValues if updateOnPristine is false', () => { + const form = createForm({ + initialValues: { foo: 'test', bar: 'test' }, + onSubmit: onSubmitMock + }) + const spy = jest.fn() + const foo = jest.fn() + const bar = jest.fn() + form.subscribe(spy, { values: true }) + form.registerField('foo', foo, { value: true }) + form.registerField('bar', bar, { value: true }) + const decorator = createDecorator({ + field: 'foo', + updateOnPristine: false, + updates: { + bar: (fooValue) => `${fooValue}bar` + } + }) + const unsubscribe = decorator(form) + expect(typeof unsubscribe).toBe('function') + + expect(spy).toHaveBeenCalled() + expect(spy).toHaveBeenCalledTimes(1) + expect(spy.mock.calls[0][0].values).toEqual({ foo: 'test', bar: 'test' }) + + expect(foo).toHaveBeenCalled() + expect(foo).toHaveBeenCalledTimes(1) + expect(foo.mock.calls[0][0].value).toBe('test') + + expect(bar).toHaveBeenCalled() + expect(bar).toHaveBeenCalledTimes(1) + expect(bar.mock.calls[0][0].value).toBe('test') + + // change foo (should trigger calculation on bar) + form.change('foo', 'baz') + + expect(spy).toHaveBeenCalledTimes(3) + expect(spy.mock.calls[1][0].values).toEqual({ foo: 'baz', bar: 'test' }) + expect(spy.mock.calls[2][0].values).toEqual({ foo: 'baz', bar: 'bazbar' }) + + expect(foo).toHaveBeenCalledTimes(2) + expect(foo.mock.calls[1][0].value).toBe('baz') + + expect(bar).toHaveBeenCalledTimes(2) + expect(bar.mock.calls[1][0].value).toBe('bazbar') + }) + + it('should not trigger update on initialValues if updateOnPristine is false, using array of field names', () => { + const form = createForm({ + initialValues: { foo: 'test', bar: 'test' }, + onSubmit: onSubmitMock + }) + const spy = jest.fn() + const foo = jest.fn() + const bar = jest.fn() + form.subscribe(spy, { values: true }) + form.registerField('foo', foo, { value: true }) + form.registerField('bar', bar, { value: true }) + const decorator = createDecorator({ + field: ['cat', 'dog', 'rat', 'foo', 'hog'], + updateOnPristine: false, + updates: { + bar: (fooValue) => `${fooValue}bar` + } + }) + const unsubscribe = decorator(form) + expect(typeof unsubscribe).toBe('function') + + expect(spy).toHaveBeenCalled() + expect(spy).toHaveBeenCalledTimes(1) + expect(spy.mock.calls[0][0].values).toEqual({ foo: 'test', bar: 'test' }) + + expect(foo).toHaveBeenCalled() + expect(foo).toHaveBeenCalledTimes(1) + expect(foo.mock.calls[0][0].value).toBe('test') + + expect(bar).toHaveBeenCalled() + expect(bar).toHaveBeenCalledTimes(1) + expect(bar.mock.calls[0][0].value).toBe('test') + + // change foo (should trigger calculation on bar) + form.change('foo', 'baz') + + expect(spy).toHaveBeenCalledTimes(3) + expect(spy.mock.calls[1][0].values).toEqual({ foo: 'baz', bar: 'test' }) + expect(spy.mock.calls[2][0].values).toEqual({ foo: 'baz', bar: 'bazbar' }) + + expect(foo).toHaveBeenCalledTimes(2) + expect(foo.mock.calls[1][0].value).toBe('baz') + + expect(bar).toHaveBeenCalledTimes(2) + expect(bar.mock.calls[1][0].value).toBe('bazbar') + }) + it('should update one field when another changes', () => { const form = createForm({ onSubmit: onSubmitMock }) const spy = jest.fn() diff --git a/src/index.d.ts b/src/index.d.ts index b0d227b..b682aa6 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -20,6 +20,7 @@ export type Updates = UpdatesByName | UpdatesForAll export type Calculation = { field: FieldPattern, updates: Updates, + updateOnPristine?: boolean, isEqual?: (a: any, b: any) => boolean, } diff --git a/src/types.js.flow b/src/types.js.flow index 7e77e90..a8937ce 100644 --- a/src/types.js.flow +++ b/src/types.js.flow @@ -14,6 +14,7 @@ export type Updates = UpdatesByName | UpdatesForAll export type Calculation = { field: FieldPattern, + updateOnPristine?: boolean, isEqual?: (any, any) => boolean, updates: Updates }