diff --git a/.eslintrc b/.eslintrc index 4d858d1..805d07d 100644 --- a/.eslintrc +++ b/.eslintrc @@ -50,6 +50,8 @@ "react/jsx-handler-names": 0, "react/jsx-fragments": 0, "react/no-unused-prop-types": 0, + "react/jsx-indent": ["error", 2], + "react/jsx-indent-props": ["error", 2], "import/export": 0, "max-len": [ "error", diff --git a/README.md b/README.md index 82aeb50..d1028a4 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,6 @@ Formbit is a **lightweight React state form library** designed to simplify form - [Method Types](#method-types) - [Options Types](#options-types) - [Yup Re-Exports](#yup-re-exports) - - [Deprecated Types](#deprecated-types) - [License](#license) @@ -343,167 +342,132 @@ For local development we suggest using [Yalc](https://github.com/wclr/yalc) to t ### FormbitObject -Ƭ **FormbitObject**\<`Values`\>: `Object` +Ƭ **FormbitObject**\<`T`\>: `Object` -Object returned by useFormbit() and useFormbitContextHook(). -It contains all the data and methods needed to handle the form. +The object returned by `useFormbit()` and `useFormbitContext()`. Holds the form +state and every method needed to read, mutate and validate the form. #### Type parameters | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration | Name | Type | Description | | :------ | :------ | :------ | -| `check` | [`Check`](#check)\<`Partial`\<`Values`\>\> | Checks the given json against the form schema and returns an array of errors. It returns undefined if the json is valid. | +| `check` | [`Check`](#check)\<`Partial`\<`T`\>\> | Validates `json` against the current schema; returns the errors, or undefined if valid. | | `error` | (`path`: `string`) => `string` \| `undefined` | - | -| `errors` | [`Errors`](#errors) | Object including all the registered error messages since the last validation. Errors are stored using the same path of the corresponding form values. **`Example`** If the form object has this structure: ```json { "age": 1 } ``` and age is a non valid field, errors object will look like this ```json { "age": "Age must be greater then 18" } ``` | -| `form` | `Partial`\<`Values`\> | Object containing the updated form. | -| `initialize` | [`Initialize`](#initialize)\<`Values`\> | Initialize the form with new initial values. | -| `isDirty` | `boolean` | Returns true if the form is Dirty (user already interacted with the form), false otherwise. | +| `errors` | [`Errors`](#errors) | Error messages registered since the last validation, keyed by the value's dot-path. **`Example`** ```ts form: { age: 1 } errors: { age: "Age must be greater than 18" } ``` | +| `form` | `Partial`\<`T`\> | The current form values. Partial: fields may be missing until validated. | +| `initialize` | [`Initialize`](#initialize)\<`T`\> | Re-initializes the form with new initial values. | +| `isDirty` | `boolean` | True once the user has interacted with the form. | | `isFormInvalid` | () => `boolean` | - | | `isFormValid` | () => `boolean` | - | | `liveValidation` | (`path`: `string`) => ``true`` \| `undefined` | - | -| `remove` | [`Remove`](#remove)\<`Values`\> | This method updates the form state deleting value, setting isDirty to true. After writing, it validates all the paths contained into pathsToValidate (if any) and all the fields that have the live validation active. | -| `removeAll` | [`RemoveAll`](#removeall)\<`Values`\> | This method updates the form state deleting multiple values, setting isDirty to true. | +| `remove` | [`Remove`](#remove)\<`T`\> | Removes the value at `path`, sets `isDirty`, then validates `pathsToValidate` plus every live-validated field. | +| `removeAll` | [`RemoveAll`](#removeall)\<`T`\> | Removes every given path, sets `isDirty`, then validates `pathsToValidate` plus every live-validated field. | | `resetForm` | () => `void` | - | -| `setError` | [`SetError`](#seterror) | Set a message (value) to the given error path. | -| `setSchema` | [`SetSchema`](#setschema)\<`Values`\> | Override the current schema with the given one. | -| `submitForm` | [`SubmitForm`](#submitform)\<`Values`\> | Perform a validation against the current form object, and execute the successCallback if the validation passes, otherwise it executes the errorCallback. | -| `validate` | [`Validate`](#validate)\<`Values`\> | This method only validates the specified path. Does not check for fields that have the live validation active. | -| `validateAll` | [`ValidateAll`](#validateall)\<`Values`\> | This method only validates the specified paths. Does not check for fields that have the live validation active. | -| `validateForm` | [`ValidateForm`](#validateform)\<`Partial`\<`Values`\>\> | This method validates the entire form and sets the corresponding errors if any. | -| `write` | [`Write`](#write)\<`Values`\> | This method updates the form state writing $value into the $path, setting isDirty to true. After writing, it validates all the paths contained into $pathsToValidate (if any) and all the fields that have the live validation active. | -| `writeAll` | [`WriteAll`](#writeall)\<`Values`\> | This method takes an array of [path, value] and updates the form state writing all those values into the specified paths. It sets isDirty to true. After writing, it validates all the paths contained into $pathToValidate and all the fields that have the live validation active. | +| `setError` | [`SetError`](#seterror) | Sets the error message at `path`. | +| `setSchema` | [`SetSchema`](#setschema)\<`T`\> | Replaces the current validation schema. | +| `submitForm` | [`SubmitForm`](#submitform)\<`T`\> | Validates the whole form and, if valid, runs the success callback to submit. | +| `validate` | [`Validate`](#validate)\<`T`\> | Validates only `path` (ignores live-validated fields). | +| `validateAll` | [`ValidateAll`](#validateall)\<`T`\> | Validates only the given `paths` (ignores live-validated fields). | +| `validateForm` | [`ValidateForm`](#validateform)\<`Partial`\<`T`\>\> | Validates the whole form and registers any error. | +| `write` | [`Write`](#write)\<`T`\> | Writes `value` at `path`, sets `isDirty`, then validates `pathsToValidate` plus every live-validated field. | +| `writeAll` | [`WriteAll`](#writeall)\<`T`\> | Writes every `[path, value]` pair, sets `isDirty`, then validates `pathsToValidate` plus every live-validated field. | #### Defined in -[index.ts:298](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L298) +[index.ts:186](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L186) ### Core Types #### Errors Ƭ **Errors**: `Record`\<`string`, `string`\> -Object including all the registered error messages since the last validation. -Errors are stored using the same path of the corresponding form values. +Error messages registered since the last validation, stored under the same +dot-path as the corresponding form value. **`Example`** -If the form object has this structure: -```json -{ - "age": 1 -} -``` -and age is a non valid field, errors object will look like this -```json -{ - "age": "Age must be greater then 18" -} +```ts +form: { age: 1 } +errors: { age: "Age must be greater than 18" } ``` #### Defined in -[index.ts:78](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L78) -#### Form - -Ƭ **Form**: [`FormbitValues`](#formbitvalues) - -Object containing the updated form. - -#### Defined in - -[index.ts:55](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L55) +[index.ts:23](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L23) #### FormState -Ƭ **FormState**\<`Values`\>: `Object` +Ƭ **FormState**\<`T`\>: `Object` -Internal form state storing all the data of the form (except the validation schema). +The whole internal state of the form (everything except the validation schema). #### Type parameters | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration | Name | Type | | :------ | :------ | | `errors` | [`Errors`](#errors) | -| `form` | `Values` | -| `initialValues` | `Values` | +| `form` | `T` | +| `initialValues` | `T` | | `isDirty` | `boolean` | | `liveValidation` | [`LiveValidation`](#livevalidation) | #### Defined in -[index.ts:110](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L110) +[index.ts:38](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L38) #### FormbitValues -Ƭ **FormbitValues**: \{ `__metadata?`: `FormbitRecord` } & `FormbitRecord` - -Base type for form values: a record of string keys with an optional `__metadata` field. - -#### Defined in - -[index.ts:52](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L52) -#### InitialValues +Ƭ **FormbitValues**: `Record`\<`string`, `unknown`\> & \{ `__metadata?`: `Record`\<`string`, `unknown`\> } -Ƭ **InitialValues**: [`FormbitValues`](#formbitvalues) +Base shape of every form handled by formbit: an open record of values, plus an +optional `__metadata` field formbit uses to carry data that must survive a +reset/initialize but must NOT be submitted. -InitialValues used to set up formbit; also used to reset the form to its original version. +The generic `T` you pass to `useFormbit()` must extend this type. #### Defined in -[index.ts:58](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L58) +[index.ts:13](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L13) #### LiveValidation Ƭ **LiveValidation**: `Record`\<`string`, ``true``\> -Object including all the values that are being live validated. -Usually fields that fail validation (using one of the methods that triggers validation) -will automatically be set to live-validated. - -A value/path is live-validated when validated at every change of the form. - -By default no field is live-validated. +Fields currently under live-validation (re-validated on every form change). +A field is added here automatically when it fails a validation. Empty by default. **`Example`** -If the form object has this structure: -```json -{ - "age": 1 -} -``` -and age is a field that is being live-validated, liveValidation object will look like this -```json -{ - "age": true -} +```ts +form: { age: 1 } +liveValidation: { age: true } ``` #### Defined in -[index.ts:103](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L103) +[index.ts:33](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L33) ### Callback Types #### CheckErrorCallback -Ƭ **CheckErrorCallback**\<`Values`\>: (`json`: [`Form`](#form), `inner`: [`ValidationError`](#validationerror)[], `writer`: [`FormState`](#formstate)\<`Values`\>, `setError`: [`SetError`](#seterror)) => `void` +Ƭ **CheckErrorCallback**\<`T`\>: (`json`: [`FormbitValues`](#formbitvalues), `inner`: [`ValidationError`](#validationerror)[], `writer`: [`FormState`](#formstate)\<`T`\>, `setError`: [`SetError`](#seterror)) => `void` -Invoked in case of errors raised by validation of check method. +Invoked by `check()` when the given json is invalid. #### Type parameters | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -513,9 +477,9 @@ Invoked in case of errors raised by validation of check method. | Name | Type | | :------ | :------ | -| `json` | [`Form`](#form) | +| `json` | [`FormbitValues`](#formbitvalues) | | `inner` | [`ValidationError`](#validationerror)[] | -| `writer` | [`FormState`](#formstate)\<`Values`\> | +| `writer` | [`FormState`](#formstate)\<`T`\> | | `setError` | [`SetError`](#seterror) | ##### Returns @@ -524,18 +488,18 @@ Invoked in case of errors raised by validation of check method. #### Defined in -[index.ts:171](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L171) +[index.ts:72](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L72) #### CheckSuccessCallback -Ƭ **CheckSuccessCallback**\<`Values`\>: (`json`: [`Form`](#form), `writer`: [`FormState`](#formstate)\<`Values`\>, `setError`: [`SetError`](#seterror)) => `void` +Ƭ **CheckSuccessCallback**\<`T`\>: (`json`: [`FormbitValues`](#formbitvalues), `writer`: [`FormState`](#formstate)\<`T`\>, `setError`: [`SetError`](#seterror)) => `void` -Success callback invoked by the check method when the operation is successful. +Invoked by `check()` when the given json is valid. #### Type parameters | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -545,8 +509,8 @@ Success callback invoked by the check method when the operation is successful. | Name | Type | | :------ | :------ | -| `json` | [`Form`](#form) | -| `writer` | [`FormState`](#formstate)\<`Values`\> | +| `json` | [`FormbitValues`](#formbitvalues) | +| `writer` | [`FormState`](#formstate)\<`T`\> | | `setError` | [`SetError`](#seterror) | ##### Returns @@ -555,18 +519,18 @@ Success callback invoked by the check method when the operation is successful. #### Defined in -[index.ts:162](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L162) +[index.ts:68](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L68) #### ErrorCallback -Ƭ **ErrorCallback**\<`Values`\>: (`writer`: [`FormState`](#formstate)\<`Values`\>, `setError`: [`SetError`](#seterror)) => `void` +Ƭ **ErrorCallback**\<`T`\>: (`writer`: [`FormState`](#formstate)\<`T`\>, `setError`: [`SetError`](#seterror)) => `void` -Invoked in case of errors raised by validation. +Invoked by validation methods when validation fails. #### Type parameters | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -576,7 +540,7 @@ Invoked in case of errors raised by validation. | Name | Type | | :------ | :------ | -| `writer` | [`FormState`](#formstate)\<`Values`\> | +| `writer` | [`FormState`](#formstate)\<`T`\> | | `setError` | [`SetError`](#seterror) | ##### Returns @@ -585,19 +549,19 @@ Invoked in case of errors raised by validation. #### Defined in -[index.ts:157](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L157) +[index.ts:64](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L64) #### SubmitSuccessCallback -Ƭ **SubmitSuccessCallback**\<`Values`\>: (`writer`: [`FormState`](#formstate)\<`Values` \| `Omit`\<`Values`, ``"__metadata"``\>\>, `setError`: [`SetError`](#seterror), `clearIsDirty`: () => `void`) => `void` +Ƭ **SubmitSuccessCallback**\<`T`\>: (`writer`: [`FormState`](#formstate)\<`Omit`\<`T`, ``"__metadata"``\>\>, `setError`: [`SetError`](#seterror), `clearIsDirty`: () => `void`) => `void` -Success callback invoked by the submit method when the validation is successful. -Is the right place to send your data to the backend. +Invoked by `submitForm()` once the whole form is valid — the place to send data +to the backend. `__metadata` is stripped from `writer.form` before this runs. #### Type parameters | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -607,7 +571,7 @@ Is the right place to send your data to the backend. | Name | Type | | :------ | :------ | -| `writer` | [`FormState`](#formstate)\<`Values` \| `Omit`\<`Values`, ``"__metadata"``\>\> | +| `writer` | [`FormState`](#formstate)\<`Omit`\<`T`, ``"__metadata"``\>\> | | `setError` | [`SetError`](#seterror) | | `clearIsDirty` | () => `void` | @@ -617,18 +581,18 @@ Is the right place to send your data to the backend. #### Defined in -[index.ts:181](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L181) +[index.ts:79](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L79) #### SuccessCallback -Ƭ **SuccessCallback**\<`Values`\>: (`writer`: [`FormState`](#formstate)\<`Values`\>, `setError`: [`SetError`](#seterror)) => `void` +Ƭ **SuccessCallback**\<`T`\>: (`writer`: [`FormState`](#formstate)\<`T`\>, `setError`: [`SetError`](#seterror)) => `void` -Success callback invoked by some formbit methods when the operation is successful. +Invoked by validation methods when the form (or the validated paths) are valid. #### Type parameters | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -638,7 +602,7 @@ Success callback invoked by some formbit methods when the operation is successfu | Name | Type | | :------ | :------ | -| `writer` | [`FormState`](#formstate)\<`Values`\> | +| `writer` | [`FormState`](#formstate)\<`T`\> | | `setError` | [`SetError`](#seterror) | ##### Returns @@ -647,12 +611,12 @@ Success callback invoked by some formbit methods when the operation is successfu #### Defined in -[index.ts:152](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L152) +[index.ts:60](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L60) ### Method Types #### Check -Ƭ **Check**\<`Values`\>: (`json`: [`Form`](#form), `options?`: [`CheckFnOptions`](#checkfnoptions)\<`Values`\>) => [`ValidationError`](#validationerror)[] \| `undefined` +Ƭ **Check**\<`T`\>: (`json`: [`FormbitValues`](#formbitvalues), `options?`: [`CheckFnOptions`](#checkfnoptions)\<`T`\>) => [`ValidationError`](#validationerror)[] \| `undefined` See [FormbitObject.check](#check). @@ -660,7 +624,7 @@ See [FormbitObject.check](#check). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -670,8 +634,8 @@ See [FormbitObject.check](#check). | Name | Type | | :------ | :------ | -| `json` | [`Form`](#form) | -| `options?` | [`CheckFnOptions`](#checkfnoptions)\<`Values`\> | +| `json` | [`FormbitValues`](#formbitvalues) | +| `options?` | [`CheckFnOptions`](#checkfnoptions)\<`T`\> | ##### Returns @@ -679,10 +643,10 @@ See [FormbitObject.check](#check). #### Defined in -[index.ts:214](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L214) +[index.ts:89](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L89) #### Initialize -Ƭ **Initialize**\<`Values`\>: (`values`: `Partial`\<`Values`\>) => `void` +Ƭ **Initialize**\<`T`\>: (`values`: `Partial`\<`T`\>) => `void` See [FormbitObject.initialize](#initialize). @@ -690,7 +654,7 @@ See [FormbitObject.initialize](#initialize). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -700,7 +664,7 @@ See [FormbitObject.initialize](#initialize). | Name | Type | | :------ | :------ | -| `values` | `Partial`\<`Values`\> | +| `values` | `Partial`\<`T`\> | ##### Returns @@ -708,10 +672,10 @@ See [FormbitObject.initialize](#initialize). #### Defined in -[index.ts:218](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L218) +[index.ts:93](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L93) #### Remove -Ƭ **Remove**\<`Values`\>: (`path`: `string`, `options?`: [`WriteFnOptions`](#writefnoptions)\<`Values`\>) => `void` +Ƭ **Remove**\<`T`\>: (`path`: `string`, `options?`: [`WriteFnOptions`](#writefnoptions)\<`T`\>) => `void` See [FormbitObject.remove](#remove). @@ -719,7 +683,7 @@ See [FormbitObject.remove](#remove). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -730,7 +694,7 @@ See [FormbitObject.remove](#remove). | Name | Type | | :------ | :------ | | `path` | `string` | -| `options?` | [`WriteFnOptions`](#writefnoptions)\<`Values`\> | +| `options?` | [`WriteFnOptions`](#writefnoptions)\<`T`\> | ##### Returns @@ -738,10 +702,10 @@ See [FormbitObject.remove](#remove). #### Defined in -[index.ts:221](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L221) +[index.ts:96](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L96) #### RemoveAll -Ƭ **RemoveAll**\<`Values`\>: (`arr`: `string`[], `options?`: [`WriteFnOptions`](#writefnoptions)\<`Values`\>) => `void` +Ƭ **RemoveAll**\<`T`\>: (`arr`: `string`[], `options?`: [`WriteFnOptions`](#writefnoptions)\<`T`\>) => `void` See [FormbitObject.removeAll](#removeall). @@ -749,7 +713,7 @@ See [FormbitObject.removeAll](#removeall). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -760,7 +724,7 @@ See [FormbitObject.removeAll](#removeall). | Name | Type | | :------ | :------ | | `arr` | `string`[] | -| `options?` | [`WriteFnOptions`](#writefnoptions)\<`Values`\> | +| `options?` | [`WriteFnOptions`](#writefnoptions)\<`T`\> | ##### Returns @@ -768,7 +732,7 @@ See [FormbitObject.removeAll](#removeall). #### Defined in -[index.ts:256](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L256) +[index.ts:116](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L116) #### SetError Ƭ **SetError**: (`path`: `string`, `value`: `string`) => `void` @@ -792,10 +756,10 @@ See [FormbitObject.setError](#seterror). #### Defined in -[index.ts:224](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L224) +[index.ts:99](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L99) #### SetSchema -Ƭ **SetSchema**\<`Values`\>: (`newSchema`: [`ValidationSchema`](#validationschema)\<`Values`\>) => `void` +Ƭ **SetSchema**\<`T`\>: (`newSchema`: [`ValidationSchema`](#validationschema)\<`T`\>) => `void` See [FormbitObject.setSchema](#setschema). @@ -803,7 +767,7 @@ See [FormbitObject.setSchema](#setschema). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -813,7 +777,7 @@ See [FormbitObject.setSchema](#setschema). | Name | Type | | :------ | :------ | -| `newSchema` | [`ValidationSchema`](#validationschema)\<`Values`\> | +| `newSchema` | [`ValidationSchema`](#validationschema)\<`T`\> | ##### Returns @@ -821,10 +785,10 @@ See [FormbitObject.setSchema](#setschema). #### Defined in -[index.ts:227](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L227) +[index.ts:102](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L102) #### SubmitForm -Ƭ **SubmitForm**\<`Values`\>: (`successCallback`: [`SubmitSuccessCallback`](#submitsuccesscallback)\<`Values`\>, `errorCallback?`: [`ErrorCallback`](#errorcallback)\<`Partial`\<`Values`\>\>, `options?`: [`ValidateOptions`](#validateoptions)) => `void` +Ƭ **SubmitForm**\<`T`\>: (`successCallback`: [`SubmitSuccessCallback`](#submitsuccesscallback)\<`T`\>, `errorCallback?`: [`ErrorCallback`](#errorcallback)\<`Partial`\<`T`\>\>, `options?`: [`ValidateOptions`](#validateoptions)) => `void` See [FormbitObject.submitForm](#submitform). @@ -832,7 +796,7 @@ See [FormbitObject.submitForm](#submitform). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -842,8 +806,8 @@ See [FormbitObject.submitForm](#submitform). | Name | Type | | :------ | :------ | -| `successCallback` | [`SubmitSuccessCallback`](#submitsuccesscallback)\<`Values`\> | -| `errorCallback?` | [`ErrorCallback`](#errorcallback)\<`Partial`\<`Values`\>\> | +| `successCallback` | [`SubmitSuccessCallback`](#submitsuccesscallback)\<`T`\> | +| `errorCallback?` | [`ErrorCallback`](#errorcallback)\<`Partial`\<`T`\>\> | | `options?` | [`ValidateOptions`](#validateoptions) | ##### Returns @@ -852,10 +816,10 @@ See [FormbitObject.submitForm](#submitform). #### Defined in -[index.ts:230](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L230) +[index.ts:132](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L132) #### Validate -Ƭ **Validate**\<`Values`\>: (`path`: `string`, `options?`: [`ValidateFnOptions`](#validatefnoptions)\<`Values`\>) => `void` +Ƭ **Validate**\<`T`\>: (`path`: `string`, `options?`: [`ValidateFnOptions`](#validatefnoptions)\<`T`\>) => `void` See [FormbitObject.validate](#validate). @@ -863,7 +827,7 @@ See [FormbitObject.validate](#validate). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -874,7 +838,7 @@ See [FormbitObject.validate](#validate). | Name | Type | | :------ | :------ | | `path` | `string` | -| `options?` | [`ValidateFnOptions`](#validatefnoptions)\<`Values`\> | +| `options?` | [`ValidateFnOptions`](#validatefnoptions)\<`T`\> | ##### Returns @@ -882,10 +846,10 @@ See [FormbitObject.validate](#validate). #### Defined in -[index.ts:236](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L236) +[index.ts:120](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L120) #### ValidateAll -Ƭ **ValidateAll**\<`Values`\>: (`paths`: `string`[], `options?`: [`ValidateFnOptions`](#validatefnoptions)\<`Values`\>) => `void` +Ƭ **ValidateAll**\<`T`\>: (`paths`: `string`[], `options?`: [`ValidateFnOptions`](#validatefnoptions)\<`T`\>) => `void` See [FormbitObject.validateAll](#validateall). @@ -893,7 +857,7 @@ See [FormbitObject.validateAll](#validateall). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -904,7 +868,7 @@ See [FormbitObject.validateAll](#validateall). | Name | Type | | :------ | :------ | | `paths` | `string`[] | -| `options?` | [`ValidateFnOptions`](#validatefnoptions)\<`Values`\> | +| `options?` | [`ValidateFnOptions`](#validatefnoptions)\<`T`\> | ##### Returns @@ -912,10 +876,10 @@ See [FormbitObject.validateAll](#validateall). #### Defined in -[index.ts:239](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L239) +[index.ts:123](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L123) #### ValidateForm -Ƭ **ValidateForm**\<`Values`\>: (`successCallback?`: [`SuccessCallback`](#successcallback)\<`Values`\>, `errorCallback?`: [`ErrorCallback`](#errorcallback)\<`Values`\>, `options?`: [`ValidateOptions`](#validateoptions)) => `void` +Ƭ **ValidateForm**\<`T`\>: (`successCallback?`: [`SuccessCallback`](#successcallback)\<`T`\>, `errorCallback?`: [`ErrorCallback`](#errorcallback)\<`T`\>, `options?`: [`ValidateOptions`](#validateoptions)) => `void` See [FormbitObject.validateForm](#validateform). @@ -923,7 +887,7 @@ See [FormbitObject.validateForm](#validateform). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -933,8 +897,8 @@ See [FormbitObject.validateForm](#validateform). | Name | Type | | :------ | :------ | -| `successCallback?` | [`SuccessCallback`](#successcallback)\<`Values`\> | -| `errorCallback?` | [`ErrorCallback`](#errorcallback)\<`Values`\> | +| `successCallback?` | [`SuccessCallback`](#successcallback)\<`T`\> | +| `errorCallback?` | [`ErrorCallback`](#errorcallback)\<`T`\> | | `options?` | [`ValidateOptions`](#validateoptions) | ##### Returns @@ -943,10 +907,10 @@ See [FormbitObject.validateForm](#validateform). #### Defined in -[index.ts:242](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L242) +[index.ts:126](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L126) #### Write -Ƭ **Write**\<`Values`\>: (`path`: keyof `Values` \| `string`, `value`: `unknown`, `options?`: [`WriteFnOptions`](#writefnoptions)\<`Values`\>) => `void` +Ƭ **Write**\<`T`\>: (`path`: keyof `T` \| `string`, `value`: `unknown`, `options?`: [`WriteFnOptions`](#writefnoptions)\<`T`\>) => `void` See [FormbitObject.write](#write). @@ -954,7 +918,7 @@ See [FormbitObject.write](#write). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -964,9 +928,9 @@ See [FormbitObject.write](#write). | Name | Type | | :------ | :------ | -| `path` | keyof `Values` \| `string` | +| `path` | keyof `T` \| `string` | | `value` | `unknown` | -| `options?` | [`WriteFnOptions`](#writefnoptions)\<`Values`\> | +| `options?` | [`WriteFnOptions`](#writefnoptions)\<`T`\> | ##### Returns @@ -974,10 +938,10 @@ See [FormbitObject.write](#write). #### Defined in -[index.ts:248](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L248) +[index.ts:108](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L108) #### WriteAll -Ƭ **WriteAll**\<`Values`\>: (`arr`: [`WriteAllValue`](#writeallvalue)\<`Values`\>[], `options?`: [`WriteFnOptions`](#writefnoptions)\<`Values`\>) => `void` +Ƭ **WriteAll**\<`T`\>: (`arr`: [`WriteAllValue`](#writeallvalue)\<`T`\>[], `options?`: [`WriteFnOptions`](#writefnoptions)\<`T`\>) => `void` See [FormbitObject.writeAll](#writeall). @@ -985,7 +949,7 @@ See [FormbitObject.writeAll](#writeall). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -995,8 +959,8 @@ See [FormbitObject.writeAll](#writeall). | Name | Type | | :------ | :------ | -| `arr` | [`WriteAllValue`](#writeallvalue)\<`Values`\>[] | -| `options?` | [`WriteFnOptions`](#writefnoptions)\<`Values`\> | +| `arr` | [`WriteAllValue`](#writeallvalue)\<`T`\>[] | +| `options?` | [`WriteFnOptions`](#writefnoptions)\<`T`\> | ##### Returns @@ -1004,334 +968,120 @@ See [FormbitObject.writeAll](#writeall). #### Defined in -[index.ts:252](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L252) +[index.ts:112](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L112) ### Options Types #### CheckFnOptions -Ƭ **CheckFnOptions**\<`Values`\>: `Object` +Ƭ **CheckFnOptions**\<`T`\>: `Object` -Options object to change the behavior of the check method. +Options accepted by `check()`. #### Type parameters | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration | Name | Type | | :------ | :------ | -| `errorCallback?` | [`CheckErrorCallback`](#checkerrorcallback)\<`Values`\> | +| `errorCallback?` | [`CheckErrorCallback`](#checkerrorcallback)\<`T`\> | | `options?` | [`ValidateOptions`](#validateoptions) | -| `successCallback?` | [`CheckSuccessCallback`](#checksuccesscallback)\<`Values`\> | +| `successCallback?` | [`CheckSuccessCallback`](#checksuccesscallback)\<`T`\> | #### Defined in -[index.ts:269](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L269) +[index.ts:140](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L140) #### ValidateFnOptions -Ƭ **ValidateFnOptions**\<`Values`\>: `Object` +Ƭ **ValidateFnOptions**\<`T`\>: `Object` -Options object to change the behavior of the validate methods. +Options accepted by the `validate` methods. #### Type parameters | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration | Name | Type | | :------ | :------ | -| `errorCallback?` | [`ErrorCallback`](#errorcallback)\<`Partial`\<`Values`\>\> | +| `errorCallback?` | [`ErrorCallback`](#errorcallback)\<`Partial`\<`T`\>\> | | `options?` | [`ValidateOptions`](#validateoptions) | -| `successCallback?` | [`SuccessCallback`](#successcallback)\<`Partial`\<`Values`\>\> | +| `successCallback?` | [`SuccessCallback`](#successcallback)\<`Partial`\<`T`\>\> | #### Defined in -[index.ts:278](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L278) +[index.ts:147](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L147) #### WriteAllValue -Ƭ **WriteAllValue**\<`Values`\>: [keyof `Values` \| `string`, `unknown`] +Ƭ **WriteAllValue**\<`T`\>: [keyof `T` \| `string`, `unknown`] -Tuple of [key, value] pair. +A single `[path, value]` pair accepted by `writeAll`. #### Type parameters | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Defined in -[index.ts:262](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L262) +[index.ts:105](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L105) #### WriteFnOptions -Ƭ **WriteFnOptions**\<`Values`\>: \{ `noLiveValidation?`: `boolean` ; `pathsToValidate?`: `string`[] } & [`ValidateFnOptions`](#validatefnoptions)\<`Values`\> +Ƭ **WriteFnOptions**\<`T`\>: \{ `noLiveValidation?`: `boolean` ; `pathsToValidate?`: `string`[] } & [`ValidateFnOptions`](#validatefnoptions)\<`T`\> -Options object to change the behavior of the write methods. +Options accepted by the `write`/`remove` methods (validate options plus path control). #### Type parameters | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Defined in -[index.ts:287](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L287) +[index.ts:154](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L154) ### Yup Re-Exports #### ValidateOptions Ƭ **ValidateOptions**: `YupValidateOptions` -Type imported from the yup library. -It represents the object with all the options that can be passed to the internal yup validation method. - -Link to the Yup documentation [https://github.com/jquense/yup](https://github.com/jquense/yup) +Options forwarded to yup's validation methods. See [https://github.com/jquense/yup](https://github.com/jquense/yup). #### Defined in -[index.ts:137](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L137) +[index.ts:52](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L52) #### ValidationError Ƭ **ValidationError**: `YupValidationError` -Type imported from the yup library. -It represents the error object returned when a validation fails. - -Link to the Yup documentation [https://github.com/jquense/yup](https://github.com/jquense/yup) +The error object yup throws when a validation fails. See [https://github.com/jquense/yup](https://github.com/jquense/yup). #### Defined in -[index.ts:145](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L145) +[index.ts:55](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L55) #### ValidationSchema -Ƭ **ValidationSchema**\<`Values`\>: `ObjectSchema`\<`Values`\> - -Type imported from the yup library. -It represents any validation schema created with the yup.object() method. - -Link to the Yup documentation [https://github.com/jquense/yup](https://github.com/jquense/yup) - -#### Type parameters - -| Name | Type | -| :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | - -#### Defined in - -[index.ts:129](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L129) -### Deprecated Types - -
-Show deprecated types - -#### ClearIsDirty - -Ƭ **ClearIsDirty**: () => `void` - -**`Deprecated`** - -Inlined into [FormbitObject](#formbitobject). - -#### Type declaration - -▸ (): `void` - -##### Returns - -`void` - -#### Defined in - -[index.ts:200](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L200) -#### ErrorCheckCallback - -Ƭ **ErrorCheckCallback**\<`Values`\>: [`CheckErrorCallback`](#checkerrorcallback)\<`Values`\> - -**`Deprecated`** - -Use [CheckErrorCallback](#checkerrorcallback) instead. - -#### Type parameters - -| Name | Type | -| :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | - -#### Defined in - -[index.ts:175](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L175) -#### ErrorFn - -Ƭ **ErrorFn**: (`path`: `string`) => `string` \| `undefined` - -**`Deprecated`** - -Inlined into [FormbitObject](#formbitobject). - -#### Type declaration - -▸ (`path`): `string` \| `undefined` - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `path` | `string` | - -##### Returns - -`string` \| `undefined` - -#### Defined in - -[index.ts:191](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L191) -#### IsDirty - -Ƭ **IsDirty**: `boolean` - -**`Deprecated`** - -Inlined into [FormbitObject](#formbitobject). - -#### Defined in - -[index.ts:209](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L209) -#### IsFormInvalid - -Ƭ **IsFormInvalid**: () => `boolean` - -**`Deprecated`** - -Inlined into [FormbitObject](#formbitobject). - -#### Type declaration - -▸ (): `boolean` - -##### Returns - -`boolean` - -#### Defined in - -[index.ts:197](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L197) -#### IsFormValid - -Ƭ **IsFormValid**: () => `boolean` - -**`Deprecated`** - -Inlined into [FormbitObject](#formbitobject). - -#### Type declaration - -▸ (): `boolean` - -##### Returns - -`boolean` - -#### Defined in - -[index.ts:194](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L194) -#### LiveValidationFn - -Ƭ **LiveValidationFn**: (`path`: `string`) => ``true`` \| `undefined` - -**`Deprecated`** - -Inlined into [FormbitObject](#formbitobject). - -#### Type declaration - -▸ (`path`): ``true`` \| `undefined` - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `path` | `string` | - -##### Returns - -``true`` \| `undefined` - -#### Defined in - -[index.ts:206](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L206) -#### Object - -Ƭ **Object**: `FormbitRecord` - -**`Deprecated`** - -Use FormbitRecord instead. Renamed to avoid shadowing the global `Object`. - -#### Defined in - -[index.ts:19](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L19) -#### ResetForm - -Ƭ **ResetForm**: () => `void` - -**`Deprecated`** - -Inlined into [FormbitObject](#formbitobject). - -#### Type declaration - -▸ (): `void` - -##### Returns - -`void` - -#### Defined in - -[index.ts:203](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L203) -#### SuccessCheckCallback - -Ƭ **SuccessCheckCallback**\<`Values`\>: [`CheckSuccessCallback`](#checksuccesscallback)\<`Values`\> - -**`Deprecated`** - -Use [CheckSuccessCallback](#checksuccesscallback) instead. - -#### Type parameters - -| Name | Type | -| :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | - -#### Defined in - -[index.ts:166](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L166) -#### Writer - -Ƭ **Writer**\<`Values`\>: [`FormState`](#formstate)\<`Values`\> - -**`Deprecated`** +Ƭ **ValidationSchema**\<`T`\>: `ObjectSchema`\<`T`\> -Use [FormState](#formstate) instead. +A validation schema built with `yup.object()`. See [https://github.com/jquense/yup](https://github.com/jquense/yup). #### Type parameters | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Defined in -[index.ts:119](https://github.com/radicalbit/formbit/blob/a7ecd8e/src/types/index.ts#L119) -
+[index.ts:49](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L49) ## License diff --git a/example/.eslintrc.cjs b/example/.eslintrc.cjs index 76185b7..722faa1 100644 --- a/example/.eslintrc.cjs +++ b/example/.eslintrc.cjs @@ -71,6 +71,8 @@ module.exports = { 'react/jsx-handler-names': 0, 'react/jsx-fragments': 0, 'react/no-unused-prop-types': 0, + 'react/jsx-indent': ['error', 2], + 'react/jsx-indent-props': ['error', 2], 'import/export': 0, 'max-len': ['error', { code: 120 }], }, diff --git a/example/package.json b/example/package.json index 63682d5..c31385a 100644 --- a/example/package.json +++ b/example/package.json @@ -13,7 +13,7 @@ }, "dependencies": { "@fortawesome/free-solid-svg-icons": "6.7.2", - "@radicalbit/radicalbit-design-system": "2.13.1", + "@radicalbit/radicalbit-design-system": "2.19.5", "cypress": "^13.7.3", "formbit": "link:..", "react": "^18.2.0", diff --git a/example/src/__tests__/basic-form-context.cy.tsx b/example/src/__tests__/basic-form-context.cy.tsx index 3160384..e93dc54 100644 --- a/example/src/__tests__/basic-form-context.cy.tsx +++ b/example/src/__tests__/basic-form-context.cy.tsx @@ -1,6 +1,6 @@ import App from '../App' -describe('', () => { +describe('', () => { beforeEach(() => { cy.mount() cy.getTab('context').click() diff --git a/example/src/__tests__/basic-form-hook.cy.tsx b/example/src/__tests__/basic-form-hook.cy.tsx index fec893b..9b2d193 100644 --- a/example/src/__tests__/basic-form-hook.cy.tsx +++ b/example/src/__tests__/basic-form-hook.cy.tsx @@ -64,7 +64,7 @@ describe('', () => { cy.get('@name').should('be.empty') }) - it('Should reset name field', () => { + it('Should reset surname field', () => { cy.get('@surname').type('Lovelace') cy.button('reset').click() diff --git a/example/src/forms/a-basic-form-hook/index.tsx b/example/src/forms/a-basic-form-hook/index.tsx index 8bb73cb..073e842 100644 --- a/example/src/forms/a-basic-form-hook/index.tsx +++ b/example/src/forms/a-basic-form-hook/index.tsx @@ -9,7 +9,7 @@ import { ChangeEvent } from 'react' import { useFakeApiContext } from '../fake-api-context' import { useAutoFocus } from '../../helpers/use-autofocus' import { success } from '../../helpers/message' -import { FormData, schema } from './schema' +import { type FormValues, schema } from './schema' type FieldProps = { value?: string, @@ -31,7 +31,7 @@ export function BasicFormHook() { const { form, error, write, resetForm, submitForm, isFormInvalid, isDirty - } = useFormbit({ initialValues: {}, yup: schema }) + } = useFormbit({ initialValues: {}, yup: schema }) const handleOnChangeName = (e: ChangeEvent) => write('name', e.target.value) const handleOnChangeSurname = (e: ChangeEvent) => write('surname', e.target.value) diff --git a/example/src/forms/a-basic-form-hook/schema.ts b/example/src/forms/a-basic-form-hook/schema.ts index 719602e..4ee55fc 100644 --- a/example/src/forms/a-basic-form-hook/schema.ts +++ b/example/src/forms/a-basic-form-hook/schema.ts @@ -5,4 +5,4 @@ export const schema = yup.object().shape({ surname: yup.string().min(2).required() }) -export type FormData = yup.InferType +export type FormValues = yup.InferType diff --git a/example/src/forms/b-basic-form-context/index.tsx b/example/src/forms/b-basic-form-context/index.tsx index 01d5be8..3df1bc0 100644 --- a/example/src/forms/b-basic-form-context/index.tsx +++ b/example/src/forms/b-basic-form-context/index.tsx @@ -8,11 +8,11 @@ import { InputRef } from 'rc-input' import { ChangeEvent } from 'react' import { useAutoFocus } from '../../helpers/use-autofocus' import { useHandleOnSubmit } from './use-handle-on-submit' -import { FormData, schema } from './schema' +import { type FormValues, schema } from './schema' export function BasicFormContext() { return ( - + ) @@ -35,7 +35,7 @@ function BasicFormInner() { } function Name() { - const { form, error, write } = useFormbitContext() + const { form, error, write } = useFormbitContext() const { handleOnSubmit } = useHandleOnSubmit() @@ -58,7 +58,7 @@ function Name() { } function Surname() { - const { form, error, write } = useFormbitContext() + const { form, error, write } = useFormbitContext() const { handleOnSubmit } = useHandleOnSubmit() @@ -78,7 +78,7 @@ function Surname() { } function Age() { - const { form, error, write } = useFormbitContext() + const { form, error, write } = useFormbitContext() const { handleOnSubmit } = useHandleOnSubmit() @@ -94,11 +94,12 @@ function Age() { value={form.age} required /> - ) + + ) } function Actions() { - const { resetForm } = useFormbitContext() + const { resetForm } = useFormbitContext() const { handleOnSubmit, isSubmitDisabled, args: { isLoading } } = useHandleOnSubmit() diff --git a/example/src/forms/b-basic-form-context/schema.ts b/example/src/forms/b-basic-form-context/schema.ts index 528c61a..0b7d36b 100644 --- a/example/src/forms/b-basic-form-context/schema.ts +++ b/example/src/forms/b-basic-form-context/schema.ts @@ -6,4 +6,4 @@ export const schema = yup.object().shape({ age: yup.number().min(18).max(200).required() }) -export type FormData = yup.InferType +export type FormValues = yup.InferType diff --git a/example/src/forms/b-basic-form-context/use-handle-on-submit.tsx b/example/src/forms/b-basic-form-context/use-handle-on-submit.tsx index 0702172..604e384 100644 --- a/example/src/forms/b-basic-form-context/use-handle-on-submit.tsx +++ b/example/src/forms/b-basic-form-context/use-handle-on-submit.tsx @@ -2,10 +2,10 @@ import { useFormbitContext } from 'formbit' import { success } from '../../helpers/message' import { useFakeApiContext } from '../fake-api-context' import type { UseHandleOnSubmitResult } from './use-handle-on-submit-types' -import type { FormData } from './schema' +import type { FormValues } from './schema' export const useHandleOnSubmit = (): UseHandleOnSubmitResult => { - const { submitForm, isFormInvalid, resetForm, isDirty } = useFormbitContext() + const { submitForm, isFormInvalid, resetForm, isDirty } = useFormbitContext() const { fakePost } = useFakeApiContext() const { mutate, ...args } = fakePost diff --git a/example/src/forms/c-addable-fields/index.tsx b/example/src/forms/c-addable-fields/index.tsx index be84464..7bc4de5 100644 --- a/example/src/forms/c-addable-fields/index.tsx +++ b/example/src/forms/c-addable-fields/index.tsx @@ -10,7 +10,7 @@ import { InputRef } from 'rc-input' import { ChangeEvent, ChangeEventHandler, useRef, useState } from 'react' import { useAutoFocus } from '../../helpers/use-autofocus' import { useHandleOnSubmit } from './use-handle-on-submit' -import { FormData, schema } from './schema' +import { type FormValues, schema } from './schema' export function AddableFieldsForm() { return ( @@ -21,7 +21,7 @@ export function AddableFieldsForm() { } function BasicFormInner() { - const { form } = useFormbitContext() + const { form } = useFormbitContext() const friends = form?.friends ?? [] return ( @@ -34,7 +34,7 @@ function BasicFormInner() {
- {friends.map((_, i) => )} + {friends.map((_, i) => )}
@@ -43,7 +43,7 @@ function BasicFormInner() { } function Name() { - const { form, error, write } = useFormbitContext() + const { form, error, write } = useFormbitContext() const { handleOnSubmit } = useHandleOnSubmit() @@ -66,7 +66,7 @@ function Name() { } function Surname() { - const { form, error, write } = useFormbitContext() + const { form, error, write } = useFormbitContext() const { handleOnSubmit } = useHandleOnSubmit() const handleOnChangeSurname = (e: ChangeEvent) => write('surname', e.target.value) @@ -87,7 +87,7 @@ function Surname() { function FriendInput() { const inputNameRef = useRef(null) - const { write, form, error } = useFormbitContext() + const { write, form, error } = useFormbitContext() const friends = form?.friends ?? [] const [name, setName] = useState() @@ -133,22 +133,22 @@ function FriendInput() { } function Friend({ index }: { index: number }) { - const { error, write, validate, form } = useFormbitContext() + const { error, write, validate, form } = useFormbitContext() - const name = form.friends?.[index].name - const surname = form.friends?.[index].surname + const name = form.friends?.[index]?.name + const surname = form.friends?.[index]?.surname - const handleOnBlurFriendName = () => validate(`headers[${index}].name`) - const handleOnBlurFriendSurname = () => validate(`headers[${index}].surname`) + const handleOnBlurFriendName = () => validate(`friends[${index}].name`) + const handleOnBlurFriendSurname = () => validate(`friends[${index}].surname`) const handleOnChangeFriendName: ChangeEventHandler = - ({ target }) => write(`friends[${index}].key`, target.value) + ({ target }) => write(`friends[${index}].name`, target.value) const handleOnChangeFriendSurname: ChangeEventHandler = - ({ target }) => write(`friends[${index}].key`, target.value) + ({ target }) => write(`friends[${index}].surname`, target.value) const handleOnRemoveFriend = () => write('friends', form.friends?.filter((_, i) => index !== i)) - const errorMessage = error(`headers[${index}].name`) || error(`headers[${index}].surname`) + const errorMessage = error(`friends[${index}].name`) || error(`friends[${index}].surname`) return ( @@ -173,7 +173,7 @@ function Friend({ index }: { index: number }) { } function Actions() { - const { resetForm } = useFormbitContext() + const { resetForm } = useFormbitContext() const { handleOnSubmit, isSubmitDisabled, args: { isLoading } } = useHandleOnSubmit() diff --git a/example/src/forms/c-addable-fields/schema.ts b/example/src/forms/c-addable-fields/schema.ts index 62fde23..d15b968 100644 --- a/example/src/forms/c-addable-fields/schema.ts +++ b/example/src/forms/c-addable-fields/schema.ts @@ -11,4 +11,4 @@ export const schema = yup.object().shape({ ).required() }) -export type FormData = yup.InferType +export type FormValues = yup.InferType diff --git a/example/src/forms/c-addable-fields/use-handle-on-submit.tsx b/example/src/forms/c-addable-fields/use-handle-on-submit.tsx index 0702172..604e384 100644 --- a/example/src/forms/c-addable-fields/use-handle-on-submit.tsx +++ b/example/src/forms/c-addable-fields/use-handle-on-submit.tsx @@ -2,10 +2,10 @@ import { useFormbitContext } from 'formbit' import { success } from '../../helpers/message' import { useFakeApiContext } from '../fake-api-context' import type { UseHandleOnSubmitResult } from './use-handle-on-submit-types' -import type { FormData } from './schema' +import type { FormValues } from './schema' export const useHandleOnSubmit = (): UseHandleOnSubmitResult => { - const { submitForm, isFormInvalid, resetForm, isDirty } = useFormbitContext() + const { submitForm, isFormInvalid, resetForm, isDirty } = useFormbitContext() const { fakePost } = useFakeApiContext() const { mutate, ...args } = fakePost diff --git a/example/src/forms/d-edit-like/index.tsx b/example/src/forms/d-edit-like/index.tsx index e83a558..1b742dc 100644 --- a/example/src/forms/d-edit-like/index.tsx +++ b/example/src/forms/d-edit-like/index.tsx @@ -10,15 +10,15 @@ import { InputRef } from 'rc-input' import { ChangeEvent } from 'react' import { useAutoFocus } from '../../helpers/use-autofocus' import { useFakeApiContext } from '../fake-api-context' -import { FormData, schema } from './schema' +import { type FormValues, schema } from './schema' import { useHandleOnSubmit } from './use-handle-on-submit' import { useInitializeForm } from './use-initialize-form' export function EditLikeForm() { return ( - - - + + + ) } @@ -104,7 +104,7 @@ function IsSuccess() { function Name() { const ref = useAutoFocus() - const { form, error, write } = useFormbitContext() + const { form, error, write } = useFormbitContext() const { handleOnSubmit } = useHandleOnSubmit() @@ -125,7 +125,7 @@ function Name() { } function Surname() { - const { form, error, write } = useFormbitContext() + const { form, error, write } = useFormbitContext() const { handleOnSubmit } = useHandleOnSubmit() @@ -145,7 +145,7 @@ function Surname() { } function Email() { - const { form, error, write } = useFormbitContext() + const { form, error, write } = useFormbitContext() const { handleOnSubmit } = useHandleOnSubmit() diff --git a/example/src/forms/d-edit-like/schema.ts b/example/src/forms/d-edit-like/schema.ts index 1ae1fdc..5ade712 100644 --- a/example/src/forms/d-edit-like/schema.ts +++ b/example/src/forms/d-edit-like/schema.ts @@ -6,4 +6,4 @@ export const schema = yup.object().shape({ email: yup.string().email().required() }) -export type FormData = yup.InferType +export type FormValues = yup.InferType diff --git a/example/src/forms/d-edit-like/use-handle-on-submit.tsx b/example/src/forms/d-edit-like/use-handle-on-submit.tsx index e02ac66..4b7338f 100644 --- a/example/src/forms/d-edit-like/use-handle-on-submit.tsx +++ b/example/src/forms/d-edit-like/use-handle-on-submit.tsx @@ -1,11 +1,11 @@ import { useFormbitContext } from 'formbit' import { success } from '../../helpers/message' import { useFakeApiContext } from '../fake-api-context' -import { FormData } from './schema' +import type { FormValues } from './schema' import type { UseHandleOnSubmitResult } from './use-handle-on-submit-types' export const useHandleOnSubmit = (): UseHandleOnSubmitResult => { - const { submitForm, isFormInvalid, resetForm, isDirty } = useFormbitContext() + const { submitForm, isFormInvalid, resetForm, isDirty } = useFormbitContext() const { fakePost } = useFakeApiContext() const { mutate, ...args } = fakePost diff --git a/example/src/forms/d-edit-like/use-initialize-form.ts b/example/src/forms/d-edit-like/use-initialize-form.ts index 4badf58..725f05a 100644 --- a/example/src/forms/d-edit-like/use-initialize-form.ts +++ b/example/src/forms/d-edit-like/use-initialize-form.ts @@ -1,10 +1,10 @@ import { useFormbitContext } from 'formbit' import { useEffect } from 'react' import { useFakeApiContext } from '../fake-api-context' -import { FormData } from './schema' +import type { FormValues } from './schema' export const useInitializeForm = () => { - const { initialize } = useFormbitContext() + const { initialize } = useFormbitContext() const { fakeUser } = useFakeApiContext() const { data: user } = fakeUser diff --git a/example/src/forms/e-multiple-steps/schema.ts b/example/src/forms/e-multiple-steps/schema.ts index f2cf9fd..607787b 100644 --- a/example/src/forms/e-multiple-steps/schema.ts +++ b/example/src/forms/e-multiple-steps/schema.ts @@ -8,7 +8,7 @@ export const schema = yup.object().shape({ }) -export type FormData = yup.InferType & { +export type FormValues = yup.InferType & { __metadata: { step?: number, nextStep?: () => void, diff --git a/example/src/forms/e-multiple-steps/step-one.tsx b/example/src/forms/e-multiple-steps/step-one.tsx index 9f6b59f..94ef05a 100644 --- a/example/src/forms/e-multiple-steps/step-one.tsx +++ b/example/src/forms/e-multiple-steps/step-one.tsx @@ -3,25 +3,25 @@ import { Button, FormField, Input, SectionTitle } from '@radicalbit/radicalbit-d import { InputRef } from 'rc-input' import { ChangeEvent } from 'react' import { useAutoFocus } from '../../helpers/use-autofocus' -import { FormData } from './schema' +import type { FormValues } from './schema' import { useHandleNextStep } from './use-handle-next-step' export function StepOne() { - return <> -
- + return ( +
+ - + - + - -
- + +
+ ) } function Name() { - const { form, error, write } = useFormbitContext() + const { form, error, write } = useFormbitContext() const [handleOnNext] = useHandleNextStep(['name', 'surname']) @@ -30,36 +30,36 @@ function Name() { const ref = useAutoFocus() return ( - - - + + + ) } function Surname() { - const { form, error, write } = useFormbitContext() + const { form, error, write } = useFormbitContext() const [handleOnNext] = useHandleNextStep(['name', 'surname']) const handleOnChangeSurname = (e: ChangeEvent) => write('surname', e.target.value) return ( - - - + + + ) } @@ -67,12 +67,12 @@ function Actions() { const [handleOnNext, isStepInvalid] = useHandleNextStep(['name', 'surname']) return ( - + ) } diff --git a/example/src/forms/e-multiple-steps/step-three.tsx b/example/src/forms/e-multiple-steps/step-three.tsx index ac256ce..18454bd 100644 --- a/example/src/forms/e-multiple-steps/step-three.tsx +++ b/example/src/forms/e-multiple-steps/step-three.tsx @@ -4,21 +4,22 @@ import { InputRef } from 'rc-input' import { ChangeEvent } from 'react' import { useAutoFocus } from '../../helpers/use-autofocus' import { useHandleOnSubmit } from './use-handle-on-submit' -import { FormData } from './schema' +import type { FormValues } from './schema' export function StepThree() { return ( -
- +
+ - + - -
) + +
+ ) } function Email() { - const { form, error, write } = useFormbitContext() + const { form, error, write } = useFormbitContext() const { handleOnSubmit } = useHandleOnSubmit() @@ -27,33 +28,35 @@ function Email() { const ref = useAutoFocus() return ( - - - + + + ) } function Actions() { - const { form: { __metadata } } = useFormbitContext() + const { form: { __metadata } } = useFormbitContext() const handleReset = __metadata?.resetSteps const { handleOnSubmit, isSubmitDisabled, args: { isLoading } } = useHandleOnSubmit() - return <> - - - + return ( + <> + + + + ) } diff --git a/example/src/forms/e-multiple-steps/step-two.tsx b/example/src/forms/e-multiple-steps/step-two.tsx index 9b40589..9de8ff3 100644 --- a/example/src/forms/e-multiple-steps/step-two.tsx +++ b/example/src/forms/e-multiple-steps/step-two.tsx @@ -1,23 +1,23 @@ import { useFormbitContext } from 'formbit' import { Button, FormField, InputNumber, SectionTitle } from '@radicalbit/radicalbit-design-system' import { useAutoFocus } from '../../helpers/use-autofocus' -import { FormData } from './schema' +import type { FormValues } from './schema' import { useHandleNextStep } from './use-handle-next-step' export function StepTwo() { - return <> -
- + return ( +
+ - + - -
- + +
+ ) } function Age() { - const { form, error, write } = useFormbitContext() + const { form, error, write } = useFormbitContext() const [handleOnNext] = useHandleNextStep(['age']) @@ -26,42 +26,40 @@ function Age() { const ref = useAutoFocus() return ( - - - ) + + + + ) } function Actions() { - const { form: { __metadata } } = useFormbitContext() + const { form: { __metadata } } = useFormbitContext() const [handleOnNext, isStepInvalid] = useHandleNextStep(['age']) const prevStep = __metadata?.prevStep return ( - <> - - - - > - Prev - - + + ) } diff --git a/example/src/forms/e-multiple-steps/use-handle-next-step.ts b/example/src/forms/e-multiple-steps/use-handle-next-step.ts index cc6abaa..b4563db 100644 --- a/example/src/forms/e-multiple-steps/use-handle-next-step.ts +++ b/example/src/forms/e-multiple-steps/use-handle-next-step.ts @@ -1,14 +1,9 @@ -import { useFormbitContext, type FormbitValues } from 'formbit' +import { useFormbitContext } from 'formbit' import { useCallback } from 'react' - -type Context = FormbitValues & { - __metadata: { - nextStep?: () => void - } -} +import type { FormValues } from './schema' export const useHandleNextStep = (fields: string[]) => { - const { form: { __metadata }, validateAll, error } = useFormbitContext() + const { form: { __metadata }, validateAll, error } = useFormbitContext() const nextStep = __metadata?.nextStep diff --git a/example/src/forms/e-multiple-steps/use-handle-on-submit-types.ts b/example/src/forms/e-multiple-steps/use-handle-on-submit-types.ts index 946a51e..4b9a251 100644 --- a/example/src/forms/e-multiple-steps/use-handle-on-submit-types.ts +++ b/example/src/forms/e-multiple-steps/use-handle-on-submit-types.ts @@ -1,4 +1,3 @@ -import type { FormbitValues } from 'formbit' import type { UseFakePostResult } from '../fake-api-context/use-fake-post-types' export interface UseHandleOnSubmitResult { @@ -6,9 +5,3 @@ export interface UseHandleOnSubmitResult { isSubmitDisabled: boolean args: Omit } - -export type Context = FormbitValues & { - __metadata?: { - resetSteps?: () => void - } -} diff --git a/example/src/forms/e-multiple-steps/use-handle-on-submit.tsx b/example/src/forms/e-multiple-steps/use-handle-on-submit.tsx index b1e0c5e..bf9eb1c 100644 --- a/example/src/forms/e-multiple-steps/use-handle-on-submit.tsx +++ b/example/src/forms/e-multiple-steps/use-handle-on-submit.tsx @@ -1,10 +1,11 @@ import { useFormbitContext } from 'formbit' import { success } from '../../helpers/message' import { useFakeApiContext } from '../fake-api-context' -import type { Context, UseHandleOnSubmitResult } from './use-handle-on-submit-types' +import type { UseHandleOnSubmitResult } from './use-handle-on-submit-types' +import type { FormValues } from './schema' export const useHandleOnSubmit = (): UseHandleOnSubmitResult => { - const { form: { __metadata }, submitForm, isFormInvalid, resetForm, isDirty } = useFormbitContext() + const { form: { __metadata }, submitForm, isFormInvalid, resetForm, isDirty } = useFormbitContext() const resetSteps = __metadata?.resetSteps const { fakePost } = useFakeApiContext() diff --git a/example/src/forms/f-remove-all/index.tsx b/example/src/forms/f-remove-all/index.tsx index 8f8c31a..3655942 100644 --- a/example/src/forms/f-remove-all/index.tsx +++ b/example/src/forms/f-remove-all/index.tsx @@ -8,14 +8,14 @@ import { FormbitContextProvider, useFormbitContext } from 'formbit' import { InputRef } from 'rc-input' import { ChangeEvent } from 'react' import { useAutoFocus } from '../../helpers/use-autofocus' -import { FormData, schema } from './schema' +import { type FormValues, schema } from './schema' import { useHandleOnSubmit } from './use-handle-on-submit' import { useInitializeForm } from './use-initialize-form' import { useFakeApiContext } from '../fake-api-context' export function WriteRemoveAllForm() { return ( - + ) @@ -53,7 +53,7 @@ function IsLoading() {
- + @@ -101,7 +101,7 @@ function IsSuccess() { } function Name() { - const { form, error, write } = useFormbitContext() + const { form, error, write } = useFormbitContext() const { handleOnSubmit } = useHandleOnSubmit() @@ -124,7 +124,7 @@ function Name() { } function Surname() { - const { form, error, write } = useFormbitContext() + const { form, error, write } = useFormbitContext() const { handleOnSubmit } = useHandleOnSubmit() @@ -144,7 +144,7 @@ function Surname() { } function Age() { - const { form, error, write } = useFormbitContext() + const { form, error, write } = useFormbitContext() const { handleOnSubmit } = useHandleOnSubmit() @@ -160,11 +160,12 @@ function Age() { value={form.age} required /> - ) + + ) } function Actions() { - const { resetForm, removeAll, writeAll } = useFormbitContext() + const { resetForm, removeAll, writeAll } = useFormbitContext() const { handleOnSubmit, isSubmitDisabled, args: { isLoading } } = useHandleOnSubmit() diff --git a/example/src/forms/f-remove-all/schema.ts b/example/src/forms/f-remove-all/schema.ts index 528c61a..0b7d36b 100644 --- a/example/src/forms/f-remove-all/schema.ts +++ b/example/src/forms/f-remove-all/schema.ts @@ -6,4 +6,4 @@ export const schema = yup.object().shape({ age: yup.number().min(18).max(200).required() }) -export type FormData = yup.InferType +export type FormValues = yup.InferType diff --git a/example/src/forms/f-remove-all/use-handle-on-submit.tsx b/example/src/forms/f-remove-all/use-handle-on-submit.tsx index 0702172..604e384 100644 --- a/example/src/forms/f-remove-all/use-handle-on-submit.tsx +++ b/example/src/forms/f-remove-all/use-handle-on-submit.tsx @@ -2,10 +2,10 @@ import { useFormbitContext } from 'formbit' import { success } from '../../helpers/message' import { useFakeApiContext } from '../fake-api-context' import type { UseHandleOnSubmitResult } from './use-handle-on-submit-types' -import type { FormData } from './schema' +import type { FormValues } from './schema' export const useHandleOnSubmit = (): UseHandleOnSubmitResult => { - const { submitForm, isFormInvalid, resetForm, isDirty } = useFormbitContext() + const { submitForm, isFormInvalid, resetForm, isDirty } = useFormbitContext() const { fakePost } = useFakeApiContext() const { mutate, ...args } = fakePost diff --git a/example/src/forms/f-remove-all/use-initialize-form.ts b/example/src/forms/f-remove-all/use-initialize-form.ts index 4badf58..817926f 100644 --- a/example/src/forms/f-remove-all/use-initialize-form.ts +++ b/example/src/forms/f-remove-all/use-initialize-form.ts @@ -1,17 +1,19 @@ import { useFormbitContext } from 'formbit' import { useEffect } from 'react' import { useFakeApiContext } from '../fake-api-context' -import { FormData } from './schema' +import type { FormValues } from './schema' export const useInitializeForm = () => { - const { initialize } = useFormbitContext() + const { initialize } = useFormbitContext() const { fakeUser } = useFakeApiContext() const { data: user } = fakeUser useEffect(() => { if (user) { - initialize({ ...user }) + // The fake user carries an `email` this form's schema doesn't have, + // so we only initialize the fields this form actually manages. + initialize({ name: user.name, surname: user.surname }) } }, [initialize, user]) } diff --git a/example/src/forms/fake-api-context/use-get-fake-user.ts b/example/src/forms/fake-api-context/use-get-fake-user.ts index 553a3ec..e8dc0ca 100644 --- a/example/src/forms/fake-api-context/use-get-fake-user.ts +++ b/example/src/forms/fake-api-context/use-get-fake-user.ts @@ -24,6 +24,9 @@ export const useGetFakeUser = (): UseGetFakeUserResult => { setIsSuccess(false) const fakeGet = () => { + // Randomly fails ~20% of the time on purpose, to demo the error UI + // (see IsError / Retry in d-edit-like and f-remove-all). Note: this makes + // the Cypress tests that depend on this fetch (edit-like) non-deterministic. if (Math.random() < 0.2) { setError(new Error('Failed to fetch user')) setUser(undefined) diff --git a/example/yarn.lock b/example/yarn.lock index d089724..b3d5446 100644 --- a/example/yarn.lock +++ b/example/yarn.lock @@ -698,10 +698,10 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@radicalbit/radicalbit-design-system@2.13.1": - version "2.13.1" - resolved "https://registry.yarnpkg.com/@radicalbit/radicalbit-design-system/-/radicalbit-design-system-2.13.1.tgz#1017d2382033c24a1d5e795f58d67a9a1788bef0" - integrity sha512-QBR+UHDRfLh0TzRBnDLoKBKohgEGVb5vQ5rX4fsOTPe4BE1ZQ1x6H7srYZO1nHVRK6iumuAiQjkSki5ZbV2BbQ== +"@radicalbit/radicalbit-design-system@2.19.5": + version "2.19.5" + resolved "https://registry.yarnpkg.com/@radicalbit/radicalbit-design-system/-/radicalbit-design-system-2.19.5.tgz#5099f2c7699b4b0a8f00ef0dee07a07c4335129d" + integrity sha512-jkkualwnXtf/lWjHpZ6ALJR/TQIHNfbltdxG1+tp4VcPs1BsZLGm/t+YPhLpEOSz8vSQhaCVeg2aozf2VOL2hA== dependencies: "@babel/polyfill" "7.12.1" "@fortawesome/fontawesome-svg-core" "6.7.2" @@ -2502,7 +2502,7 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@^8.57.0: +eslint@8.57.0: version "8.57.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== diff --git a/src/__tests__/check.test.ts b/src/__tests__/check.test.ts index 693cd01..eb76d79 100644 --- a/src/__tests__/check.test.ts +++ b/src/__tests__/check.test.ts @@ -39,6 +39,28 @@ describe('check fn', () => { unmount() }) + it('Should validate against the schema set via setSchema, not the initial one', () => { + const emptyInitialSchema = Yup.object() + const invalidJson = { + age: 2 + } + + const { result, unmount } = renderHook(() => useFormbit({ initialValues, yup: emptyInitialSchema })) + + // With the initial (empty) schema the json is valid. + expect(result.current.check(invalidJson)).toBe(undefined) + + act(() => result.current.setSchema(Yup.object({ age: Yup.number().min(18) }))) + + // After setSchema, check must use the new schema. + const errors = result.current.check(invalidJson) + + expect(errors).toHaveLength(1) + expect(errors?.[0]?.path).toBe('age') + + unmount() + }) + it('Should execute given successCallback only once', () => { const validJson = { age: 20 diff --git a/src/__tests__/initialize.test.ts b/src/__tests__/initialize.test.ts index 3994202..81759e4 100644 --- a/src/__tests__/initialize.test.ts +++ b/src/__tests__/initialize.test.ts @@ -1,5 +1,5 @@ import { act, renderHook } from '@testing-library/react' -import { Form } from 'src/types' +import { FormbitValues } from 'src/types' import useFormbit from 'src/use-formbit' import * as Yup from 'yup' @@ -85,7 +85,7 @@ describe('initialize fn', () => { act(() => result.current.initialize(newInitialValues)) - expect((result.current.form as Form).__metadata).toStrictEqual(initialValues.__metadata) + expect((result.current.form as FormbitValues).__metadata).toStrictEqual(initialValues.__metadata) unmount() }) @@ -98,7 +98,7 @@ describe('initialize fn', () => { act(() => result.current.initialize(newInitialValues)) - expect((result.current.form as Form).__metadata).toStrictEqual(newInitialValues.__metadata) + expect((result.current.form as FormbitValues).__metadata).toStrictEqual(newInitialValues.__metadata) unmount() }) diff --git a/src/__tests__/is-validation-error.test.ts b/src/__tests__/is-validation-error.test.ts new file mode 100644 index 0000000..5171af1 --- /dev/null +++ b/src/__tests__/is-validation-error.test.ts @@ -0,0 +1,33 @@ +import { isValidationError } from 'src/types/helpers' +import * as Yup from 'yup' + +describe('isValidationError type guard', () => { + it('Should return true for a real yup ValidationError', () => { + try { + Yup.object({ age: Yup.number().min(18) }).validateSync({ age: 2 }) + } catch (e) { + expect(isValidationError(e)).toBe(true) + } + }) + + it('Should return true when path is undefined but message is a valid string', () => { + const error = { message: 'some error', path: undefined, inner: [] } + + expect(isValidationError(error)).toBe(true) + }) + + it('Should return false when message is not a string (path undefined)', () => { + // Bug regression: the guard must validate `message`, not re-check `path`. + const error = { message: 123, path: undefined, inner: [] } + + expect(isValidationError(error)).toBe(false) + }) + + it('Should return false for plain objects and primitives', () => { + expect(isValidationError(null)).toBe(false) + expect(isValidationError(undefined)).toBe(false) + expect(isValidationError('error')).toBe(false) + expect(isValidationError({ message: 'x', path: 'y' })).toBe(false) + expect(isValidationError({ message: 'x', path: 'y', inner: 'not-array' })).toBe(false) + }) +}) diff --git a/src/__tests__/remove-all.test.ts b/src/__tests__/remove-all.test.ts index ab59287..74c49d4 100644 --- a/src/__tests__/remove-all.test.ts +++ b/src/__tests__/remove-all.test.ts @@ -36,4 +36,25 @@ describe('removeAll fn', () => { unmount() }) + + it('Should re-validate fields with active live-validation, like writeAll does', () => { + const initialValues = { firstName: 'Jane', lastName: 'Doe', age: 10 } + + const { result, unmount } = renderHook(() => useFormbit({ initialValues, yup: schema })) + + // Make `age` live-validated: it fails validation, so formbit marks it as live-validated. + act(() => result.current.validate('age')) + expect(result.current.liveValidation('age')).toBe(true) + expect(result.current.error('age')).toBeTruthy() + + // Fix `age` to a valid value WITHOUT validating it explicitly. + act(() => result.current.write('age', 30, { noLiveValidation: true, pathsToValidate: [] })) + + // Removing another field must re-run live-validation on `age` and clear its (now stale) error. + act(() => result.current.removeAll(['firstName'])) + + expect(result.current.error('age')).toBeFalsy() + + unmount() + }) }) diff --git a/src/__tests__/use-formbit-context.test.tsx b/src/__tests__/use-formbit-context.test.tsx index b4035d8..cdf6803 100644 --- a/src/__tests__/use-formbit-context.test.tsx +++ b/src/__tests__/use-formbit-context.test.tsx @@ -1,14 +1,14 @@ import { act, renderHook } from '@testing-library/react' import { PropsWithChildren } from 'react' import FormbitContextProvider, { useFormbitContext } from 'src/formbit-context' -import { InitialValues, ValidationSchema } from 'src/types' +import { FormbitValues, ValidationSchema } from 'src/types' import * as Yup from 'yup' import { TEST_ERROR_MESSAGES } from 'src/helpers/constants' -const renderWithContext = (initialValues: InitialValues, schema: ValidationSchema<{}>) => { +const renderWithContext = (initialValues: FormbitValues, schema: ValidationSchema<{}>) => { const wrapper = ({ children }: PropsWithChildren) => - {children} + {children} return renderHook(() => useFormbitContext(), { wrapper }) } diff --git a/src/formbit-context.tsx b/src/formbit-context.tsx index 3b82728..3399cfd 100644 --- a/src/formbit-context.tsx +++ b/src/formbit-context.tsx @@ -1,19 +1,19 @@ import React, { useContext, createContext, PropsWithChildren } from 'react' import useFormbit from './use-formbit' import * as yup from 'yup' -import { FormbitObject, InitialValues, ValidationSchema } from './types' +import { FormbitObject, FormbitValues, ValidationSchema } from './types' import { MISSING_CONTEXT_ERROR } from './helpers/constants' import { once } from 'lodash' -type Props = { +type Props = { initialValues?: Partial | {} schema: ValidationSchema } & PropsWithChildren const createFormbitContext = - once(() => createContext | undefined>(undefined)) + once(() => createContext | undefined>(undefined)) -export default function FormbitContextProvider({ +export default function FormbitContextProvider({ initialValues = {}, schema, children @@ -27,7 +27,7 @@ export default function FormbitContextProvider({ ) } -export const useFormbitContext = () => { +export const useFormbitContext = () => { const context = useContext(createFormbitContext()) if (!context) { diff --git a/src/index.ts b/src/index.ts index d6d4f11..b19817d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,4 +3,46 @@ import FormbitContextProvider, { useFormbitContext } from './formbit-context' export default useFormbit export { FormbitContextProvider, useFormbitContext } -export type { FormState, FormbitValues } from './types' + +export type { + // Core value types + FormbitValues, + FormState, + Errors, + LiveValidation, + + // The object returned by the hooks + FormbitObject, + + // Yup re-exports + ValidationSchema, + ValidateOptions, + ValidationError, + + // Callbacks + SuccessCallback, + ErrorCallback, + CheckSuccessCallback, + CheckErrorCallback, + SubmitSuccessCallback, + + // Method signatures + Check, + Initialize, + Write, + WriteAll, + WriteAllValue, + Remove, + RemoveAll, + Validate, + ValidateAll, + ValidateForm, + SubmitForm, + SetError, + SetSchema, + + // Options + CheckFnOptions, + ValidateFnOptions, + WriteFnOptions +} from './types' diff --git a/src/types/helpers.ts b/src/types/helpers.ts index 4cad635..4e6f2e9 100644 --- a/src/types/helpers.ts +++ b/src/types/helpers.ts @@ -5,7 +5,7 @@ export const isValidationError = (error: unknown): error is ValidationError => { const { path, message, inner } = error return (typeof path === 'string' || path === undefined) && - (typeof message === 'string' || path === undefined) && + (typeof message === 'string' || message === undefined) && Array.isArray(inner) } diff --git a/src/types/index.ts b/src/types/index.ts index 0151805..931e7c7 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,440 +1,270 @@ import { ObjectSchema, ValidationError as YupValidationError, ValidateOptions as YupValidateOptions } from 'yup' import { ACTIONS } from '../helpers/constants' -// ─── Internal / Utility Types ──────────────────────────────────────────────── +// ─── Core value types ──────────────────────────────────────────────────────── /** - * @internal - * @private - */ -export type Action = keyof typeof ACTIONS - -/** - * @internal - * Generic object with string keys. - */ -export type FormbitRecord = Record - -/** @deprecated Use {@link FormbitRecord} instead. Renamed to avoid shadowing the global `Object`. */ -export type Object = FormbitRecord - -/** - * @internal - */ -export type GenericCallback = SuccessCallback | ErrorCallback - -/** - * @internal - */ -export type ValidationFormbitError = Pick - -/** - * @internal - * @private - */ -export type WriteOrRemove = - (path: keyof Values | string, value: unknown, options?: WriteFnOptions, action?: Action) => void - -/** - * @internal - * @private + * Base shape of every form handled by formbit: an open record of values, plus an + * optional `__metadata` field formbit uses to carry data that must survive a + * reset/initialize but must NOT be submitted. + * + * The generic `T` you pass to `useFormbit()` must extend this type. */ -export type PrivateValidateForm = ( - successCallback?: SuccessCallback, - errorCallback?: ErrorCallback>, - options?: { options?: ValidateOptions, isDirty?: boolean }) => void - -// ─── Core Value Types ──────────────────────────────────────────────────────── +export type FormbitValues = Record & { __metadata?: Record } /** - * Base type for form values: a record of string keys with an optional `__metadata` field. - */ -export type FormbitValues = { __metadata?: FormbitRecord } & FormbitRecord - -/** Object containing the updated form. */ -export type Form = FormbitValues - -/** InitialValues used to set up formbit; also used to reset the form to its original version. */ -export type InitialValues = FormbitValues - -/** - * Object including all the registered error messages since the last validation. - * Errors are stored using the same path of the corresponding form values. + * Error messages registered since the last validation, stored under the same + * dot-path as the corresponding form value. * * @example - * If the form object has this structure: - * ```json - * { - * "age": 1 - * } - * ``` - * and age is a non valid field, errors object will look like this - * ```json - * { - * "age": "Age must be greater then 18" - * } - * ``` + * form: { age: 1 } + * errors: { age: "Age must be greater than 18" } */ export type Errors = Record /** - * Object including all the values that are being live validated. - * Usually fields that fail validation (using one of the methods that triggers validation) - * will automatically be set to live-validated. - * - * A value/path is live-validated when validated at every change of the form. - * - * By default no field is live-validated. + * Fields currently under live-validation (re-validated on every form change). + * A field is added here automatically when it fails a validation. Empty by default. * * @example - * If the form object has this structure: - * ```json - * { - * "age": 1 - * } - * ``` - * and age is a field that is being live-validated, liveValidation object will look like this - * ```json - * { - * "age": true - * } - * ``` + * form: { age: 1 } + * liveValidation: { age: true } */ export type LiveValidation = Record -// ─── FormState (formerly Writer) ───────────────────────────────────────────── - /** - * Internal form state storing all the data of the form (except the validation schema). + * The whole internal state of the form (everything except the validation schema). */ -export type FormState = { - form: Values, - initialValues: Values +export type FormState = { + form: T, + initialValues: T, errors: Errors, liveValidation: LiveValidation, isDirty: boolean, } -/** @deprecated Use {@link FormState} instead. */ -export type Writer = FormState +// ─── Yup re-exports ──────────────────────────────────────────────────────────── -// ─── Yup Re-exports ───────────────────────────────────────────────────────── +/** A validation schema built with `yup.object()`. See {@link https://github.com/jquense/yup}. */ +export type ValidationSchema = ObjectSchema -/** - * Type imported from the yup library. - * It represents any validation schema created with the yup.object() method. - * - * Link to the Yup documentation {@link https://github.com/jquense/yup} - */ -export type ValidationSchema = ObjectSchema - -/** - * Type imported from the yup library. - * It represents the object with all the options that can be passed to the internal yup validation method. - * - * Link to the Yup documentation {@link https://github.com/jquense/yup} - */ +/** Options forwarded to yup's validation methods. See {@link https://github.com/jquense/yup}. */ export type ValidateOptions = YupValidateOptions -/** - * Type imported from the yup library. - * It represents the error object returned when a validation fails. - * - * Link to the Yup documentation {@link https://github.com/jquense/yup} - */ +/** The error object yup throws when a validation fails. See {@link https://github.com/jquense/yup}. */ export type ValidationError = YupValidationError -// ─── Callback Types ────────────────────────────────────────────────────────── +// ─── Callbacks ─────────────────────────────────────────────────────────────── -/** - * Success callback invoked by some formbit methods when the operation is successful. - */ -export type SuccessCallback = (writer: FormState, setError: SetError) => void - -/** - * Invoked in case of errors raised by validation. - */ -export type ErrorCallback = (writer: FormState, setError: SetError) => void - -/** - * Success callback invoked by the check method when the operation is successful. - */ -export type CheckSuccessCallback = - (json: Form, writer: FormState, setError: SetError) => void +/** Invoked by validation methods when the form (or the validated paths) are valid. */ +export type SuccessCallback = + (writer: FormState, setError: SetError) => void -/** @deprecated Use {@link CheckSuccessCallback} instead. */ -export type SuccessCheckCallback = CheckSuccessCallback +/** Invoked by validation methods when validation fails. */ +export type ErrorCallback = + (writer: FormState, setError: SetError) => void -/** - * Invoked in case of errors raised by validation of check method. - */ -export type CheckErrorCallback = - (json: Form, inner: ValidationError[], writer: FormState, setError: SetError) => void +/** Invoked by `check()` when the given json is valid. */ +export type CheckSuccessCallback = + (json: FormbitValues, writer: FormState, setError: SetError) => void -/** @deprecated Use {@link CheckErrorCallback} instead. */ -export type ErrorCheckCallback = CheckErrorCallback +/** Invoked by `check()` when the given json is invalid. */ +export type CheckErrorCallback = + (json: FormbitValues, inner: ValidationError[], writer: FormState, setError: SetError) => void /** - * Success callback invoked by the submit method when the validation is successful. - * Is the right place to send your data to the backend. + * Invoked by `submitForm()` once the whole form is valid — the place to send data + * to the backend. `__metadata` is stripped from `writer.form` before this runs. */ -export type SubmitSuccessCallback = +export type SubmitSuccessCallback = ( - writer: FormState>, + writer: FormState>, setError: SetError, clearIsDirty: () => void ) => void -// ─── Deprecated Single-Use Aliases (kept for backward compatibility) ───────── - -/** @deprecated Inlined into {@link FormbitObject}. */ -export type ErrorFn = (path: string) => string | undefined - -/** @deprecated Inlined into {@link FormbitObject}. */ -export type IsFormValid = () => boolean - -/** @deprecated Inlined into {@link FormbitObject}. */ -export type IsFormInvalid = () => boolean - -/** @deprecated Inlined into {@link FormbitObject}. */ -export type ClearIsDirty = () => void - -/** @deprecated Inlined into {@link FormbitObject}. */ -export type ResetForm = () => void - -/** @deprecated Inlined into {@link FormbitObject}. */ -export type LiveValidationFn = (path: string) => true | undefined - -/** @deprecated Inlined into {@link FormbitObject}. */ -export type IsDirty = boolean - -// ─── Method Types ──────────────────────────────────────────────────────────── +// ─── Method signatures ───────────────────────────────────────────────────────── /** See {@link FormbitObject.check}. */ -export type Check = - (json: Form, options?: CheckFnOptions) => ValidationError[] | undefined +export type Check = + (json: FormbitValues, options?: CheckFnOptions) => ValidationError[] | undefined /** See {@link FormbitObject.initialize}. */ -export type Initialize = (values: Partial) => void +export type Initialize = (values: Partial) => void /** See {@link FormbitObject.remove}. */ -export type Remove = (path: string, options?: WriteFnOptions) => void +export type Remove = (path: string, options?: WriteFnOptions) => void /** See {@link FormbitObject.setError}. */ export type SetError = (path: string, value: string) => void /** See {@link FormbitObject.setSchema}. */ -export type SetSchema = (newSchema: ValidationSchema) => void +export type SetSchema = (newSchema: ValidationSchema) => void -/** See {@link FormbitObject.submitForm}. */ -export type SubmitForm = ( - successCallback: SubmitSuccessCallback, - errorCallback?: ErrorCallback>, - options?: ValidateOptions) => void +/** A single `[path, value]` pair accepted by `writeAll`. */ +export type WriteAllValue = [keyof T | string, unknown] + +/** See {@link FormbitObject.write}. */ +export type Write = + (path: keyof T | string, value: unknown, options?: WriteFnOptions) => void + +/** See {@link FormbitObject.writeAll}. */ +export type WriteAll = + (arr: WriteAllValue[], options?: WriteFnOptions) => void + +/** See {@link FormbitObject.removeAll}. */ +export type RemoveAll = + (arr: string[], options?: WriteFnOptions) => void /** See {@link FormbitObject.validate}. */ -export type Validate = (path: string, options?: ValidateFnOptions) => void +export type Validate = (path: string, options?: ValidateFnOptions) => void /** See {@link FormbitObject.validateAll}. */ -export type ValidateAll = (paths: string[], options?: ValidateFnOptions) => void +export type ValidateAll = (paths: string[], options?: ValidateFnOptions) => void /** See {@link FormbitObject.validateForm}. */ -export type ValidateForm = ( - successCallback?: SuccessCallback, - errorCallback?: ErrorCallback, +export type ValidateForm = ( + successCallback?: SuccessCallback, + errorCallback?: ErrorCallback, options?: ValidateOptions) => void -/** See {@link FormbitObject.write}. */ -export type Write = - (path: keyof Values | string, value: unknown, options?: WriteFnOptions) => void - -/** See {@link FormbitObject.writeAll}. */ -export type WriteAll = - (arr: WriteAllValue[], options?: WriteFnOptions) => void - -/** See {@link FormbitObject.removeAll}. */ -export type RemoveAll = - (arr: string[], options?: WriteFnOptions) => void - -/** - * Tuple of [key, value] pair. - */ -export type WriteAllValue = [keyof Values | string, unknown] +/** See {@link FormbitObject.submitForm}. */ +export type SubmitForm = ( + successCallback: SubmitSuccessCallback, + errorCallback?: ErrorCallback>, + options?: ValidateOptions) => void -// ─── Options Types ─────────────────────────────────────────────────────────── +// ─── Options ───────────────────────────────────────────────────────────────── -/** - * Options object to change the behavior of the check method. - */ -export type CheckFnOptions = { - successCallback?: CheckSuccessCallback, - errorCallback?: CheckErrorCallback, +/** Options accepted by `check()`. */ +export type CheckFnOptions = { + successCallback?: CheckSuccessCallback, + errorCallback?: CheckErrorCallback, options?: ValidateOptions } -/** - * Options object to change the behavior of the validate methods. - */ -export type ValidateFnOptions = { - successCallback?: SuccessCallback>, - errorCallback?: ErrorCallback>, +/** Options accepted by the `validate` methods. */ +export type ValidateFnOptions = { + successCallback?: SuccessCallback>, + errorCallback?: ErrorCallback>, options?: ValidateOptions } -/** - * Options object to change the behavior of the write methods. - */ -export type WriteFnOptions = { +/** Options accepted by the `write`/`remove` methods (validate options plus path control). */ +export type WriteFnOptions = { noLiveValidation?: boolean, pathsToValidate?: string[] -} & ValidateFnOptions +} & ValidateFnOptions + +// ─── Internal types (not part of the public surface) ────────────────────────── + +/** @internal */ +export type Action = keyof typeof ACTIONS + +/** @internal */ +export type GenericCallback = SuccessCallback | ErrorCallback + +/** @internal Subset of a yup ValidationError kept by formbit's sync validation. */ +export type ValidationFormbitError = Pick + +/** @internal */ +export type WriteOrRemove = + (path: keyof T | string, value: unknown, options?: WriteFnOptions, action?: Action) => void + +/** @internal */ +export type PrivateValidateForm = ( + successCallback?: SuccessCallback, + errorCallback?: ErrorCallback>, + options?: { options?: ValidateOptions }) => void // ─── FormbitObject ─────────────────────────────────────────────────────────── /** - * Object returned by useFormbit() and useFormbitContextHook(). - * It contains all the data and methods needed to handle the form. + * The object returned by `useFormbit()` and `useFormbitContext()`. Holds the form + * state and every method needed to read, mutate and validate the form. */ -export type FormbitObject = { +export type FormbitObject = { // --- State --- - /** - * Object containing the updated form. - */ - form: Partial, + /** The current form values. Partial: fields may be missing until validated. */ + form: Partial, /** - * Object including all the registered error messages since the last validation. - * Errors are stored using the same path of the corresponding form values. + * Error messages registered since the last validation, keyed by the value's dot-path. * * @example - * If the form object has this structure: - * ```json - * { - * "age": 1 - * } - * ``` - * and age is a non valid field, errors object will look like this - * ```json - * { - * "age": "Age must be greater then 18" - * } - * ``` + * form: { age: 1 } + * errors: { age: "Age must be greater than 18" } */ errors: Errors, - /** - * Returns true if the form is Dirty (user already interacted with the form), false otherwise. - */ + /** True once the user has interacted with the form. */ isDirty: boolean, - // --- Queries --- + // --- Queries (never trigger validation) --- - /** - * Returns the error message for the given path if any. - * It doesn't trigger any validation. - */ + /** Returns the error message registered for `path`, if any. */ error: (path: string) => string | undefined, - /** - * Returns true if the form is valid. - * It doesn't perform any validation, it checks if any errors are present. - */ + /** True if no errors are currently registered. Does not run validation. */ isFormValid: () => boolean, - /** - * Returns true if the form is NOT valid. - * It doesn't perform any validation, it checks if any errors are present. - */ + /** True if any error is currently registered. Does not run validation. */ isFormInvalid: () => boolean, - /** - * Returns true if live validation is active for the given path. - */ + /** True if live-validation is active for `path`. */ liveValidation: (path: string) => true | undefined, - /** - * Checks the given json against the form schema and returns an array of errors. - * It returns undefined if the json is valid. - */ - check: Check>, + /** Validates `json` against the current schema; returns the errors, or undefined if valid. */ + check: Check>, // --- Mutations --- /** - * This method updates the form state writing $value into the $path, setting isDirty to true. - * - * After writing, it validates all the paths contained into $pathsToValidate (if any) - * and all the fields that have the live validation active. + * Writes `value` at `path`, sets `isDirty`, then validates `pathsToValidate` + * plus every live-validated field. */ - write: Write, + write: Write, /** - * This method takes an array of [path, value] and updates the form state writing - * all those values into the specified paths. - * - * It sets isDirty to true. - * - * After writing, it validates all the paths contained into $pathToValidate and all - * the fields that have the live validation active. + * Writes every `[path, value]` pair, sets `isDirty`, then validates + * `pathsToValidate` plus every live-validated field. */ - writeAll: WriteAll, + writeAll: WriteAll, /** - * This method updates the form state deleting value, setting isDirty to true. - * - * After writing, it validates all the paths contained into pathsToValidate (if any) - * and all the fields that have the live validation active. + * Removes the value at `path`, sets `isDirty`, then validates `pathsToValidate` + * plus every live-validated field. */ - remove: Remove, + remove: Remove, /** - * This method updates the form state deleting multiple values, setting isDirty to true. + * Removes every given path, sets `isDirty`, then validates `pathsToValidate` + * plus every live-validated field. */ - removeAll: RemoveAll, + removeAll: RemoveAll, - /** - * Initialize the form with new initial values. - */ - initialize: Initialize, + /** Re-initializes the form with new initial values. */ + initialize: Initialize, - /** - * Reset form to the initial state. - * Errors and liveValidation are set back to empty objects. - * isDirty is set back to false. - */ + /** Resets form, errors, liveValidation and isDirty back to their initial state. */ resetForm: () => void, - /** - * Set a message (value) to the given error path. - */ + /** Sets the error message at `path`. */ setError: SetError, - /** - * Override the current schema with the given one. - */ - setSchema: SetSchema, + /** Replaces the current validation schema. */ + setSchema: SetSchema, - /** - * This method only validates the specified path. Does not check for fields that have the - * live validation active. - */ - validate: Validate, + /** Validates only `path` (ignores live-validated fields). */ + validate: Validate, - /** - * This method only validates the specified paths. Does not check for fields that have the - * live validation active. - */ - validateAll: ValidateAll, + /** Validates only the given `paths` (ignores live-validated fields). */ + validateAll: ValidateAll, - /** - * This method validates the entire form and sets the corresponding errors if any. - */ - validateForm: ValidateForm>, + /** Validates the whole form and registers any error. */ + validateForm: ValidateForm>, - /** - * Perform a validation against the current form object, and execute the successCallback if the validation passes, - * otherwise it executes the errorCallback. - */ - submitForm: SubmitForm, + /** Validates the whole form and, if valid, runs the success callback to submit. */ + submitForm: SubmitForm, } diff --git a/src/use-execute-callbacks.ts b/src/use-execute-callbacks.ts index 8358e6a..27b4837 100644 --- a/src/use-execute-callbacks.ts +++ b/src/use-execute-callbacks.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect, useRef } from 'react' -import { FormState, GenericCallback, InitialValues, SetError } from './types' +import { FormState, GenericCallback, FormbitValues, SetError } from './types' import { isEmpty } from 'lodash' /** @@ -10,8 +10,8 @@ import { isEmpty } from 'lodash' * * */ -export default (writer: FormState, setError: SetError) => { - const callbacksStore = useRef> | undefined>>({}) +export default (writer: FormState, setError: SetError) => { + const callbacksStore = useRef> | undefined>>({}) useEffect(() => { if (isEmpty(callbacksStore.current)) { @@ -32,7 +32,7 @@ export default (writer: FormState, setErro * @param cb Callback that needs to be executed. * */ - return useCallback((uuid: string, cb?: GenericCallback>) => { + return useCallback((uuid: string, cb?: GenericCallback>) => { if (cb) { callbacksStore.current = { ...callbacksStore.current, [uuid]: cb } } diff --git a/src/use-formbit.ts b/src/use-formbit.ts index 250b410..06b8ec2 100644 --- a/src/use-formbit.ts +++ b/src/use-formbit.ts @@ -7,7 +7,7 @@ import { Check, FormbitObject, FormState, - InitialValues, + FormbitValues, LiveValidation, PrivateValidateForm, Remove, @@ -29,18 +29,18 @@ import useExecuteCallbacks from './use-execute-callbacks' import { cloneDeep, get, isEmpty, omit, set } from 'lodash' import { v4 as uuidv4 } from 'uuid' -type UseFormbitParams = { - initialValues?: Partial, - yup: ValidationSchema +type UseFormbitParams = { + initialValues?: Partial, + yup: ValidationSchema } -export default ({ +export default ({ initialValues = {}, yup: schema -}: UseFormbitParams): FormbitObject => { - const schemaRef = useRef>(schema) +}: UseFormbitParams): FormbitObject => { + const schemaRef = useRef>(schema) - const [writer, setWriter] = useState>>({ + const [writer, setWriter] = useState>>({ form: initialValues, initialValues, errors: {}, @@ -48,7 +48,7 @@ export default ({ isDirty: false }) - const initialize = useCallback((values: Partial) => { + const initialize = useCallback((values: Partial) => { const { __metadata } = values if (__metadata) { @@ -79,7 +79,7 @@ export default ({ }) }, []) - const setSchema = useCallback((newSchema: ValidationSchema) => { schemaRef.current = newSchema }, []) + const setSchema = useCallback((newSchema: ValidationSchema) => { schemaRef.current = newSchema }, []) const setError: SetError = useCallback((path, value) => { setWriter((w) => { @@ -89,9 +89,9 @@ export default ({ }) }, []) - const executeCb = useExecuteCallbacks>(writer, setError) + const executeCb = useExecuteCallbacks>(writer, setError) - const writeOrRemove: WriteOrRemove = useCallback(( + const writeOrRemove: WriteOrRemove = useCallback(( path, value, { @@ -119,13 +119,13 @@ export default ({ switch (action) { case ACTIONS.write: return set(cloneDeep(w.form), path, value) - case ACTIONS.remove: return omit>(cloneDeep(w.form), path) + case ACTIONS.remove: return omit>(cloneDeep(w.form), path) default: return cloneDeep(w.form) } }()) - const newWriter: FormState> = { ...w, form, isDirty: true } + const newWriter: FormState> = { ...w, form, isDirty: true } if (paths.length === 0) { newUUID && executeCb(newUUID, successCallback) @@ -168,13 +168,13 @@ export default ({ }) }, [executeCb]) - const write: Write = useCallback((path, value, options) => + const write: Write = useCallback((path, value, options) => writeOrRemove(path, value, options, ACTIONS.write), [writeOrRemove]) - const remove: Remove = useCallback((path, options) => + const remove: Remove = useCallback((path, options) => writeOrRemove(path, undefined, options, ACTIONS.remove), [writeOrRemove]) - const writeAll: WriteAll = useCallback(( + const writeAll: WriteAll = useCallback(( arr, { noLiveValidation = false, @@ -244,7 +244,7 @@ export default ({ }) }, [executeCb]) - const removeAll: RemoveAll = useCallback( + const removeAll: RemoveAll = useCallback( ( arr, { @@ -279,12 +279,12 @@ export default ({ return newWriter } - const cleanErrors = pathsToValidate.reduce( + const cleanErrors = paths.reduce( (acc, key) => set(acc, key, undefined), cloneDeep(newWriter.errors) ) - const inner = validateSyncAll(pathsToValidate, schemaRef.current, newWriter.form, options) + const inner = validateSyncAll(paths, schemaRef.current, newWriter.form, options) if (isEmpty(inner)) { const neww = { ...newWriter, errors: cleanErrors } @@ -312,7 +312,7 @@ export default ({ [executeCb] ) - const validate: Validate = useCallback(( + const validate: Validate = useCallback(( path, { successCallback, @@ -361,7 +361,7 @@ export default ({ }) }, [executeCb]) - const validateAll: ValidateAll = useCallback(( + const validateAll: ValidateAll = useCallback(( paths, { successCallback, errorCallback, options } = {} ) => { @@ -409,7 +409,7 @@ export default ({ }) }, [executeCb]) - const check: Check> = useCallback(( + const check: Check> = useCallback(( json, { successCallback, @@ -418,7 +418,7 @@ export default ({ } = {} ) => { try { - schema.validateSync(json, { abortEarly: false, ...options }) + schemaRef.current.validateSync(json, { abortEarly: false, ...options }) successCallback?.(json, writer, setError) return undefined @@ -433,12 +433,12 @@ export default ({ return undefined } - }, [schema, setError, writer]) + }, [setError, writer]) - const privateValidateForm: PrivateValidateForm> = useCallback(( + const privateValidateForm: PrivateValidateForm> = useCallback(( successCallback, errorCallback, - { isDirty: _, options } = {} + { options } = {} ) => { const newUUID = (function getUUID() { if (successCallback || errorCallback) { @@ -484,20 +484,20 @@ export default ({ }) }, [executeCb]) - const validateForm: ValidateForm> = useCallback((successCallback, errorCallback, options = {}) => + const validateForm: ValidateForm> = useCallback((successCallback, errorCallback, options = {}) => privateValidateForm( successCallback, errorCallback, { options } ), [privateValidateForm]) - const submitForm: SubmitForm = + const submitForm: SubmitForm = useCallback((successCallback, errorCallback, options = {}) => { const fn = () => setWriter((w) => ({ ...w, isDirty: false })) - const successCallbackAndClearIsDirty: SuccessCallback> = (a, b) => { - // Success callback is called only if the form is valid so we can safely cast a as FormState - const writer = a as FormState + const successCallbackAndClearIsDirty: SuccessCallback> = (a, b) => { + // Success callback is called only if the form is valid so we can safely cast a as FormState + const writer = a as FormState // __metadata is a field used to store metadata about the form and should not be submitted const { __metadata: _, ...form } = writer.form diff --git a/src/validate-sync-all.ts b/src/validate-sync-all.ts index 30e88f1..4c7c187 100644 --- a/src/validate-sync-all.ts +++ b/src/validate-sync-all.ts @@ -1,13 +1,13 @@ import { isEmpty } from 'lodash' -import { Form, InitialValues, ValidateOptions, ValidationFormbitError, ValidationSchema } from './types' +import { FormbitValues, ValidateOptions, ValidationFormbitError, ValidationSchema } from './types' import { isValidationError } from './types/helpers' /* We implement the validateSyncAll because yup.pick won't work with * schema with nested values: https://github.com/jquense/yup/issues/1269 */ -export const validateSyncAll = ( +export const validateSyncAll = ( paths:string[], - schema:ValidationSchema, - form: Form, + schema:ValidationSchema, + form: FormbitValues, options: ValidateOptions = {} ): ValidationFormbitError[] => { let errors: ValidationFormbitError[] = []