diff --git a/docs/api-forms.md b/docs/api-forms.md index 7cf3cf224..94c728423 100644 --- a/docs/api-forms.md +++ b/docs/api-forms.md @@ -25,9 +25,9 @@ By default, the validation will take place `onSubmit`, and `onChange` **after th ##### Props: -| Name | Description | -| :-------------: | :----------------------------------------------------------------------------------------------------------------------------: | -| `onChangeModel` | Like `onChange` but for the whole model. Triggered just after `onChange` but with the next model instead of (key, value) pair. | +| Name | Description | +| :-------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| `onChangeModel` | Like `onChange` but for the whole model. Triggered just after `onChange` with the next model and information what `{ key, value, previousValue }` caused the change. `previousValue` will be `undefined` if there was no value before. | **Note:** All `ValidatedQuickForm` props are also accepted and all methods are available. In other words, that means that `AutoForm` receives all props listed on this page. diff --git a/packages/uniforms/__tests__/AutoForm.tsx b/packages/uniforms/__tests__/AutoForm.tsx index 86355ba75..87fa53bb7 100644 --- a/packages/uniforms/__tests__/AutoForm.tsx +++ b/packages/uniforms/__tests__/AutoForm.tsx @@ -67,7 +67,10 @@ describe('', () => { }); it('calls `onChangeModel`', () => { - const schema = new SimpleSchema({ a: { type: String, optional: true } }); + const schema = new SimpleSchema({ + a: { type: String, optional: true }, + b: { type: String, optional: true }, + }); const bridge = new SimpleSchema2Bridge({ schema }); render( @@ -75,11 +78,27 @@ describe('', () => { , ); - const input = screen.getByLabelText('A'); - fireEvent.change(input, { target: { value: 'a' } }); + const inputA = screen.getByLabelText('A'); + fireEvent.change(inputA, { target: { value: 'a' } }); + expect(onChangeModel).toHaveBeenLastCalledWith( + { a: 'a' }, + { key: 'a', value: 'a', previousValue: undefined }, + ); + + const inputB = screen.getByLabelText('B'); + fireEvent.change(inputB, { target: { value: 'b' } }); + expect(onChangeModel).toHaveBeenLastCalledWith( + { a: 'a', b: 'b' }, + { key: 'b', value: 'b', previousValue: undefined }, + ); + + fireEvent.change(inputB, { target: { value: 'bb' } }); + expect(onChangeModel).toHaveBeenLastCalledWith( + { a: 'a', b: 'bb' }, + { key: 'b', value: 'bb', previousValue: 'b' }, + ); - expect(onChangeModel).toHaveBeenCalledTimes(1); - expect(onChangeModel).toHaveBeenLastCalledWith({ a: 'a' }); + expect(onChangeModel).toHaveBeenCalledTimes(3); }); it('updates `changed` and `changedMap`', () => { diff --git a/packages/uniforms/src/AutoForm.tsx b/packages/uniforms/src/AutoForm.tsx index 0462b2188..fa03f5209 100644 --- a/packages/uniforms/src/AutoForm.tsx +++ b/packages/uniforms/src/AutoForm.tsx @@ -1,4 +1,5 @@ import clone from 'lodash/clone'; +import get from 'lodash/get'; import isEqual from 'lodash/isEqual'; import omit from 'lodash/omit'; import setWith from 'lodash/setWith'; @@ -15,7 +16,10 @@ import { ModelTransformMode, UnknownObject } from './types'; export type AutoFormProps = ValidatedQuickFormProps & { - onChangeModel?: (model: Model) => void; + onChangeModel?: ( + model: Model, + info: { key: string; value: unknown; previousValue: unknown }, + ) => void; }; export type AutoFormState = @@ -77,12 +81,17 @@ export function Auto(Base: Base) { } onChange(key: string, value: unknown) { + const previousValue: unknown = get(this.state.model, key); super.onChange(key, value); this.setState( state => ({ model: setWith(clone(state.model), key, value, clone) }), () => { if (this.props.onChangeModel) { - this.props.onChangeModel(this.state.model); + this.props.onChangeModel(this.state.model, { + key, + value, + previousValue, + }); } }, );