diff --git a/src/List.tsx b/src/List.tsx index 0315fe21..743c012a 100644 --- a/src/List.tsx +++ b/src/List.tsx @@ -12,7 +12,7 @@ interface ListField { } interface ListOperations { - add: (defaultValue?: StoreValue) => void; + add: (defaultValue?: StoreValue, index?: number) => void; remove: (index: number | number[]) => void; move: (from: number, to: number) => void; } @@ -59,13 +59,31 @@ const List: React.FunctionComponent = ({ name, children }) => { * Always get latest value in case user update fields by `form` api. */ const operations: ListOperations = { - add: defaultValue => { + add: (defaultValue, index?: number) => { // Mapping keys - keyManager.keys = [...keyManager.keys, keyManager.id]; - keyManager.id += 1; - const newValue = getNewValue(); - onChange([...newValue, defaultValue]); + + if (index >= 0 && index <= newValue.length) { + keyManager.keys = [ + ...keyManager.keys.slice(0, index), + keyManager.id, + ...keyManager.keys.slice(index), + ]; + onChange([...newValue.slice(0, index), defaultValue, ...newValue.slice(index)]); + } else { + if ( + process.env.NODE_ENV !== 'production' && + (index < 0 || index > newValue.length) + ) { + warning( + false, + 'The second parameter of the add function should be a valid positive number.', + ); + } + keyManager.keys = [...keyManager.keys, keyManager.id]; + onChange([...newValue, defaultValue]); + } + keyManager.id += 1; }, remove: (index: number | number[]) => { const newValue = getNewValue(); diff --git a/tests/list.test.js b/tests/list.test.js index b0d6d14b..e5798acb 100644 --- a/tests/list.test.js +++ b/tests/list.test.js @@ -321,6 +321,58 @@ describe('Form.List', () => { matchKey(0, '3'); }); + it('add when the second param is number', () => { + let operation; + const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const [wrapper, getList] = generateForm((fields, opt) => { + operation = opt; + return ( +
+ {fields.map(field => ( + + + + ))} +
+ ); + }); + + act(() => { + operation.add(); + }); + act(() => { + operation.add('1', 2); + }); + + act(() => { + operation.add('2', -1); + }); + + expect(errorSpy).toHaveBeenCalledWith( + 'Warning: The second parameter of the add function should be a valid positive number.', + ); + errorSpy.mockRestore(); + + wrapper.update(); + expect(getList().find(Field).length).toEqual(3); + expect(form.getFieldsValue()).toEqual({ + list: [undefined, '1', '2'], + }); + + act(() => { + operation.add('0', 0); + }); + act(() => { + operation.add('4', 3); + }); + + wrapper.update(); + expect(getList().find(Field).length).toEqual(5); + expect(form.getFieldsValue()).toEqual({ + list: ['0', undefined, '1', '4', '2'], + }); + }); + describe('validate', () => { it('basic', async () => { const [, getList] = generateForm( @@ -417,6 +469,52 @@ describe('Form.List', () => { expect(form.getFieldError(['list', 0])).toEqual(["'list.1' must be at least 5 characters"]); expect(wrapper.find('input').props().value).toEqual('test'); }); + + it('when add() second param is number', async () => { + const [wrapper, getList] = generateForm( + (fields, { add }) => ( +
+ {fields.map(field => ( + + + + ))} + +
+ ), + { + initialValues: { list: ['test1', 'test2', 'test3'] }, + }, + ); + + expect(wrapper.find(Input)).toHaveLength(3); + await changeValue(getField(getList(), 0), ''); + expect(form.getFieldError(['list', 0])).toEqual(["'list.0' is required"]); + + wrapper.find('.button').simulate('click'); + wrapper.find('.button1').simulate('click'); + + expect(wrapper.find(Input)).toHaveLength(5); + expect(form.getFieldError(['list', 1])).toEqual(["'list.0' is required"]); + + await changeValue(getField(getList(), 1), 'test'); + expect(form.getFieldError(['list', 1])).toEqual(["'list.1' must be at least 5 characters"]); + }); }); it('warning if children is not function', () => {