From 7444c15c49a1b44da40d735e8c1548d7b463d7de Mon Sep 17 00:00:00 2001 From: Geraldine Atayan Date: Fri, 26 Jan 2024 02:33:49 -0800 Subject: [PATCH] add onChange method (#37) * add onChange method * change model to form type * add tests for onChange method --- Readme.md | 5 +++-- package.json | 3 ++- sandbox-app/simple-object.tsx | 5 ++++- src/form.spec.ts | 29 ++++++++++++++++++++++++++++- src/form.ts | 7 ++++++- src/hooks.ts | 5 ++++- 6 files changed, 47 insertions(+), 7 deletions(-) diff --git a/Readme.md b/Readme.md index 67423a1..d23e900 100644 --- a/Readme.md +++ b/Readme.md @@ -721,9 +721,10 @@ return ( | Property | Details | | ------------- | ---------------------------------------------------------------------------------------------------------------- | | model | Your form model, it should be an object (can be empty). Every property will be mapped into a field. | +| validations | A validations object. | | onSubmit | Your custom submit function. It will be parsed internally and provide a onSubmit handler to call programaticaly. | | onSubmitError | A useful handler to deal with errors. | -| validations | A validations object. | +| onChange | A handler for changes in all form fields. |

 

@@ -818,7 +819,7 @@ Releases are done via GitHub Releases. Create a new release there, this will aut ### Sandbox App -There is a also a sandbox basic application to play around with the library. Use `yarn dev` to start up the parcel server, and you can find the files inside the `/sandbox-app` folder. +There is a also a sandbox basic application to play around with the library. Use `yarn start` to start up the parcel server, and you can find the files inside the `/sandbox-app` folder. #### Troubleshoot diff --git a/package.json b/package.json index 88af1a0..9eba045 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ }, "contributors": [ "Leo Gonzalez", - "Jens Ravens" + "Jens Ravens", + "Geraldine Atayan" ], "license": "MIT", "devDependencies": { diff --git a/sandbox-app/simple-object.tsx b/sandbox-app/simple-object.tsx index 2734c49..ab19f45 100644 --- a/sandbox-app/simple-object.tsx +++ b/sandbox-app/simple-object.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { useForm } from '../src'; -import { Input } from './components/Input'; interface Model { simpleObject?: { @@ -27,6 +26,10 @@ export function SimpleObject(): JSX.Element { // eslint-disable-next-line no-console console.log(model); }, + onChange: ({ model }) => { + // eslint-disable-next-line no-console + console.log(model); + }, }); return ( diff --git a/src/form.spec.ts b/src/form.spec.ts index 41d0733..2f2d1ae 100644 --- a/src/form.spec.ts +++ b/src/form.spec.ts @@ -54,20 +54,23 @@ function createForm({ onSubmit, onSubmitError, onInit, + onChange, }: { value?: Partial; validations?: Partial>; onSubmit?: (form: Form) => Promise | void; onSubmitError?: (error: Error) => void; onInit?: (form: Form) => void; + onChange?: (form: Form) => void; } = {}): Form { return new Form({ model: { ...defaultValue, ...(value || {}) }, + validations, onUpdate: tracker.onUpdate, onSubmit: onSubmit ?? tracker.onSubmit, onSubmitError, onInit, - validations, + onChange, }); } @@ -826,4 +829,28 @@ describe(Form, () => { expect(form.fields.address.dirty).toBeTruthy(); }); }); + + describe('change handler', () => { + it('invokes the onChange callback when a field changes value', () => { + const initializeAppSpy = jest.fn(); + const form = createForm({ + onChange: initializeAppSpy, + }); + form.fields.name.onChange('Freddy'); + form.fields.age.onChange(27); + expect(initializeAppSpy).toHaveBeenCalledTimes(2); + }); + + it('passes the form values on the onChange method', () => { + let storedFormValues = null; + const form = createForm({ + onChange: (form) => { + storedFormValues = form; + }, + }); + form.fields.name.onChange('George'); + form.fields.age.onChange(30); + expect(storedFormValues).toEqual(form); + }); + }); }); diff --git a/src/form.ts b/src/form.ts index cbbb94c..ca8b957 100644 --- a/src/form.ts +++ b/src/form.ts @@ -9,8 +9,9 @@ export class Form { onSubmit?: (form: Form) => void | Promise; onSubmitError: ((error: Error) => void) | undefined; #validations: MappedValidation; - #onUpdate?: () => void; #field: FieldImplementation; + #onChange?: (form: Form) => void; + #onUpdate?: () => void; constructor({ model, @@ -19,6 +20,7 @@ export class Form { onSubmit, onSubmitError, onInit, + onChange, }: { model: T; validations?: MappedValidation; @@ -26,6 +28,7 @@ export class Form { onSubmit?: (form: Form) => void | Promise; onSubmitError?: (error: Error) => void; onInit?: (form: Form) => void; + onChange?: (form: Form) => void; }) { this.#validations = validations ?? {}; this.#field = new FieldImplementation({ @@ -40,6 +43,7 @@ export class Form { this.#onUpdate = onUpdate; this.onSubmit = onSubmit; this.onSubmitError = onSubmitError; + this.#onChange = onChange; } // This method will touch every field, for the purpose of displaying the errors in the view @@ -149,6 +153,7 @@ export class Form { ) { this.submissionStatus = 'idle'; } + this.#onChange?.(this); this.#onUpdate?.(); } } diff --git a/src/hooks.ts b/src/hooks.ts index 792792f..cf3c848 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -11,6 +11,7 @@ export interface UseFormProps { onSubmit?: (form: Form) => void | Promise; onSubmitError?: (error: Error) => void; onInit?: (form: Form) => void; + onChange?: (form: Form) => void; } // This interface is what you get back from the useForm hook @@ -36,6 +37,7 @@ export function useForm({ onSubmitError, validations, onInit, + onChange, _unstableUpdateModelOnChange, }: UseFormProps): FormModel { // Using a custom hook to call a rerender on every change @@ -43,11 +45,12 @@ export function useForm({ const formRef = useRef>( new Form({ model, - onUpdate, validations, + onUpdate, onSubmit, onSubmitError, onInit, + onChange, }) ); const form = formRef.current;