diff --git a/package-lock.json b/package-lock.json index 75af58c..462f28b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4188,9 +4188,9 @@ } }, "final-form": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/final-form/-/final-form-4.17.0.tgz", - "integrity": "sha512-/vFaHCybK89qR4E6ytKRKiDCqMNtW+H9FtEGcJQEKyLJU2znPRMmKXSEGnDHnTCkuud5fGhegLepW3+h/RvblQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/final-form/-/final-form-4.18.0.tgz", + "integrity": "sha512-0ROl7GOFRWV3Pg6FmHx5G8tCZ8jAF6laVD81e9buxEocHbWMml3fYSpKZRQIOMh6SosYtQTk/tDf/vnamksO2w==", "dev": true, "requires": { "@babel/runtime": "^7.3.1" diff --git a/package.json b/package.json index 342e964..0a1f45d 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "eslint-plugin-import": "^2.16.0", "eslint-plugin-jsx-a11y": "^6.2.1", "eslint-plugin-react": "^7.13.0", - "final-form": "^4.17.0", + "final-form": "^4.18.0", "flow-bin": "^0.102.0", "glow": "^1.2.2", "husky": "^3.0.0", @@ -73,7 +73,7 @@ "typescript": "^3.5.3" }, "peerDependencies": { - "final-form": "^4.17.0" + "final-form": "^4.18.0" }, "lint-staged": { "*.{js*,ts*,json,md,css}": [ diff --git a/src/insert.js b/src/insert.js index 4fdd2ca..30797b0 100644 --- a/src/insert.js +++ b/src/insert.js @@ -4,7 +4,7 @@ import type { MutableState, Mutator, Tools } from 'final-form' const insert: Mutator = ( [name, index, value]: any[], state: MutableState, - { changeValue }: Tools + { changeValue, resetFieldState }: Tools ) => { changeValue(state, name, (array: ?(any[])): any[] => { const copy = [...(array || [])] @@ -22,12 +22,12 @@ const insert: Mutator = ( if (fieldIndex >= index) { // inc index one higher const incrementedKey = `${name}[${fieldIndex + 1}]${tokens[2]}` - changes[incrementedKey] = state.fields[key] + changes[incrementedKey] = { ...state.fields[key] } // make copy of field state changes[incrementedKey].name = incrementedKey - changes[incrementedKey].forceUpdate = true + changes[incrementedKey].lastFieldState = undefined } if (fieldIndex === index) { - delete state.fields[key] + resetFieldState(key) } } }) diff --git a/src/insert.test.js b/src/insert.test.js index 66427d2..78cde4a 100644 --- a/src/insert.test.js +++ b/src/insert.test.js @@ -4,6 +4,7 @@ import { getIn, setIn } from 'final-form' describe('insert', () => { const getOp = (index, value) => { const changeValue = jest.fn() + const resetFieldState = jest.fn() const state = { formState: { values: { @@ -23,12 +24,13 @@ describe('insert', () => { } } } - insert(['foo', index, value], state, { changeValue }) + insert(['foo', index, value], state, { changeValue, resetFieldState }) return changeValue.mock.calls[0][2] } it('should call changeValue once', () => { const changeValue = jest.fn() + const resetFieldState = jest.fn() const state = { formState: { values: { @@ -52,7 +54,10 @@ describe('insert', () => { } } } - const result = insert(['foo', 0, 'bar'], state, { changeValue }) + const result = insert(['foo', 0, 'bar'], state, { + changeValue, + resetFieldState + }) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) @@ -86,6 +91,9 @@ describe('insert', () => { const after = mutate(before) state.formState.values = setIn(state.formState.values, name, after) || {} } + const resetFieldState = name => { + state.fields[name].touched = false + } const state = { formState: { values: { @@ -100,7 +108,7 @@ describe('insert', () => { }, 'foo[1]': { name: 'foo[1]', - touched: false, + touched: true, error: 'B Error' }, 'foo[2]': { @@ -115,7 +123,10 @@ describe('insert', () => { } } } - const returnValue = insert(['foo', 1, 'NEWVALUE'], state, { changeValue }) + const returnValue = insert(['foo', 1, 'NEWVALUE'], state, { + changeValue, + resetFieldState + }) expect(returnValue).toBeUndefined() expect(state.formState.values.foo).not.toBe(array) // copied expect(state).toEqual({ @@ -130,23 +141,29 @@ describe('insert', () => { touched: true, error: 'A Error' }, + 'foo[1]': { + name: 'foo[1]', + touched: false, + error: 'B Error', + lastFieldState: undefined + }, 'foo[2]': { name: 'foo[2]', - touched: false, + touched: true, error: 'B Error', - forceUpdate: true + lastFieldState: undefined }, 'foo[3]': { name: 'foo[3]', touched: true, error: 'C Error', - forceUpdate: true + lastFieldState: undefined }, 'foo[4]': { name: 'foo[4]', touched: false, error: 'D Error', - forceUpdate: true + lastFieldState: undefined } } }) diff --git a/src/move.js b/src/move.js index ef1e81b..3aae096 100644 --- a/src/move.js +++ b/src/move.js @@ -61,7 +61,6 @@ const move: Mutator = ( change: state.fields[destKey] && state.fields[destKey].change, blur: state.fields[destKey] && state.fields[destKey].blur, focus: state.fields[destKey] && state.fields[destKey].focus, - forceUpdate: true, lastFieldState: undefined // clearing lastFieldState forces renotification } } diff --git a/src/move.test.js b/src/move.test.js index 9517233..68ce2ad 100644 --- a/src/move.test.js +++ b/src/move.test.js @@ -97,22 +97,19 @@ describe('move', () => { name: 'foo[0]', touched: true, error: 'Error B', - lastFieldState: undefined, - forceUpdate: true + lastFieldState: undefined }, 'foo[1]': { name: 'foo[1]', touched: false, error: 'Error C', - lastFieldState: undefined, - forceUpdate: true + lastFieldState: undefined }, 'foo[2]': { name: 'foo[2]', touched: true, error: 'Error A', - lastFieldState: undefined, - forceUpdate: true + lastFieldState: undefined }, 'foo[3]': { name: 'foo[3]', @@ -176,22 +173,19 @@ describe('move', () => { name: 'foo[0]', touched: false, error: 'Error C', - lastFieldState: undefined, - forceUpdate: true + lastFieldState: undefined }, 'foo[1]': { name: 'foo[1]', touched: true, error: 'Error A', - lastFieldState: undefined, - forceUpdate: true + lastFieldState: undefined }, 'foo[2]': { name: 'foo[2]', touched: true, error: 'Error B', - lastFieldState: undefined, - forceUpdate: true + lastFieldState: undefined }, 'foo[3]': { name: 'foo[3]', @@ -281,7 +275,7 @@ describe('move', () => { name: 'foo[0].dog', touched: true, error: 'Error B Dog', - forceUpdate: true + lastFieldState: undefined }, 'foo[0].cat': { name: 'foo[0].cat', @@ -302,13 +296,13 @@ describe('move', () => { name: 'foo[2].dog', touched: true, error: 'Error A Dog', - forceUpdate: true + lastFieldState: undefined }, 'foo[2].cat': { name: 'foo[2].cat', touched: false, error: 'Error A Cat', - forceUpdate: true + lastFieldState: undefined }, 'foo[3].dog': { name: 'foo[3].dog', @@ -402,13 +396,13 @@ describe('move', () => { name: 'foo[0].dog', touched: true, error: 'Error C Dog', - forceUpdate: true + lastFieldState: undefined }, 'foo[0].cat': { name: 'foo[0].cat', touched: false, error: 'Error C Cat', - forceUpdate: true + lastFieldState: undefined }, 'foo[1].dog': { name: 'foo[1].dog', @@ -424,13 +418,13 @@ describe('move', () => { name: 'foo[2].dog', touched: true, error: 'Error B Dog', - forceUpdate: true + lastFieldState: undefined }, 'foo[2].cat': { name: 'foo[2].cat', touched: true, error: 'Error B Cat', - forceUpdate: true + lastFieldState: undefined }, 'foo[3].dog': { name: 'foo[3].dog', @@ -489,19 +483,19 @@ describe('move', () => { name: 'foo[0].dog', touched: true, error: 'Error B Dog', - forceUpdate: true + lastFieldState: undefined }, 'foo[1].dog': { name: 'foo[1].dog', touched: true, error: 'Error A Dog', - forceUpdate: true + lastFieldState: undefined }, 'foo[1].cat': { name: 'foo[1].cat', touched: false, error: 'Error A Cat', - forceUpdate: true + lastFieldState: undefined } } }) diff --git a/src/remove.js b/src/remove.js index 233cbb8..6704737 100644 --- a/src/remove.js +++ b/src/remove.js @@ -31,7 +31,7 @@ const remove: Mutator = ( const decrementedKey = `${name}[${fieldIndex - 1}]${tokens[2]}` state.fields[decrementedKey] = backup[key] state.fields[decrementedKey].name = decrementedKey - state.fields[decrementedKey].forceUpdate = true + state.fields[decrementedKey].lastFieldState = undefined } } }) diff --git a/src/remove.test.js b/src/remove.test.js index c3527d3..94ce9a9 100644 --- a/src/remove.test.js +++ b/src/remove.test.js @@ -112,13 +112,13 @@ describe('remove', () => { name: 'foo[1]', touched: true, error: 'C Error', - forceUpdate: true + lastFieldState: undefined }, 'foo[2]': { name: 'foo[2]', touched: false, error: 'D Error', - forceUpdate: true + lastFieldState: undefined }, anotherField: { name: 'anotherField', diff --git a/src/removeBatch.js b/src/removeBatch.js index d443709..3bae81e 100644 --- a/src/removeBatch.js +++ b/src/removeBatch.js @@ -50,7 +50,7 @@ const removeBatch: Mutator = ( countBelow(sortedIndexes, fieldIndex)}]${tokens[2]}` newFields[decrementedKey] = state.fields[key] newFields[decrementedKey].name = decrementedKey - newFields[decrementedKey].forceUpdate = true + newFields[decrementedKey].lastFieldState = undefined } } else { newFields[key] = state.fields[key] diff --git a/src/removeBatch.test.js b/src/removeBatch.test.js index d220129..c69209b 100644 --- a/src/removeBatch.test.js +++ b/src/removeBatch.test.js @@ -77,7 +77,7 @@ describe('removeBatch', () => { name: 'foo[0]', touched: true, error: 'First Error', - forceUpdate: true + lastFieldState: undefined } } }) @@ -133,7 +133,7 @@ describe('removeBatch', () => { name: 'foo[0]', touched: false, error: 'Second Error', - forceUpdate: true + lastFieldState: undefined } } }) @@ -228,19 +228,19 @@ describe('removeBatch', () => { name: 'foo[0]', touched: true, error: 'A Error', - forceUpdate: true + lastFieldState: undefined }, 'foo[1]': { name: 'foo[1]', touched: false, error: 'D Error', - forceUpdate: true + lastFieldState: undefined }, 'foo[2]': { name: 'foo[2]', touched: true, error: 'E Error', - forceUpdate: true + lastFieldState: undefined }, anotherField: { name: 'anotherField', diff --git a/src/shift.test.js b/src/shift.test.js index 51e7c9a..8cdd551 100644 --- a/src/shift.test.js +++ b/src/shift.test.js @@ -1,32 +1,7 @@ -import insert from './insert' +import shift from './shift' import { getIn, setIn } from 'final-form' -describe('insert', () => { - const getOp = (index, value) => { - const changeValue = jest.fn() - const state = { - formState: { - values: { - foo: ['one', 'two'] - } - }, - fields: { - 'foo[0]': { - name: 'foo[0]', - touched: true, - error: 'First Error' - }, - 'foo[1]': { - name: 'foo[1]', - touched: false, - error: 'Second Error' - } - } - } - insert(['foo', index, value], state, { changeValue }) - return changeValue.mock.calls[0][2] - } - +describe('shift', () => { it('should call changeValue once', () => { const changeValue = jest.fn() const state = { @@ -48,7 +23,7 @@ describe('insert', () => { } } } - const result = insert(['foo', 0, 'bar'], state, { changeValue }) + const result = shift(['foo'], state, { changeValue }) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) @@ -58,23 +33,24 @@ describe('insert', () => { }) it('should treat undefined like an empty array', () => { - const op = getOp(0, 'bar') + const changeValue = jest.fn() + const state = { + formState: { + values: { + foo: undefined + } + }, + fields: {} + } + const returnValue = shift(['foo'], state, { changeValue }) + expect(returnValue).toBeUndefined() + const op = changeValue.mock.calls[0][2] const result = op(undefined) expect(Array.isArray(result)).toBe(true) - expect(result.length).toBe(1) - expect(result[0]).toBe('bar') + expect(result.length).toBe(0) }) - it('should insert value into the specified index', () => { - const op = getOp(1, 'd') - const array = ['a', 'b', 'c'] - const result = op(array) - expect(result).not.toBe(array) // copied - expect(Array.isArray(result)).toBe(true) - expect(result).toEqual(['a', 'd', 'b', 'c']) - }) - - it('should increment other field data from the specified index', () => { + it('should remove first value from array and return it', () => { const array = ['a', 'b', 'c', 'd'] // implementation of changeValue taken directly from Final Form const changeValue = (state, name, mutate) => { @@ -85,7 +61,8 @@ describe('insert', () => { const state = { formState: { values: { - foo: array + foo: array, + anotherField: 42 } }, fields: { @@ -108,41 +85,45 @@ describe('insert', () => { name: 'foo[3]', touched: false, error: 'D Error' + }, + anotherField: { + name: 'anotherField', + touched: false } } } - const returnValue = insert(['foo', 1, 'NEWVALUE'], state, { changeValue }) - expect(returnValue).toBeUndefined() + const returnValue = shift(['foo'], state, { changeValue }) + expect(returnValue).toBe('a') expect(state.formState.values.foo).not.toBe(array) // copied expect(state).toEqual({ formState: { values: { - foo: ['a', 'NEWVALUE', 'b', 'c', 'd'] + foo: ['b', 'c', 'd'], + anotherField: 42 } }, fields: { 'foo[0]': { name: 'foo[0]', - touched: true, - error: 'A Error' - }, - 'foo[2]': { - name: 'foo[2]', touched: false, error: 'B Error', - forceUpdate: true + lastFieldState: undefined }, - 'foo[3]': { - name: 'foo[3]', + 'foo[1]': { + name: 'foo[1]', touched: true, error: 'C Error', - forceUpdate: true + lastFieldState: undefined }, - 'foo[4]': { - name: 'foo[4]', + 'foo[2]': { + name: 'foo[2]', touched: false, error: 'D Error', - forceUpdate: true + lastFieldState: undefined + }, + anotherField: { + name: 'anotherField', + touched: false } } }) diff --git a/src/unshift.test.js b/src/unshift.test.js index 0ddac68..cb2dd7d 100644 --- a/src/unshift.test.js +++ b/src/unshift.test.js @@ -4,6 +4,7 @@ import { getIn, setIn } from 'final-form' describe('unshift', () => { const getOp = value => { const changeValue = jest.fn() + const resetFieldState = jest.fn() const state = { formState: { values: { @@ -23,12 +24,13 @@ describe('unshift', () => { } } } - unshift(['foo', value], state, { changeValue }) + unshift(['foo', value], state, { changeValue, resetFieldState }) return changeValue.mock.calls[0][2] } it('should call changeValue once', () => { const changeValue = jest.fn() + const resetFieldState = jest.fn() const state = { formState: { values: { @@ -48,7 +50,10 @@ describe('unshift', () => { } } } - const result = unshift(['foo', 'bar'], state, { changeValue }) + const result = unshift(['foo', 'bar'], state, { + changeValue, + resetFieldState + }) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) @@ -73,6 +78,9 @@ describe('unshift', () => { const after = mutate(before) state.formState.values = setIn(state.formState.values, name, after) || {} } + const resetFieldState = name => { + state.fields[name].touched = false + } const state = { formState: { values: { @@ -97,7 +105,10 @@ describe('unshift', () => { } } } - const returnValue = unshift(['foo', 'NEWVALUE'], state, { changeValue }) + const returnValue = unshift(['foo', 'NEWVALUE'], state, { + changeValue, + resetFieldState + }) expect(returnValue).toBeUndefined() expect(state.formState.values.foo).not.toBe(array) // copied expect(state).toEqual({ @@ -107,23 +118,29 @@ describe('unshift', () => { } }, fields: { + 'foo[0]': { + name: 'foo[0]', + touched: false, + error: 'A Error', + lastFieldState: undefined + }, 'foo[1]': { name: 'foo[1]', touched: true, error: 'A Error', - forceUpdate: true + lastFieldState: undefined }, 'foo[2]': { name: 'foo[2]', touched: false, error: 'B Error', - forceUpdate: true + lastFieldState: undefined }, 'foo[3]': { name: 'foo[3]', touched: true, error: 'C Error', - forceUpdate: true + lastFieldState: undefined } } })