From 0b58c3dc9eea692ce3fa2f2dece6d78b67472fe6 Mon Sep 17 00:00:00 2001 From: Luca Tagliabue Date: Wed, 29 Jul 2026 15:15:30 +0200 Subject: [PATCH 01/10] fix: correct message check in isValidationError type guard The second OR branch re-checked `path === undefined` instead of `message === undefined`, so an object with a non-string message and an undefined path was wrongly accepted as a ValidationError. Add a dedicated test suite for the guard covering the regression. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/__tests__/is-validation-error.test.ts | 33 +++++++++++++++++++++++ src/types/helpers.ts | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/__tests__/is-validation-error.test.ts 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/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) } From 428957f721089b3a852971dd23965b8eac7d2abf Mon Sep 17 00:00:00 2001 From: Luca Tagliabue Date: Wed, 29 Jul 2026 15:16:21 +0200 Subject: [PATCH 02/10] fix: make check() respect the schema set via setSchema check() validated against the schema captured at hook init, ignoring setSchema(). Every other validation method already uses schemaRef.current, so check() now does too, making its behaviour consistent. Add a regression test asserting check() uses the updated schema. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/__tests__/check.test.ts | 22 ++++++++++++++++++++++ src/use-formbit.ts | 4 ++-- 2 files changed, 24 insertions(+), 2 deletions(-) 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/use-formbit.ts b/src/use-formbit.ts index 250b410..c2b03ec 100644 --- a/src/use-formbit.ts +++ b/src/use-formbit.ts @@ -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,7 +433,7 @@ export default ({ return undefined } - }, [schema, setError, writer]) + }, [setError, writer]) const privateValidateForm: PrivateValidateForm> = useCallback(( successCallback, From ea8fbb17a63303f2a2401be7e45d5131a8d77313 Mon Sep 17 00:00:00 2001 From: Luca Tagliabue Date: Wed, 29 Jul 2026 15:16:34 +0200 Subject: [PATCH 03/10] fix: re-validate live-validated fields in removeAll MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit removeAll computed `paths` (pathsToValidate + live-validated fields) but then used only `pathsToValidate` for error teardown and validation, so fields with active live-validation were skipped — inconsistent with writeAll, which uses `paths`. Align removeAll to writeAll. Add a regression test asserting live-validated fields are re-validated. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/__tests__/remove-all.test.ts | 21 +++++++++++++++++++++ src/use-formbit.ts | 4 ++-- 2 files changed, 23 insertions(+), 2 deletions(-) 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/use-formbit.ts b/src/use-formbit.ts index c2b03ec..902bcef 100644 --- a/src/use-formbit.ts +++ b/src/use-formbit.ts @@ -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 } From a259df626ad063018e406ea39bd8b042a9fe0406 Mon Sep 17 00:00:00 2001 From: Luca Tagliabue Date: Wed, 29 Jul 2026 15:26:30 +0200 Subject: [PATCH 04/10] feat!: clean up and simplify the public TypeScript types Rewrite src/types/index.ts for readability and correctness, and export the useful types from the package entry point so consumers can type their own code. - Collapse the redundant base types (FormbitRecord, Form, InitialValues) into a single FormbitValues type used everywhere. - Remove the field `isDirty` from PrivateValidateForm options: it was destructured and discarded, i.e. dead. - Simplify SubmitSuccessCallback's writer type to FormState>, matching what submitForm actually passes at runtime. - Reorganise the file into clear sections and tighten the doc comments. - Re-export FormbitObject, the method/callback/options types and the yup re-exports from the package root. BREAKING CHANGE: the deprecated type aliases are removed (Object, Writer, SuccessCheckCallback, ErrorCheckCallback, ErrorFn, IsFormValid, IsFormInvalid, ClearIsDirty, ResetForm, LiveValidationFn, IsDirty), together with the internal base types FormbitRecord, Form and InitialValues. Use FormbitValues instead. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 462 +++++---------------- src/__tests__/initialize.test.ts | 6 +- src/__tests__/use-formbit-context.test.tsx | 4 +- src/formbit-context.tsx | 10 +- src/index.ts | 44 +- src/types/index.ts | 422 ++++++------------- src/use-execute-callbacks.ts | 4 +- src/use-formbit.ts | 8 +- src/validate-sync-all.ts | 6 +- 9 files changed, 294 insertions(+), 672 deletions(-) diff --git a/README.md b/README.md index 82aeb50..8e58690 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) @@ -345,90 +344,73 @@ For local development we suggest using [Yalc](https://github.com/wclr/yalc) to t Ƭ **FormbitObject**\<`Values`\>: `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) | +| `Values` | 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`\<`Values`\>\> | 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`\<`Values`\> | The current form values. Partial: fields may be missing until validated. | +| `initialize` | [`Initialize`](#initialize)\<`Values`\> | 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)\<`Values`\> | Removes the value at `path`, sets `isDirty`, then validates `pathsToValidate` plus every live-validated field. | +| `removeAll` | [`RemoveAll`](#removeall)\<`Values`\> | 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)\<`Values`\> | Replaces the current validation schema. | +| `submitForm` | [`SubmitForm`](#submitform)\<`Values`\> | Validates the whole form and, if valid, runs the success callback to submit. | +| `validate` | [`Validate`](#validate)\<`Values`\> | Validates only `path` (ignores live-validated fields). | +| `validateAll` | [`ValidateAll`](#validateall)\<`Values`\> | Validates only the given `paths` (ignores live-validated fields). | +| `validateForm` | [`ValidateForm`](#validateform)\<`Partial`\<`Values`\>\> | Validates the whole form and registers any error. | +| `write` | [`Write`](#write)\<`Values`\> | Writes `value` at `path`, sets `isDirty`, then validates `pathsToValidate` plus every live-validated field. | +| `writeAll` | [`WriteAll`](#writeall)\<`Values`\> | 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/ea8fbb1/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/ea8fbb1/src/types/index.ts#L23) #### FormState Ƭ **FormState**\<`Values`\>: `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) | +| `Values` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -442,68 +424,50 @@ Internal form state storing all the data of the form (except the validation sche #### 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/ea8fbb1/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 `Values` 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/ea8fbb1/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/ea8fbb1/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**\<`Values`\>: (`json`: [`FormbitValues`](#formbitvalues), `inner`: [`ValidationError`](#validationerror)[], `writer`: [`FormState`](#formstate)\<`Values`\>, `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) | +| `Values` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -513,7 +477,7 @@ 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`\> | | `setError` | [`SetError`](#seterror) | @@ -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/ea8fbb1/src/types/index.ts#L72) #### CheckSuccessCallback -Ƭ **CheckSuccessCallback**\<`Values`\>: (`json`: [`Form`](#form), `writer`: [`FormState`](#formstate)\<`Values`\>, `setError`: [`SetError`](#seterror)) => `void` +Ƭ **CheckSuccessCallback**\<`Values`\>: (`json`: [`FormbitValues`](#formbitvalues), `writer`: [`FormState`](#formstate)\<`Values`\>, `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) | +| `Values` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -545,7 +509,7 @@ Success callback invoked by the check method when the operation is successful. | Name | Type | | :------ | :------ | -| `json` | [`Form`](#form) | +| `json` | [`FormbitValues`](#formbitvalues) | | `writer` | [`FormState`](#formstate)\<`Values`\> | | `setError` | [`SetError`](#seterror) | @@ -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/ea8fbb1/src/types/index.ts#L68) #### ErrorCallback Ƭ **ErrorCallback**\<`Values`\>: (`writer`: [`FormState`](#formstate)\<`Values`\>, `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) | +| `Values` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -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/ea8fbb1/src/types/index.ts#L64) #### SubmitSuccessCallback -Ƭ **SubmitSuccessCallback**\<`Values`\>: (`writer`: [`FormState`](#formstate)\<`Values` \| `Omit`\<`Values`, ``"__metadata"``\>\>, `setError`: [`SetError`](#seterror), `clearIsDirty`: () => `void`) => `void` +Ƭ **SubmitSuccessCallback**\<`Values`\>: (`writer`: [`FormState`](#formstate)\<`Omit`\<`Values`, ``"__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) | +| `Values` | 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`\<`Values`, ``"__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/ea8fbb1/src/types/index.ts#L79) #### SuccessCallback Ƭ **SuccessCallback**\<`Values`\>: (`writer`: [`FormState`](#formstate)\<`Values`\>, `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) | +| `Values` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -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/ea8fbb1/src/types/index.ts#L60) ### Method Types #### Check -Ƭ **Check**\<`Values`\>: (`json`: [`Form`](#form), `options?`: [`CheckFnOptions`](#checkfnoptions)\<`Values`\>) => [`ValidationError`](#validationerror)[] \| `undefined` +Ƭ **Check**\<`Values`\>: (`json`: [`FormbitValues`](#formbitvalues), `options?`: [`CheckFnOptions`](#checkfnoptions)\<`Values`\>) => [`ValidationError`](#validationerror)[] \| `undefined` See [FormbitObject.check](#check). @@ -660,7 +624,7 @@ See [FormbitObject.check](#check). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `Values` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -670,7 +634,7 @@ See [FormbitObject.check](#check). | Name | Type | | :------ | :------ | -| `json` | [`Form`](#form) | +| `json` | [`FormbitValues`](#formbitvalues) | | `options?` | [`CheckFnOptions`](#checkfnoptions)\<`Values`\> | ##### Returns @@ -679,7 +643,7 @@ 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/ea8fbb1/src/types/index.ts#L89) #### Initialize Ƭ **Initialize**\<`Values`\>: (`values`: `Partial`\<`Values`\>) => `void` @@ -690,7 +654,7 @@ See [FormbitObject.initialize](#initialize). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `Values` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -708,7 +672,7 @@ 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/ea8fbb1/src/types/index.ts#L93) #### Remove Ƭ **Remove**\<`Values`\>: (`path`: `string`, `options?`: [`WriteFnOptions`](#writefnoptions)\<`Values`\>) => `void` @@ -719,7 +683,7 @@ See [FormbitObject.remove](#remove). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `Values` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -738,7 +702,7 @@ 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/ea8fbb1/src/types/index.ts#L96) #### RemoveAll Ƭ **RemoveAll**\<`Values`\>: (`arr`: `string`[], `options?`: [`WriteFnOptions`](#writefnoptions)\<`Values`\>) => `void` @@ -749,7 +713,7 @@ See [FormbitObject.removeAll](#removeall). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `Values` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -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/ea8fbb1/src/types/index.ts#L116) #### SetError Ƭ **SetError**: (`path`: `string`, `value`: `string`) => `void` @@ -792,7 +756,7 @@ 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/ea8fbb1/src/types/index.ts#L99) #### SetSchema Ƭ **SetSchema**\<`Values`\>: (`newSchema`: [`ValidationSchema`](#validationschema)\<`Values`\>) => `void` @@ -803,7 +767,7 @@ See [FormbitObject.setSchema](#setschema). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `Values` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -821,7 +785,7 @@ 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/ea8fbb1/src/types/index.ts#L102) #### SubmitForm Ƭ **SubmitForm**\<`Values`\>: (`successCallback`: [`SubmitSuccessCallback`](#submitsuccesscallback)\<`Values`\>, `errorCallback?`: [`ErrorCallback`](#errorcallback)\<`Partial`\<`Values`\>\>, `options?`: [`ValidateOptions`](#validateoptions)) => `void` @@ -832,7 +796,7 @@ See [FormbitObject.submitForm](#submitform). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `Values` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -852,7 +816,7 @@ 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/ea8fbb1/src/types/index.ts#L132) #### Validate Ƭ **Validate**\<`Values`\>: (`path`: `string`, `options?`: [`ValidateFnOptions`](#validatefnoptions)\<`Values`\>) => `void` @@ -863,7 +827,7 @@ See [FormbitObject.validate](#validate). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `Values` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -882,7 +846,7 @@ 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/ea8fbb1/src/types/index.ts#L120) #### ValidateAll Ƭ **ValidateAll**\<`Values`\>: (`paths`: `string`[], `options?`: [`ValidateFnOptions`](#validatefnoptions)\<`Values`\>) => `void` @@ -893,7 +857,7 @@ See [FormbitObject.validateAll](#validateall). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `Values` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -912,7 +876,7 @@ 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/ea8fbb1/src/types/index.ts#L123) #### ValidateForm Ƭ **ValidateForm**\<`Values`\>: (`successCallback?`: [`SuccessCallback`](#successcallback)\<`Values`\>, `errorCallback?`: [`ErrorCallback`](#errorcallback)\<`Values`\>, `options?`: [`ValidateOptions`](#validateoptions)) => `void` @@ -923,7 +887,7 @@ See [FormbitObject.validateForm](#validateform). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `Values` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -943,7 +907,7 @@ 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/ea8fbb1/src/types/index.ts#L126) #### Write Ƭ **Write**\<`Values`\>: (`path`: keyof `Values` \| `string`, `value`: `unknown`, `options?`: [`WriteFnOptions`](#writefnoptions)\<`Values`\>) => `void` @@ -954,7 +918,7 @@ See [FormbitObject.write](#write). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `Values` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -974,7 +938,7 @@ 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/ea8fbb1/src/types/index.ts#L108) #### WriteAll Ƭ **WriteAll**\<`Values`\>: (`arr`: [`WriteAllValue`](#writeallvalue)\<`Values`\>[], `options?`: [`WriteFnOptions`](#writefnoptions)\<`Values`\>) => `void` @@ -985,7 +949,7 @@ See [FormbitObject.writeAll](#writeall). | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `Values` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -1004,20 +968,20 @@ 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/ea8fbb1/src/types/index.ts#L112) ### Options Types #### CheckFnOptions Ƭ **CheckFnOptions**\<`Values`\>: `Object` -Options object to change the behavior of the check method. +Options accepted by `check()`. #### Type parameters | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `Values` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -1029,18 +993,18 @@ Options object to change the behavior of the check method. #### 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/ea8fbb1/src/types/index.ts#L140) #### ValidateFnOptions Ƭ **ValidateFnOptions**\<`Values`\>: `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) | +| `Values` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -1052,286 +1016,72 @@ Options object to change the behavior of the validate methods. #### 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/ea8fbb1/src/types/index.ts#L147) #### WriteAllValue Ƭ **WriteAllValue**\<`Values`\>: [keyof `Values` \| `string`, `unknown`] -Tuple of [key, value] pair. +A single `[path, value]` pair accepted by `writeAll`. #### Type parameters | Name | Type | | :------ | :------ | -| `Values` | extends [`InitialValues`](#initialvalues) | +| `Values` | 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/ea8fbb1/src/types/index.ts#L105) #### WriteFnOptions Ƭ **WriteFnOptions**\<`Values`\>: \{ `noLiveValidation?`: `boolean` ; `pathsToValidate?`: `string`[] } & [`ValidateFnOptions`](#validatefnoptions)\<`Values`\> -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) | +| `Values` | 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/ea8fbb1/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/ea8fbb1/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/ea8fbb1/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`** - -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) | +| `Values` | 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/ea8fbb1/src/types/index.ts#L49) ## License 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__/use-formbit-context.test.tsx b/src/__tests__/use-formbit-context.test.tsx index b4035d8..be3579d 100644 --- a/src/__tests__/use-formbit-context.test.tsx +++ b/src/__tests__/use-formbit-context.test.tsx @@ -1,11 +1,11 @@ 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} 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/index.ts b/src/types/index.ts index 0151805..83a8218 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 `Values` 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 = { +export type FormState = { form: Values, - initialValues: Values + initialValues: Values, errors: Errors, liveValidation: LiveValidation, isDirty: boolean, } -/** @deprecated Use {@link FormState} instead. */ -export type Writer = FormState +// ─── Yup re-exports ──────────────────────────────────────────────────────────── -// ─── Yup Re-exports ───────────────────────────────────────────────────────── - -/** - * 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 +/** 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 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 by validation methods when the form (or the validated paths) are valid. */ +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 validation fails. */ +export type ErrorCallback = + (writer: FormState, setError: SetError) => void -/** @deprecated Use {@link CheckSuccessCallback} instead. */ -export type SuccessCheckCallback = CheckSuccessCallback +/** Invoked by `check()` when the given json is valid. */ +export type CheckSuccessCallback = + (json: FormbitValues, 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 - -/** @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 - -/** See {@link FormbitObject.validate}. */ -export type Validate = (path: string, options?: ValidateFnOptions) => void - -/** See {@link FormbitObject.validateAll}. */ -export type ValidateAll = (paths: string[], options?: ValidateFnOptions) => void - -/** See {@link FormbitObject.validateForm}. */ -export type ValidateForm = ( - successCallback?: SuccessCallback, - errorCallback?: ErrorCallback, - options?: ValidateOptions) => void +/** A single `[path, value]` pair accepted by `writeAll`. */ +export type WriteAllValue = [keyof Values | string, unknown] /** See {@link FormbitObject.write}. */ -export type Write = +export type Write = (path: keyof Values | string, value: unknown, options?: WriteFnOptions) => void /** See {@link FormbitObject.writeAll}. */ -export type WriteAll = +export type WriteAll = (arr: WriteAllValue[], options?: WriteFnOptions) => void /** See {@link FormbitObject.removeAll}. */ -export type RemoveAll = +export type RemoveAll = (arr: string[], options?: WriteFnOptions) => void -/** - * Tuple of [key, value] pair. - */ -export type WriteAllValue = [keyof Values | string, unknown] +/** See {@link FormbitObject.validate}. */ +export type Validate = (path: string, options?: ValidateFnOptions) => void -// ─── Options Types ─────────────────────────────────────────────────────────── +/** See {@link FormbitObject.validateAll}. */ +export type ValidateAll = (paths: string[], options?: ValidateFnOptions) => void -/** - * Options object to change the behavior of the check method. - */ -export type CheckFnOptions = { +/** See {@link FormbitObject.validateForm}. */ +export type ValidateForm = ( + successCallback?: SuccessCallback, + errorCallback?: ErrorCallback, + options?: ValidateOptions) => void + +/** See {@link FormbitObject.submitForm}. */ +export type SubmitForm = ( + successCallback: SubmitSuccessCallback, + errorCallback?: ErrorCallback>, + options?: ValidateOptions) => void + +// ─── Options ───────────────────────────────────────────────────────────────── + +/** 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 = { +/** 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 +// ─── 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 Values | 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. - */ + /** 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. - */ + /** 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, /** - * 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, /** - * 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, /** - * 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, - /** - * Initialize the form with new initial values. - */ + /** 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. - */ + /** 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. - */ + /** 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. - */ + /** Validates only the given `paths` (ignores live-validated fields). */ validateAll: ValidateAll, - /** - * This method validates the entire form and sets the corresponding errors if any. - */ + /** 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. - */ + /** 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..ff11b99 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,7 +10,7 @@ import { isEmpty } from 'lodash' * * */ -export default (writer: FormState, setError: SetError) => { +export default (writer: FormState, setError: SetError) => { const callbacksStore = useRef> | undefined>>({}) useEffect(() => { diff --git a/src/use-formbit.ts b/src/use-formbit.ts index 902bcef..7226840 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,12 +29,12 @@ import useExecuteCallbacks from './use-execute-callbacks' import { cloneDeep, get, isEmpty, omit, set } from 'lodash' import { v4 as uuidv4 } from 'uuid' -type UseFormbitParams = { +type UseFormbitParams = { initialValues?: Partial, yup: ValidationSchema } -export default ({ +export default ({ initialValues = {}, yup: schema }: UseFormbitParams): FormbitObject => { @@ -438,7 +438,7 @@ export default ({ const privateValidateForm: PrivateValidateForm> = useCallback(( successCallback, errorCallback, - { isDirty: _, options } = {} + { options } = {} ) => { const newUUID = (function getUUID() { if (successCallback || errorCallback) { diff --git a/src/validate-sync-all.ts b/src/validate-sync-all.ts index 30e88f1..c4df308 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, + form: FormbitValues, options: ValidateOptions = {} ): ValidationFormbitError[] => { let errors: ValidationFormbitError[] = [] From ac0cc1ad71a5cd678006132d032c68b0fe127b83 Mon Sep 17 00:00:00 2001 From: Luca Tagliabue Date: Wed, 29 Jul 2026 16:05:45 +0200 Subject: [PATCH 05/10] refactor(example): rename the schema type FormData to FormValues `FormData` shadows the browser's global FormData type, which is confusing in an example meant to teach. Rename it to FormValues across every example and switch the imports to type-only (`import type` / `import { type ... }`) for consistency. Co-Authored-By: Claude Opus 4.8 (1M context) --- example/src/forms/a-basic-form-hook/index.tsx | 4 ++-- example/src/forms/a-basic-form-hook/schema.ts | 2 +- example/src/forms/b-basic-form-context/index.tsx | 10 +++++----- example/src/forms/b-basic-form-context/schema.ts | 2 +- .../b-basic-form-context/use-handle-on-submit.tsx | 4 ++-- example/src/forms/c-addable-fields/index.tsx | 14 +++++++------- example/src/forms/c-addable-fields/schema.ts | 2 +- .../c-addable-fields/use-handle-on-submit.tsx | 4 ++-- example/src/forms/d-edit-like/index.tsx | 8 ++++---- example/src/forms/d-edit-like/schema.ts | 2 +- .../src/forms/d-edit-like/use-handle-on-submit.tsx | 4 ++-- .../src/forms/d-edit-like/use-initialize-form.ts | 4 ++-- example/src/forms/e-multiple-steps/schema.ts | 2 +- example/src/forms/e-multiple-steps/step-one.tsx | 6 +++--- example/src/forms/e-multiple-steps/step-three.tsx | 6 +++--- example/src/forms/e-multiple-steps/step-two.tsx | 6 +++--- example/src/forms/f-remove-all/index.tsx | 10 +++++----- example/src/forms/f-remove-all/schema.ts | 2 +- .../forms/f-remove-all/use-handle-on-submit.tsx | 4 ++-- .../src/forms/f-remove-all/use-initialize-form.ts | 4 ++-- 20 files changed, 50 insertions(+), 50 deletions(-) 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..a746768 100644 --- a/example/src/forms/b-basic-form-context/index.tsx +++ b/example/src/forms/b-basic-form-context/index.tsx @@ -8,7 +8,7 @@ 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() @@ -98,7 +98,7 @@ function Age() { } 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..13be150 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 ( @@ -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,7 +133,7 @@ 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 @@ -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..8a86c4d 100644 --- a/example/src/forms/d-edit-like/index.tsx +++ b/example/src/forms/d-edit-like/index.tsx @@ -10,7 +10,7 @@ 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' @@ -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..8bc67e1 100644 --- a/example/src/forms/e-multiple-steps/step-one.tsx +++ b/example/src/forms/e-multiple-steps/step-one.tsx @@ -3,7 +3,7 @@ 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() { @@ -21,7 +21,7 @@ export function StepOne() { } function Name() { - const { form, error, write } = useFormbitContext() + const { form, error, write } = useFormbitContext() const [handleOnNext] = useHandleNextStep(['name', 'surname']) @@ -44,7 +44,7 @@ function Name() { } function Surname() { - const { form, error, write } = useFormbitContext() + const { form, error, write } = useFormbitContext() const [handleOnNext] = useHandleNextStep(['name', 'surname']) diff --git a/example/src/forms/e-multiple-steps/step-three.tsx b/example/src/forms/e-multiple-steps/step-three.tsx index ac256ce..a2b54fd 100644 --- a/example/src/forms/e-multiple-steps/step-three.tsx +++ b/example/src/forms/e-multiple-steps/step-three.tsx @@ -4,7 +4,7 @@ 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 ( @@ -18,7 +18,7 @@ export function StepThree() { } function Email() { - const { form, error, write } = useFormbitContext() + const { form, error, write } = useFormbitContext() const { handleOnSubmit } = useHandleOnSubmit() @@ -41,7 +41,7 @@ function Email() { } function Actions() { - const { form: { __metadata } } = useFormbitContext() + const { form: { __metadata } } = useFormbitContext() const handleReset = __metadata?.resetSteps diff --git a/example/src/forms/e-multiple-steps/step-two.tsx b/example/src/forms/e-multiple-steps/step-two.tsx index 9b40589..3c11466 100644 --- a/example/src/forms/e-multiple-steps/step-two.tsx +++ b/example/src/forms/e-multiple-steps/step-two.tsx @@ -1,7 +1,7 @@ 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() { @@ -17,7 +17,7 @@ export function StepTwo() { } function Age() { - const { form, error, write } = useFormbitContext() + const { form, error, write } = useFormbitContext() const [handleOnNext] = useHandleNextStep(['age']) @@ -40,7 +40,7 @@ function Age() { } function Actions() { - const { form: { __metadata } } = useFormbitContext() + const { form: { __metadata } } = useFormbitContext() const [handleOnNext, isStepInvalid] = useHandleNextStep(['age']) diff --git a/example/src/forms/f-remove-all/index.tsx b/example/src/forms/f-remove-all/index.tsx index 8f8c31a..609f156 100644 --- a/example/src/forms/f-remove-all/index.tsx +++ b/example/src/forms/f-remove-all/index.tsx @@ -8,7 +8,7 @@ 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' @@ -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() @@ -164,7 +164,7 @@ function Age() { } 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..725f05a 100644 --- a/example/src/forms/f-remove-all/use-initialize-form.ts +++ b/example/src/forms/f-remove-all/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 From b7dae6b491bda615284e29b6a3b05a0fb8df91d4 Mon Sep 17 00:00:00 2001 From: Luca Tagliabue Date: Wed, 29 Jul 2026 16:08:56 +0200 Subject: [PATCH 06/10] fix(example): fix broken addable-fields form and align the examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The addable-fields example was writing and validating the wrong paths, so editing an existing friend never worked: - write targeted `friends[i].key` for both name and surname (→ `.name` / `.surname`) - validate/error targeted the non-existent `headers[i]` path (→ `friends[i]`) - the mapped had no React key, and `friends?.[i].name` could throw Also fix f-remove-all, which mixed up `age` and `email`: the loading skeleton showed an "Email" field with an "Age" placeholder, and it initialized an `email` value the schema doesn't have — both now consistently use `age`. Alignment across the examples: - pass `initialValues={{}}` explicitly on every Provider - use the single `FormValues` type in e-multiple-steps instead of two divergent local `Context` types - fix copy/pasted Cypress describe/it names - document why the fake GET randomly fails (and that it makes edit-like flaky) Co-Authored-By: Claude Opus 4.8 (1M context) --- example/src/__tests__/basic-form-context.cy.tsx | 2 +- example/src/__tests__/basic-form-hook.cy.tsx | 2 +- example/src/forms/b-basic-form-context/index.tsx | 2 +- example/src/forms/c-addable-fields/index.tsx | 16 ++++++++-------- .../e-multiple-steps/use-handle-next-step.ts | 11 +++-------- .../use-handle-on-submit-types.ts | 7 ------- .../e-multiple-steps/use-handle-on-submit.tsx | 5 +++-- example/src/forms/f-remove-all/index.tsx | 4 ++-- .../forms/f-remove-all/use-initialize-form.ts | 4 +++- .../forms/fake-api-context/use-get-fake-user.ts | 3 +++ 10 files changed, 25 insertions(+), 31 deletions(-) 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/b-basic-form-context/index.tsx b/example/src/forms/b-basic-form-context/index.tsx index a746768..ae2d587 100644 --- a/example/src/forms/b-basic-form-context/index.tsx +++ b/example/src/forms/b-basic-form-context/index.tsx @@ -12,7 +12,7 @@ import { type FormValues, schema } from './schema' export function BasicFormContext() { return ( - + ) diff --git a/example/src/forms/c-addable-fields/index.tsx b/example/src/forms/c-addable-fields/index.tsx index 13be150..7bc4de5 100644 --- a/example/src/forms/c-addable-fields/index.tsx +++ b/example/src/forms/c-addable-fields/index.tsx @@ -34,7 +34,7 @@ function BasicFormInner() {
- {friends.map((_, i) => )} + {friends.map((_, i) => )}
@@ -135,20 +135,20 @@ function FriendInput() { function Friend({ index }: { index: number }) { 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 ( 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 609f156..3201128 100644 --- a/example/src/forms/f-remove-all/index.tsx +++ b/example/src/forms/f-remove-all/index.tsx @@ -15,7 +15,7 @@ import { useFakeApiContext } from '../fake-api-context' export function WriteRemoveAllForm() { return ( - + ) @@ -53,7 +53,7 @@ function IsLoading() { - + 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 725f05a..817926f 100644 --- a/example/src/forms/f-remove-all/use-initialize-form.ts +++ b/example/src/forms/f-remove-all/use-initialize-form.ts @@ -11,7 +11,9 @@ export const useInitializeForm = () => { 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) From 520ca794b4816428dfdca46f9a45dab874ca51e1 Mon Sep 17 00:00:00 2001 From: Luca Tagliabue Date: Wed, 29 Jul 2026 16:21:52 +0200 Subject: [PATCH 07/10] style(example): fix JSX indentation in multi-step forms The three multi-step files were indented with 4 spaces per level (and an extra offset) instead of the project's 2-space style; d-edit-like's provider had a stray offset too. ESLint doesn't enforce JSX indentation here and there is no Prettier, so this went unnoticed. Reindent to 2 spaces and drop the single-child fragments in StepOne/StepTwo. Co-Authored-By: Claude Opus 4.8 (1M context) --- example/src/forms/d-edit-like/index.tsx | 6 +- .../src/forms/e-multiple-steps/step-one.tsx | 68 +++++++++---------- .../src/forms/e-multiple-steps/step-three.tsx | 49 ++++++------- .../src/forms/e-multiple-steps/step-two.tsx | 64 +++++++++-------- 4 files changed, 94 insertions(+), 93 deletions(-) diff --git a/example/src/forms/d-edit-like/index.tsx b/example/src/forms/d-edit-like/index.tsx index 8a86c4d..1b742dc 100644 --- a/example/src/forms/d-edit-like/index.tsx +++ b/example/src/forms/d-edit-like/index.tsx @@ -16,9 +16,9 @@ import { useInitializeForm } from './use-initialize-form' export function EditLikeForm() { return ( - - - + + + ) } diff --git a/example/src/forms/e-multiple-steps/step-one.tsx b/example/src/forms/e-multiple-steps/step-one.tsx index 8bc67e1..94ef05a 100644 --- a/example/src/forms/e-multiple-steps/step-one.tsx +++ b/example/src/forms/e-multiple-steps/step-one.tsx @@ -7,17 +7,17 @@ import type { FormValues } from './schema' import { useHandleNextStep } from './use-handle-next-step' export function StepOne() { - return <> -
- + return ( +
+ - + - + - -
- + +
+ ) } function Name() { @@ -30,16 +30,16 @@ function Name() { const ref = useAutoFocus() return ( - - - + + + ) } @@ -51,15 +51,15 @@ function 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 a2b54fd..18454bd 100644 --- a/example/src/forms/e-multiple-steps/step-three.tsx +++ b/example/src/forms/e-multiple-steps/step-three.tsx @@ -8,13 +8,14 @@ import type { FormValues } from './schema' export function StepThree() { return ( -
- +
+ - + - -
) + +
+ ) } function Email() { @@ -27,16 +28,16 @@ function Email() { const ref = useAutoFocus() return ( - - - + + + ) } @@ -47,13 +48,15 @@ function Actions() { 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 3c11466..9de8ff3 100644 --- a/example/src/forms/e-multiple-steps/step-two.tsx +++ b/example/src/forms/e-multiple-steps/step-two.tsx @@ -5,15 +5,15 @@ import type { FormValues } from './schema' import { useHandleNextStep } from './use-handle-next-step' export function StepTwo() { - return <> -
- + return ( +
+ - + - -
- + +
+ ) } function Age() { @@ -26,17 +26,18 @@ function Age() { const ref = useAutoFocus() return ( - - - ) + + + + ) } function Actions() { @@ -47,21 +48,18 @@ function Actions() { const prevStep = __metadata?.prevStep return ( - <> - - - - > - Prev - - + + ) } From 2eadccaf99578c0582a1c2fbbec3327719a0faea Mon Sep 17 00:00:00 2001 From: Luca Tagliabue Date: Wed, 29 Jul 2026 16:25:39 +0200 Subject: [PATCH 08/10] chore(example): bump radicalbit-design-system to 2.19.5 package.json and yarn.lock declared 2.13.1 but node_modules actually had a newer version installed, so the examples rendered with styles that didn't match the pinned version. Align all three on 2.19.5, which is the version that carries the corrected button :focus styling (box-shadow instead of the old primary color/border on focus). Co-Authored-By: Claude Opus 4.8 (1M context) --- example/package.json | 2 +- example/yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) 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/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== From 2ca1a8ac4ceba36459778b756de2bf75ccb77cb8 Mon Sep 17 00:00:00 2001 From: Luca Tagliabue Date: Wed, 29 Jul 2026 17:01:05 +0200 Subject: [PATCH 09/10] chore: enforce JSX indentation via eslint Add react/jsx-indent and react/jsx-indent-props (2 spaces) to both the root and example eslint configs, so the JSX indentation issues we hit can no longer slip through. Reindent the few files that weren't conforming. --- .eslintrc | 2 ++ example/.eslintrc.cjs | 2 ++ example/src/forms/b-basic-form-context/index.tsx | 3 ++- example/src/forms/f-remove-all/index.tsx | 3 ++- src/__tests__/use-formbit-context.test.tsx | 2 +- 5 files changed, 9 insertions(+), 3 deletions(-) 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/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/src/forms/b-basic-form-context/index.tsx b/example/src/forms/b-basic-form-context/index.tsx index ae2d587..3df1bc0 100644 --- a/example/src/forms/b-basic-form-context/index.tsx +++ b/example/src/forms/b-basic-form-context/index.tsx @@ -94,7 +94,8 @@ function Age() { value={form.age} required /> -
) + + ) } function Actions() { diff --git a/example/src/forms/f-remove-all/index.tsx b/example/src/forms/f-remove-all/index.tsx index 3201128..3655942 100644 --- a/example/src/forms/f-remove-all/index.tsx +++ b/example/src/forms/f-remove-all/index.tsx @@ -160,7 +160,8 @@ function Age() { value={form.age} required /> - ) + + ) } function Actions() { diff --git a/src/__tests__/use-formbit-context.test.tsx b/src/__tests__/use-formbit-context.test.tsx index be3579d..cdf6803 100644 --- a/src/__tests__/use-formbit-context.test.tsx +++ b/src/__tests__/use-formbit-context.test.tsx @@ -8,7 +8,7 @@ import { TEST_ERROR_MESSAGES } from 'src/helpers/constants' const renderWithContext = (initialValues: FormbitValues, schema: ValidationSchema<{}>) => { const wrapper = ({ children }: PropsWithChildren) => - {children} + {children} return renderHook(() => useFormbitContext(), { wrapper }) } From 1cfcb58fdee2c91822f4661c6babc65dbc698bf6 Mon Sep 17 00:00:00 2001 From: Luca Tagliabue Date: Wed, 29 Jul 2026 17:19:05 +0200 Subject: [PATCH 10/10] refactor(types): rename generic type parameter Values to T MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rinomina il type parameter generico da Values a T in tutte le firme dei tipi e nell'implementazione. Convenzione più concisa in stile Java; FormbitValues resta invariato. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 228 +++++++++++++++++------------------ src/types/index.ts | 124 +++++++++---------- src/use-execute-callbacks.ts | 6 +- src/use-formbit.ts | 52 ++++---- src/validate-sync-all.ts | 4 +- 5 files changed, 207 insertions(+), 207 deletions(-) diff --git a/README.md b/README.md index 8e58690..d1028a4 100644 --- a/README.md +++ b/README.md @@ -342,7 +342,7 @@ For local development we suggest using [Yalc](https://github.com/wclr/yalc) to t ### FormbitObject -Ƭ **FormbitObject**\<`Values`\>: `Object` +Ƭ **FormbitObject**\<`T`\>: `Object` The object returned by `useFormbit()` and `useFormbitContext()`. Holds the form state and every method needed to read, mutate and validate the form. @@ -351,36 +351,36 @@ state and every method needed to read, mutate and validate the form. | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration | Name | Type | Description | | :------ | :------ | :------ | -| `check` | [`Check`](#check)\<`Partial`\<`Values`\>\> | Validates `json` against the current schema; returns the errors, or undefined if 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) | 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`\<`Values`\> | The current form values. Partial: fields may be missing until validated. | -| `initialize` | [`Initialize`](#initialize)\<`Values`\> | Re-initializes the form with new initial values. | +| `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`\> | Removes the value at `path`, sets `isDirty`, then validates `pathsToValidate` plus every live-validated field. | -| `removeAll` | [`RemoveAll`](#removeall)\<`Values`\> | Removes every given path, sets `isDirty`, then validates `pathsToValidate` plus every live-validated field. | +| `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) | Sets the error message at `path`. | -| `setSchema` | [`SetSchema`](#setschema)\<`Values`\> | Replaces the current validation schema. | -| `submitForm` | [`SubmitForm`](#submitform)\<`Values`\> | Validates the whole form and, if valid, runs the success callback to submit. | -| `validate` | [`Validate`](#validate)\<`Values`\> | Validates only `path` (ignores live-validated fields). | -| `validateAll` | [`ValidateAll`](#validateall)\<`Values`\> | Validates only the given `paths` (ignores live-validated fields). | -| `validateForm` | [`ValidateForm`](#validateform)\<`Partial`\<`Values`\>\> | Validates the whole form and registers any error. | -| `write` | [`Write`](#write)\<`Values`\> | Writes `value` at `path`, sets `isDirty`, then validates `pathsToValidate` plus every live-validated field. | -| `writeAll` | [`WriteAll`](#writeall)\<`Values`\> | Writes every `[path, value]` pair, sets `isDirty`, then validates `pathsToValidate` plus every live-validated field. | +| `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:186](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L186) +[index.ts:186](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L186) ### Core Types #### Errors @@ -399,10 +399,10 @@ errors: { age: "Age must be greater than 18" } #### Defined in -[index.ts:23](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L23) +[index.ts:23](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L23) #### FormState -Ƭ **FormState**\<`Values`\>: `Object` +Ƭ **FormState**\<`T`\>: `Object` The whole internal state of the form (everything except the validation schema). @@ -410,21 +410,21 @@ The whole internal state of the form (everything except the validation schema). | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `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:38](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L38) +[index.ts:38](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L38) #### FormbitValues Ƭ **FormbitValues**: `Record`\<`string`, `unknown`\> & \{ `__metadata?`: `Record`\<`string`, `unknown`\> } @@ -433,11 +433,11 @@ 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 `Values` you pass to `useFormbit()` must extend this type. +The generic `T` you pass to `useFormbit()` must extend this type. #### Defined in -[index.ts:13](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L13) +[index.ts:13](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L13) #### LiveValidation Ƭ **LiveValidation**: `Record`\<`string`, ``true``\> @@ -454,12 +454,12 @@ liveValidation: { age: true } #### Defined in -[index.ts:33](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L33) +[index.ts:33](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L33) ### Callback Types #### CheckErrorCallback -Ƭ **CheckErrorCallback**\<`Values`\>: (`json`: [`FormbitValues`](#formbitvalues), `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 by `check()` when the given json is invalid. @@ -467,7 +467,7 @@ Invoked by `check()` when the given json is invalid. | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -479,7 +479,7 @@ Invoked by `check()` when the given json is invalid. | :------ | :------ | | `json` | [`FormbitValues`](#formbitvalues) | | `inner` | [`ValidationError`](#validationerror)[] | -| `writer` | [`FormState`](#formstate)\<`Values`\> | +| `writer` | [`FormState`](#formstate)\<`T`\> | | `setError` | [`SetError`](#seterror) | ##### Returns @@ -488,10 +488,10 @@ Invoked by `check()` when the given json is invalid. #### Defined in -[index.ts:72](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L72) +[index.ts:72](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L72) #### CheckSuccessCallback -Ƭ **CheckSuccessCallback**\<`Values`\>: (`json`: [`FormbitValues`](#formbitvalues), `writer`: [`FormState`](#formstate)\<`Values`\>, `setError`: [`SetError`](#seterror)) => `void` +Ƭ **CheckSuccessCallback**\<`T`\>: (`json`: [`FormbitValues`](#formbitvalues), `writer`: [`FormState`](#formstate)\<`T`\>, `setError`: [`SetError`](#seterror)) => `void` Invoked by `check()` when the given json is valid. @@ -499,7 +499,7 @@ Invoked by `check()` when the given json is valid. | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -510,7 +510,7 @@ Invoked by `check()` when the given json is valid. | Name | Type | | :------ | :------ | | `json` | [`FormbitValues`](#formbitvalues) | -| `writer` | [`FormState`](#formstate)\<`Values`\> | +| `writer` | [`FormState`](#formstate)\<`T`\> | | `setError` | [`SetError`](#seterror) | ##### Returns @@ -519,10 +519,10 @@ Invoked by `check()` when the given json is valid. #### Defined in -[index.ts:68](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L68) +[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 by validation methods when validation fails. @@ -530,7 +530,7 @@ Invoked by validation methods when validation fails. | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -540,7 +540,7 @@ Invoked by validation methods when validation fails. | Name | Type | | :------ | :------ | -| `writer` | [`FormState`](#formstate)\<`Values`\> | +| `writer` | [`FormState`](#formstate)\<`T`\> | | `setError` | [`SetError`](#seterror) | ##### Returns @@ -549,10 +549,10 @@ Invoked by validation methods when validation fails. #### Defined in -[index.ts:64](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L64) +[index.ts:64](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L64) #### SubmitSuccessCallback -Ƭ **SubmitSuccessCallback**\<`Values`\>: (`writer`: [`FormState`](#formstate)\<`Omit`\<`Values`, ``"__metadata"``\>\>, `setError`: [`SetError`](#seterror), `clearIsDirty`: () => `void`) => `void` +Ƭ **SubmitSuccessCallback**\<`T`\>: (`writer`: [`FormState`](#formstate)\<`Omit`\<`T`, ``"__metadata"``\>\>, `setError`: [`SetError`](#seterror), `clearIsDirty`: () => `void`) => `void` 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. @@ -561,7 +561,7 @@ to the backend. `__metadata` is stripped from `writer.form` before this runs. | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -571,7 +571,7 @@ to the backend. `__metadata` is stripped from `writer.form` before this runs. | Name | Type | | :------ | :------ | -| `writer` | [`FormState`](#formstate)\<`Omit`\<`Values`, ``"__metadata"``\>\> | +| `writer` | [`FormState`](#formstate)\<`Omit`\<`T`, ``"__metadata"``\>\> | | `setError` | [`SetError`](#seterror) | | `clearIsDirty` | () => `void` | @@ -581,10 +581,10 @@ to the backend. `__metadata` is stripped from `writer.form` before this runs. #### Defined in -[index.ts:79](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L79) +[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` Invoked by validation methods when the form (or the validated paths) are valid. @@ -592,7 +592,7 @@ Invoked by validation methods when the form (or the validated paths) are valid. | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -602,7 +602,7 @@ Invoked by validation methods when the form (or the validated paths) are valid. | Name | Type | | :------ | :------ | -| `writer` | [`FormState`](#formstate)\<`Values`\> | +| `writer` | [`FormState`](#formstate)\<`T`\> | | `setError` | [`SetError`](#seterror) | ##### Returns @@ -611,12 +611,12 @@ Invoked by validation methods when the form (or the validated paths) are valid. #### Defined in -[index.ts:60](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L60) +[index.ts:60](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L60) ### Method Types #### Check -Ƭ **Check**\<`Values`\>: (`json`: [`FormbitValues`](#formbitvalues), `options?`: [`CheckFnOptions`](#checkfnoptions)\<`Values`\>) => [`ValidationError`](#validationerror)[] \| `undefined` +Ƭ **Check**\<`T`\>: (`json`: [`FormbitValues`](#formbitvalues), `options?`: [`CheckFnOptions`](#checkfnoptions)\<`T`\>) => [`ValidationError`](#validationerror)[] \| `undefined` See [FormbitObject.check](#check). @@ -624,7 +624,7 @@ See [FormbitObject.check](#check). | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -635,7 +635,7 @@ See [FormbitObject.check](#check). | Name | Type | | :------ | :------ | | `json` | [`FormbitValues`](#formbitvalues) | -| `options?` | [`CheckFnOptions`](#checkfnoptions)\<`Values`\> | +| `options?` | [`CheckFnOptions`](#checkfnoptions)\<`T`\> | ##### Returns @@ -643,10 +643,10 @@ See [FormbitObject.check](#check). #### Defined in -[index.ts:89](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L89) +[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). @@ -654,7 +654,7 @@ See [FormbitObject.initialize](#initialize). | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -664,7 +664,7 @@ See [FormbitObject.initialize](#initialize). | Name | Type | | :------ | :------ | -| `values` | `Partial`\<`Values`\> | +| `values` | `Partial`\<`T`\> | ##### Returns @@ -672,10 +672,10 @@ See [FormbitObject.initialize](#initialize). #### Defined in -[index.ts:93](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L93) +[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). @@ -683,7 +683,7 @@ See [FormbitObject.remove](#remove). | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -694,7 +694,7 @@ See [FormbitObject.remove](#remove). | Name | Type | | :------ | :------ | | `path` | `string` | -| `options?` | [`WriteFnOptions`](#writefnoptions)\<`Values`\> | +| `options?` | [`WriteFnOptions`](#writefnoptions)\<`T`\> | ##### Returns @@ -702,10 +702,10 @@ See [FormbitObject.remove](#remove). #### Defined in -[index.ts:96](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L96) +[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). @@ -713,7 +713,7 @@ See [FormbitObject.removeAll](#removeall). | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -724,7 +724,7 @@ See [FormbitObject.removeAll](#removeall). | Name | Type | | :------ | :------ | | `arr` | `string`[] | -| `options?` | [`WriteFnOptions`](#writefnoptions)\<`Values`\> | +| `options?` | [`WriteFnOptions`](#writefnoptions)\<`T`\> | ##### Returns @@ -732,7 +732,7 @@ See [FormbitObject.removeAll](#removeall). #### Defined in -[index.ts:116](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L116) +[index.ts:116](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L116) #### SetError Ƭ **SetError**: (`path`: `string`, `value`: `string`) => `void` @@ -756,10 +756,10 @@ See [FormbitObject.setError](#seterror). #### Defined in -[index.ts:99](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L99) +[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). @@ -767,7 +767,7 @@ See [FormbitObject.setSchema](#setschema). | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -777,7 +777,7 @@ See [FormbitObject.setSchema](#setschema). | Name | Type | | :------ | :------ | -| `newSchema` | [`ValidationSchema`](#validationschema)\<`Values`\> | +| `newSchema` | [`ValidationSchema`](#validationschema)\<`T`\> | ##### Returns @@ -785,10 +785,10 @@ See [FormbitObject.setSchema](#setschema). #### Defined in -[index.ts:102](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L102) +[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). @@ -796,7 +796,7 @@ See [FormbitObject.submitForm](#submitform). | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -806,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 @@ -816,10 +816,10 @@ See [FormbitObject.submitForm](#submitform). #### Defined in -[index.ts:132](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L132) +[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). @@ -827,7 +827,7 @@ See [FormbitObject.validate](#validate). | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -838,7 +838,7 @@ See [FormbitObject.validate](#validate). | Name | Type | | :------ | :------ | | `path` | `string` | -| `options?` | [`ValidateFnOptions`](#validatefnoptions)\<`Values`\> | +| `options?` | [`ValidateFnOptions`](#validatefnoptions)\<`T`\> | ##### Returns @@ -846,10 +846,10 @@ See [FormbitObject.validate](#validate). #### Defined in -[index.ts:120](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L120) +[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). @@ -857,7 +857,7 @@ See [FormbitObject.validateAll](#validateall). | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -868,7 +868,7 @@ See [FormbitObject.validateAll](#validateall). | Name | Type | | :------ | :------ | | `paths` | `string`[] | -| `options?` | [`ValidateFnOptions`](#validatefnoptions)\<`Values`\> | +| `options?` | [`ValidateFnOptions`](#validatefnoptions)\<`T`\> | ##### Returns @@ -876,10 +876,10 @@ See [FormbitObject.validateAll](#validateall). #### Defined in -[index.ts:123](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L123) +[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). @@ -887,7 +887,7 @@ See [FormbitObject.validateForm](#validateform). | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -897,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 @@ -907,10 +907,10 @@ See [FormbitObject.validateForm](#validateform). #### Defined in -[index.ts:126](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L126) +[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). @@ -918,7 +918,7 @@ See [FormbitObject.write](#write). | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -928,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 @@ -938,10 +938,10 @@ See [FormbitObject.write](#write). #### Defined in -[index.ts:108](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L108) +[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). @@ -949,7 +949,7 @@ See [FormbitObject.writeAll](#writeall). | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Type declaration @@ -959,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 @@ -968,12 +968,12 @@ See [FormbitObject.writeAll](#writeall). #### Defined in -[index.ts:112](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L112) +[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 accepted by `check()`. @@ -981,22 +981,22 @@ Options accepted by `check()`. | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `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:140](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L140) +[index.ts:140](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L140) #### ValidateFnOptions -Ƭ **ValidateFnOptions**\<`Values`\>: `Object` +Ƭ **ValidateFnOptions**\<`T`\>: `Object` Options accepted by the `validate` methods. @@ -1004,22 +1004,22 @@ Options accepted by the `validate` methods. | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `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:147](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L147) +[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`] A single `[path, value]` pair accepted by `writeAll`. @@ -1027,14 +1027,14 @@ A single `[path, value]` pair accepted by `writeAll`. | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Defined in -[index.ts:105](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L105) +[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 accepted by the `write`/`remove` methods (validate options plus path control). @@ -1042,11 +1042,11 @@ Options accepted by the `write`/`remove` methods (validate options plus path con | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Defined in -[index.ts:154](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L154) +[index.ts:154](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L154) ### Yup Re-Exports #### ValidateOptions @@ -1057,7 +1057,7 @@ Options forwarded to yup's validation methods. See [https://github.com/jquense/y #### Defined in -[index.ts:52](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L52) +[index.ts:52](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L52) #### ValidationError Ƭ **ValidationError**: `YupValidationError` @@ -1066,10 +1066,10 @@ The error object yup throws when a validation fails. See [https://github.com/jqu #### Defined in -[index.ts:55](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L55) +[index.ts:55](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L55) #### ValidationSchema -Ƭ **ValidationSchema**\<`Values`\>: `ObjectSchema`\<`Values`\> +Ƭ **ValidationSchema**\<`T`\>: `ObjectSchema`\<`T`\> A validation schema built with `yup.object()`. See [https://github.com/jquense/yup](https://github.com/jquense/yup). @@ -1077,11 +1077,11 @@ A validation schema built with `yup.object()`. See [https://github.com/jquense/y | Name | Type | | :------ | :------ | -| `Values` | extends [`FormbitValues`](#formbitvalues) | +| `T` | extends [`FormbitValues`](#formbitvalues) | #### Defined in -[index.ts:49](https://github.com/radicalbit/formbit/blob/ea8fbb1/src/types/index.ts#L49) +[index.ts:49](https://github.com/radicalbit/formbit/blob/2ca1a8a/src/types/index.ts#L49) ## License diff --git a/src/types/index.ts b/src/types/index.ts index 83a8218..931e7c7 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -8,7 +8,7 @@ import { ACTIONS } from '../helpers/constants' * optional `__metadata` field formbit uses to carry data that must survive a * reset/initialize but must NOT be submitted. * - * The generic `Values` you pass to `useFormbit()` must extend this type. + * The generic `T` you pass to `useFormbit()` must extend this type. */ export type FormbitValues = Record & { __metadata?: Record } @@ -35,9 +35,9 @@ export type LiveValidation = Record /** * 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, @@ -46,7 +46,7 @@ export type FormState = { // ─── Yup re-exports ──────────────────────────────────────────────────────────── /** A validation schema built with `yup.object()`. See {@link https://github.com/jquense/yup}. */ -export type ValidationSchema = ObjectSchema +export type ValidationSchema = ObjectSchema /** Options forwarded to yup's validation methods. See {@link https://github.com/jquense/yup}. */ export type ValidateOptions = YupValidateOptions @@ -57,28 +57,28 @@ export type ValidationError = YupValidationError // ─── Callbacks ─────────────────────────────────────────────────────────────── /** Invoked by validation methods when the form (or the validated paths) are valid. */ -export type SuccessCallback = - (writer: FormState, setError: SetError) => void +export type SuccessCallback = + (writer: FormState, setError: SetError) => void /** Invoked by validation methods when validation fails. */ -export type ErrorCallback = - (writer: FormState, setError: SetError) => void +export type ErrorCallback = + (writer: FormState, setError: SetError) => void /** Invoked by `check()` when the given json is valid. */ -export type CheckSuccessCallback = - (json: FormbitValues, writer: FormState, setError: SetError) => void +export type CheckSuccessCallback = + (json: FormbitValues, writer: FormState, setError: SetError) => void /** Invoked by `check()` when the given json is invalid. */ -export type CheckErrorCallback = - (json: FormbitValues, inner: ValidationError[], writer: FormState, setError: SetError) => void +export type CheckErrorCallback = + (json: FormbitValues, inner: ValidationError[], writer: FormState, setError: SetError) => void /** * 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 @@ -86,75 +86,75 @@ export type SubmitSuccessCallback = // ─── Method signatures ───────────────────────────────────────────────────────── /** See {@link FormbitObject.check}. */ -export type Check = - (json: FormbitValues, 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 /** A single `[path, value]` pair accepted by `writeAll`. */ -export type WriteAllValue = [keyof Values | string, unknown] +export type WriteAllValue = [keyof T | string, unknown] /** See {@link FormbitObject.write}. */ -export type Write = - (path: keyof Values | string, value: unknown, options?: WriteFnOptions) => void +export type Write = + (path: keyof T | string, value: unknown, options?: WriteFnOptions) => void /** See {@link FormbitObject.writeAll}. */ -export type WriteAll = - (arr: WriteAllValue[], options?: WriteFnOptions) => void +export type WriteAll = + (arr: WriteAllValue[], options?: WriteFnOptions) => void /** See {@link FormbitObject.removeAll}. */ -export type RemoveAll = - (arr: string[], options?: WriteFnOptions) => void +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.submitForm}. */ -export type SubmitForm = ( - successCallback: SubmitSuccessCallback, - errorCallback?: ErrorCallback>, +export type SubmitForm = ( + successCallback: SubmitSuccessCallback, + errorCallback?: ErrorCallback>, options?: ValidateOptions) => void // ─── Options ───────────────────────────────────────────────────────────────── /** Options accepted by `check()`. */ -export type CheckFnOptions = { - successCallback?: CheckSuccessCallback, - errorCallback?: CheckErrorCallback, +export type CheckFnOptions = { + successCallback?: CheckSuccessCallback, + errorCallback?: CheckErrorCallback, options?: ValidateOptions } /** Options accepted by the `validate` methods. */ -export type ValidateFnOptions = { - successCallback?: SuccessCallback>, - errorCallback?: ErrorCallback>, +export type ValidateFnOptions = { + successCallback?: SuccessCallback>, + errorCallback?: ErrorCallback>, options?: ValidateOptions } /** Options accepted by the `write`/`remove` methods (validate options plus path control). */ -export type WriteFnOptions = { +export type WriteFnOptions = { noLiveValidation?: boolean, pathsToValidate?: string[] -} & ValidateFnOptions +} & ValidateFnOptions // ─── Internal types (not part of the public surface) ────────────────────────── @@ -162,19 +162,19 @@ export type WriteFnOptions = { export type Action = keyof typeof ACTIONS /** @internal */ -export type GenericCallback = SuccessCallback | ErrorCallback +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 Values | string, value: unknown, options?: WriteFnOptions, action?: Action) => void +export type WriteOrRemove = + (path: keyof T | string, value: unknown, options?: WriteFnOptions, action?: Action) => void /** @internal */ -export type PrivateValidateForm = ( - successCallback?: SuccessCallback, - errorCallback?: ErrorCallback>, +export type PrivateValidateForm = ( + successCallback?: SuccessCallback, + errorCallback?: ErrorCallback>, options?: { options?: ValidateOptions }) => void // ─── FormbitObject ─────────────────────────────────────────────────────────── @@ -183,11 +183,11 @@ export type PrivateValidateForm = ( * 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 --- /** The current form values. Partial: fields may be missing until validated. */ - form: Partial, + form: Partial, /** * Error messages registered since the last validation, keyed by the value's dot-path. @@ -216,7 +216,7 @@ export type FormbitObject = { liveValidation: (path: string) => true | undefined, /** Validates `json` against the current schema; returns the errors, or undefined if valid. */ - check: Check>, + check: Check>, // --- Mutations --- @@ -224,28 +224,28 @@ export type FormbitObject = { * Writes `value` at `path`, sets `isDirty`, then validates `pathsToValidate` * plus every live-validated field. */ - write: Write, + write: Write, /** * Writes every `[path, value]` pair, sets `isDirty`, then validates * `pathsToValidate` plus every live-validated field. */ - writeAll: WriteAll, + writeAll: WriteAll, /** * Removes the value at `path`, sets `isDirty`, then validates `pathsToValidate` * plus every live-validated field. */ - remove: Remove, + remove: Remove, /** * Removes every given path, sets `isDirty`, then validates `pathsToValidate` * plus every live-validated field. */ - removeAll: RemoveAll, + removeAll: RemoveAll, /** Re-initializes the form with new initial values. */ - initialize: Initialize, + initialize: Initialize, /** Resets form, errors, liveValidation and isDirty back to their initial state. */ resetForm: () => void, @@ -254,17 +254,17 @@ export type FormbitObject = { setError: SetError, /** Replaces the current validation schema. */ - setSchema: SetSchema, + setSchema: SetSchema, /** Validates only `path` (ignores live-validated fields). */ - validate: Validate, + validate: Validate, /** Validates only the given `paths` (ignores live-validated fields). */ - validateAll: ValidateAll, + validateAll: ValidateAll, /** Validates the whole form and registers any error. */ - validateForm: ValidateForm>, + validateForm: ValidateForm>, /** Validates the whole form and, if valid, runs the success callback to submit. */ - submitForm: SubmitForm, + submitForm: SubmitForm, } diff --git a/src/use-execute-callbacks.ts b/src/use-execute-callbacks.ts index ff11b99..27b4837 100644 --- a/src/use-execute-callbacks.ts +++ b/src/use-execute-callbacks.ts @@ -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 7226840..06b8ec2 100644 --- a/src/use-formbit.ts +++ b/src/use-formbit.ts @@ -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, { @@ -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, @@ -435,7 +435,7 @@ export default ({ } }, [setError, writer]) - const privateValidateForm: PrivateValidateForm> = useCallback(( + const privateValidateForm: PrivateValidateForm> = useCallback(( successCallback, errorCallback, { options } = {} @@ -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 c4df308..4c7c187 100644 --- a/src/validate-sync-all.ts +++ b/src/validate-sync-all.ts @@ -4,9 +4,9 @@ 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, + schema:ValidationSchema, form: FormbitValues, options: ValidateOptions = {} ): ValidationFormbitError[] => {