remix-validated-form 4.5.0-beta.0 → 4.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +7 -7
- package/browser/ValidatedForm.js +10 -32
- package/browser/internal/hooks.d.ts +3 -1
- package/browser/internal/hooks.js +2 -0
- package/browser/internal/logic/nestedObjectToPathObject.d.ts +1 -0
- package/browser/internal/logic/nestedObjectToPathObject.js +47 -0
- package/browser/internal/state/arrayUtil.d.ts +12 -0
- package/browser/internal/state/arrayUtil.js +337 -0
- package/browser/internal/state/createFormStore.d.ts +4 -2
- package/browser/internal/state/createFormStore.js +21 -4
- package/browser/internal/state/fieldArray.d.ts +28 -0
- package/browser/internal/state/fieldArray.js +73 -0
- package/browser/internal/state/types.d.ts +0 -0
- package/browser/internal/state/types.js +0 -0
- package/browser/unreleased/formStateHooks.d.ts +12 -2
- package/browser/unreleased/formStateHooks.js +15 -2
- package/browser/userFacingFormContext.d.ts +12 -2
- package/browser/userFacingFormContext.js +5 -1
- package/dist/remix-validated-form.cjs.js +3 -3
- package/dist/remix-validated-form.cjs.js.map +1 -1
- package/dist/remix-validated-form.es.js +55 -37
- package/dist/remix-validated-form.es.js.map +1 -1
- package/dist/remix-validated-form.umd.js +3 -3
- package/dist/remix-validated-form.umd.js.map +1 -1
- package/dist/types/internal/hooks.d.ts +3 -1
- package/dist/types/internal/state/createFormStore.d.ts +4 -2
- package/dist/types/unreleased/formStateHooks.d.ts +12 -2
- package/dist/types/userFacingFormContext.d.ts +12 -2
- package/package.json +1 -1
- package/src/ValidatedForm.tsx +24 -42
- package/src/internal/hooks.ts +6 -0
- package/src/internal/state/createFormStore.ts +40 -6
- package/src/unreleased/formStateHooks.ts +32 -3
- package/src/userFacingFormContext.ts +22 -2
- package/src/validation/validation.test.ts +7 -7
|
@@ -18,7 +18,7 @@ export declare const useInternalIsSubmitting: (formId: InternalFormId) => boolea
|
|
|
18
18
|
export declare const useInternalIsValid: (formId: InternalFormId) => boolean;
|
|
19
19
|
export declare const useInternalHasBeenSubmitted: (formId: InternalFormId) => boolean;
|
|
20
20
|
export declare const useValidateField: (formId: InternalFormId) => (fieldName: string) => Promise<string | null>;
|
|
21
|
-
export declare const useValidate: (formId: InternalFormId) => () => Promise<
|
|
21
|
+
export declare const useValidate: (formId: InternalFormId) => () => Promise<import("..").ValidationResult<unknown>>;
|
|
22
22
|
export declare const useRegisterReceiveFocus: (formId: InternalFormId) => (fieldName: string, handler: () => void) => () => void;
|
|
23
23
|
export declare const useSyncedDefaultValues: (formId: InternalFormId) => {
|
|
24
24
|
[fieldName: string]: any;
|
|
@@ -28,5 +28,7 @@ export declare const useTouchedFields: (formId: InternalFormId) => import("..").
|
|
|
28
28
|
export declare const useFieldErrors: (formId: InternalFormId) => FieldErrors;
|
|
29
29
|
export declare const useSetFieldErrors: (formId: InternalFormId) => (errors: FieldErrors) => void;
|
|
30
30
|
export declare const useResetFormElement: (formId: InternalFormId) => () => void;
|
|
31
|
+
export declare const useSubmitForm: (formId: InternalFormId) => () => void;
|
|
31
32
|
export declare const useFormActionProp: (formId: InternalFormId) => string | undefined;
|
|
32
33
|
export declare const useFormSubactionProp: (formId: InternalFormId) => string | undefined;
|
|
34
|
+
export declare const useFormValues: (formId: InternalFormId) => () => FormData;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WritableDraft } from "immer/dist/internal";
|
|
2
|
-
import { FieldErrors, TouchedFields, Validator } from "../../validation/types";
|
|
2
|
+
import { FieldErrors, TouchedFields, ValidationResult, Validator } from "../../validation/types";
|
|
3
3
|
import { InternalFormId } from "./types";
|
|
4
4
|
export declare type SyncedFormProps = {
|
|
5
5
|
formId?: string;
|
|
@@ -39,8 +39,10 @@ export declare type FormState = {
|
|
|
39
39
|
setHydrated: () => void;
|
|
40
40
|
setFormElement: (formElement: HTMLFormElement | null) => void;
|
|
41
41
|
validateField: (fieldName: string) => Promise<string | null>;
|
|
42
|
-
validate: () => Promise<
|
|
42
|
+
validate: () => Promise<ValidationResult<unknown>>;
|
|
43
43
|
resetFormElement: () => void;
|
|
44
|
+
submit: () => void;
|
|
45
|
+
getValues: () => FormData;
|
|
44
46
|
};
|
|
45
47
|
export declare const useRootFormStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<FormStoreState>, "setState"> & {
|
|
46
48
|
setState(nextStateOrUpdater: FormStoreState | Partial<FormStoreState> | ((state: WritableDraft<FormStoreState>) => void), shouldReplace?: boolean | undefined): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FieldErrors, TouchedFields } from "../validation/types";
|
|
1
|
+
import { FieldErrors, TouchedFields, ValidationResult } from "../validation/types";
|
|
2
2
|
export declare type FormState = {
|
|
3
3
|
fieldErrors: FieldErrors;
|
|
4
4
|
isSubmitting: boolean;
|
|
@@ -33,7 +33,7 @@ export declare type FormHelpers = {
|
|
|
33
33
|
/**
|
|
34
34
|
* Validate the whole form and populate any errors.
|
|
35
35
|
*/
|
|
36
|
-
validate: () => Promise<
|
|
36
|
+
validate: () => Promise<ValidationResult<unknown>>;
|
|
37
37
|
/**
|
|
38
38
|
* Clears all errors on the form.
|
|
39
39
|
*/
|
|
@@ -45,6 +45,16 @@ export declare type FormHelpers = {
|
|
|
45
45
|
* or clicking a button element with `type="reset"`.
|
|
46
46
|
*/
|
|
47
47
|
reset: () => void;
|
|
48
|
+
/**
|
|
49
|
+
* Submits the form, running all validations first.
|
|
50
|
+
*
|
|
51
|
+
* _Note_: This is equivalent to clicking a button element with `type="submit"` or calling formElement.submit().
|
|
52
|
+
*/
|
|
53
|
+
submit: () => void;
|
|
54
|
+
/**
|
|
55
|
+
* Returns the current form values as FormData
|
|
56
|
+
*/
|
|
57
|
+
getValues: () => FormData;
|
|
48
58
|
};
|
|
49
59
|
/**
|
|
50
60
|
* Returns helpers that can be used to update the form state.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FieldErrors, TouchedFields } from "./validation/types";
|
|
1
|
+
import { FieldErrors, TouchedFields, ValidationResult } from "./validation/types";
|
|
2
2
|
export declare type FormContextValue = {
|
|
3
3
|
/**
|
|
4
4
|
* All the errors in all the fields in the form.
|
|
@@ -56,7 +56,7 @@ export declare type FormContextValue = {
|
|
|
56
56
|
/**
|
|
57
57
|
* Validate the whole form and populate any errors.
|
|
58
58
|
*/
|
|
59
|
-
validate: () => Promise<
|
|
59
|
+
validate: () => Promise<ValidationResult<unknown>>;
|
|
60
60
|
/**
|
|
61
61
|
* Clears all errors on the form.
|
|
62
62
|
*/
|
|
@@ -68,6 +68,16 @@ export declare type FormContextValue = {
|
|
|
68
68
|
* or clicking a button element with `type="reset"`.
|
|
69
69
|
*/
|
|
70
70
|
reset: () => void;
|
|
71
|
+
/**
|
|
72
|
+
* Submits the form, running all validations first.
|
|
73
|
+
*
|
|
74
|
+
* _Note_: This is equivalent to clicking a button element with `type="submit"` or calling formElement.submit().
|
|
75
|
+
*/
|
|
76
|
+
submit: () => void;
|
|
77
|
+
/**
|
|
78
|
+
* Returns the current form values as FormData
|
|
79
|
+
*/
|
|
80
|
+
getValues: () => FormData;
|
|
71
81
|
};
|
|
72
82
|
/**
|
|
73
83
|
* Provides access to some of the internal state of the form.
|
package/package.json
CHANGED
package/src/ValidatedForm.tsx
CHANGED
|
@@ -98,7 +98,8 @@ const focusFirstInvalidInput = (
|
|
|
98
98
|
const namesInOrder = [...formElement.elements]
|
|
99
99
|
.map((el) => {
|
|
100
100
|
const input = el instanceof RadioNodeList ? el[0] : el;
|
|
101
|
-
if (input instanceof
|
|
101
|
+
if (input instanceof HTMLElement && "name" in input)
|
|
102
|
+
return (input as any).name;
|
|
102
103
|
return null;
|
|
103
104
|
})
|
|
104
105
|
.filter(nonNull)
|
|
@@ -129,8 +130,8 @@ const focusFirstInvalidInput = (
|
|
|
129
130
|
}
|
|
130
131
|
}
|
|
131
132
|
|
|
132
|
-
if (elem instanceof
|
|
133
|
-
if (elem.type === "hidden") {
|
|
133
|
+
if (elem instanceof HTMLElement) {
|
|
134
|
+
if (elem instanceof HTMLInputElement && elem.type === "hidden") {
|
|
134
135
|
continue;
|
|
135
136
|
}
|
|
136
137
|
|
|
@@ -296,33 +297,11 @@ export function ValidatedForm<DataType>({
|
|
|
296
297
|
endSubmit();
|
|
297
298
|
});
|
|
298
299
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
function handleClick(event: MouseEvent) {
|
|
305
|
-
if (!(event.target instanceof HTMLElement)) return;
|
|
306
|
-
let submitButton = event.target.closest<
|
|
307
|
-
HTMLButtonElement | HTMLInputElement
|
|
308
|
-
>("button,input[type=submit]");
|
|
309
|
-
|
|
310
|
-
if (
|
|
311
|
-
submitButton &&
|
|
312
|
-
submitButton.form === form &&
|
|
313
|
-
submitButton.type === "submit"
|
|
314
|
-
) {
|
|
315
|
-
clickedButtonRef.current = submitButton;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
window.addEventListener("click", handleClick, { capture: true });
|
|
320
|
-
return () => {
|
|
321
|
-
window.removeEventListener("click", handleClick, { capture: true });
|
|
322
|
-
};
|
|
323
|
-
}, []);
|
|
324
|
-
|
|
325
|
-
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
|
300
|
+
const handleSubmit = async (
|
|
301
|
+
e: FormEvent<HTMLFormElement>,
|
|
302
|
+
target: typeof e.currentTarget,
|
|
303
|
+
nativeEvent: HTMLSubmitEvent["nativeEvent"]
|
|
304
|
+
) => {
|
|
326
305
|
startSubmit();
|
|
327
306
|
const result = await validator.validate(getDataFromForm(e.currentTarget));
|
|
328
307
|
if (result.error) {
|
|
@@ -343,8 +322,7 @@ export function ValidatedForm<DataType>({
|
|
|
343
322
|
return;
|
|
344
323
|
}
|
|
345
324
|
|
|
346
|
-
const submitter =
|
|
347
|
-
.submitter as HTMLFormSubmitter | null;
|
|
325
|
+
const submitter = nativeEvent.submitter as HTMLFormSubmitter | null;
|
|
348
326
|
|
|
349
327
|
// We deviate from the remix code here a bit because of our async submit.
|
|
350
328
|
// In remix's `FormImpl`, they use `event.currentTarget` to get the form,
|
|
@@ -352,9 +330,7 @@ export function ValidatedForm<DataType>({
|
|
|
352
330
|
// If we use `event.currentTarget` here, it will break because `currentTarget`
|
|
353
331
|
// will have changed since the start of the submission.
|
|
354
332
|
if (fetcher) fetcher.submit(submitter || e.currentTarget);
|
|
355
|
-
else submit(submitter ||
|
|
356
|
-
|
|
357
|
-
clickedButtonRef.current = null;
|
|
333
|
+
else submit(submitter || target, { replace });
|
|
358
334
|
}
|
|
359
335
|
};
|
|
360
336
|
|
|
@@ -368,7 +344,11 @@ export function ValidatedForm<DataType>({
|
|
|
368
344
|
replace={replace}
|
|
369
345
|
onSubmit={(e) => {
|
|
370
346
|
e.preventDefault();
|
|
371
|
-
handleSubmit(
|
|
347
|
+
handleSubmit(
|
|
348
|
+
e,
|
|
349
|
+
e.currentTarget,
|
|
350
|
+
(e as unknown as HTMLSubmitEvent).nativeEvent
|
|
351
|
+
);
|
|
372
352
|
}}
|
|
373
353
|
onReset={(event) => {
|
|
374
354
|
onReset?.(event);
|
|
@@ -378,12 +358,14 @@ export function ValidatedForm<DataType>({
|
|
|
378
358
|
}}
|
|
379
359
|
>
|
|
380
360
|
<InternalFormContext.Provider value={contextValue}>
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
361
|
+
<>
|
|
362
|
+
<FormResetter formRef={formRef} resetAfterSubmit={resetAfterSubmit} />
|
|
363
|
+
{subaction && (
|
|
364
|
+
<input type="hidden" value={subaction} name="subaction" />
|
|
365
|
+
)}
|
|
366
|
+
{id && <input type="hidden" value={id} name={FORM_ID_FIELD} />}
|
|
367
|
+
{children}
|
|
368
|
+
</>
|
|
387
369
|
</InternalFormContext.Provider>
|
|
388
370
|
</Form>
|
|
389
371
|
);
|
package/src/internal/hooks.ts
CHANGED
|
@@ -196,8 +196,14 @@ export const useSetFieldErrors = (formId: InternalFormId) =>
|
|
|
196
196
|
export const useResetFormElement = (formId: InternalFormId) =>
|
|
197
197
|
useFormStore(formId, (state) => state.resetFormElement);
|
|
198
198
|
|
|
199
|
+
export const useSubmitForm = (formId: InternalFormId) =>
|
|
200
|
+
useFormStore(formId, (state) => state.submit);
|
|
201
|
+
|
|
199
202
|
export const useFormActionProp = (formId: InternalFormId) =>
|
|
200
203
|
useFormStore(formId, (state) => state.formProps?.action);
|
|
201
204
|
|
|
202
205
|
export const useFormSubactionProp = (formId: InternalFormId) =>
|
|
203
206
|
useFormStore(formId, (state) => state.formProps?.subaction);
|
|
207
|
+
|
|
208
|
+
export const useFormValues = (formId: InternalFormId) =>
|
|
209
|
+
useFormStore(formId, (state) => state.getValues);
|
|
@@ -2,7 +2,12 @@ import { WritableDraft } from "immer/dist/internal";
|
|
|
2
2
|
import invariant from "tiny-invariant";
|
|
3
3
|
import create, { GetState } from "zustand";
|
|
4
4
|
import { immer } from "zustand/middleware/immer";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
FieldErrors,
|
|
7
|
+
TouchedFields,
|
|
8
|
+
ValidationResult,
|
|
9
|
+
Validator,
|
|
10
|
+
} from "../../validation/types";
|
|
6
11
|
import { useControlledFieldStore } from "./controlledFieldStore";
|
|
7
12
|
import { InternalFormId } from "./types";
|
|
8
13
|
|
|
@@ -43,8 +48,10 @@ export type FormState = {
|
|
|
43
48
|
setHydrated: () => void;
|
|
44
49
|
setFormElement: (formElement: HTMLFormElement | null) => void;
|
|
45
50
|
validateField: (fieldName: string) => Promise<string | null>;
|
|
46
|
-
validate: () => Promise<
|
|
51
|
+
validate: () => Promise<ValidationResult<unknown>>;
|
|
47
52
|
resetFormElement: () => void;
|
|
53
|
+
submit: () => void;
|
|
54
|
+
getValues: () => FormData;
|
|
48
55
|
};
|
|
49
56
|
|
|
50
57
|
const noOp = () => {};
|
|
@@ -69,9 +76,16 @@ const defaultFormState: FormState = {
|
|
|
69
76
|
setFormElement: noOp,
|
|
70
77
|
validateField: async () => null,
|
|
71
78
|
|
|
72
|
-
validate: async () => {
|
|
79
|
+
validate: async () => {
|
|
80
|
+
throw new Error("Validate called before form was initialized.");
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
submit: async () => {
|
|
84
|
+
throw new Error("Submit called before form was initialized.");
|
|
85
|
+
},
|
|
73
86
|
|
|
74
87
|
resetFormElement: noOp,
|
|
88
|
+
getValues: () => new FormData(),
|
|
75
89
|
};
|
|
76
90
|
|
|
77
91
|
const createFormState = (
|
|
@@ -113,7 +127,6 @@ const createFormState = (
|
|
|
113
127
|
set((state) => {
|
|
114
128
|
delete state.fieldErrors[fieldName];
|
|
115
129
|
}),
|
|
116
|
-
|
|
117
130
|
reset: () =>
|
|
118
131
|
set((state) => {
|
|
119
132
|
state.fieldErrors = {};
|
|
@@ -181,8 +194,29 @@ const createFormState = (
|
|
|
181
194
|
"Cannot validator. This is probably a bug in remix-validated-form."
|
|
182
195
|
);
|
|
183
196
|
|
|
184
|
-
const
|
|
185
|
-
if (error) get().setFieldErrors(error.fieldErrors);
|
|
197
|
+
const result = await validator.validate(new FormData(formElement));
|
|
198
|
+
if (result.error) get().setFieldErrors(result.error.fieldErrors);
|
|
199
|
+
return result;
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
submit: () => {
|
|
203
|
+
const formElement = get().formElement;
|
|
204
|
+
invariant(
|
|
205
|
+
formElement,
|
|
206
|
+
"Cannot find reference to form. This is probably a bug in remix-validated-form."
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
formElement.submit();
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
getValues: () => {
|
|
213
|
+
const formElement = get().formElement;
|
|
214
|
+
invariant(
|
|
215
|
+
formElement,
|
|
216
|
+
"Cannot find reference to form. This is probably a bug in remix-validated-form."
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
return new FormData(formElement);
|
|
186
220
|
},
|
|
187
221
|
|
|
188
222
|
resetFormElement: () => get().formElement?.reset(),
|
|
@@ -18,8 +18,14 @@ import {
|
|
|
18
18
|
useSyncedDefaultValues,
|
|
19
19
|
useFormActionProp,
|
|
20
20
|
useFormSubactionProp,
|
|
21
|
+
useSubmitForm,
|
|
22
|
+
useFormValues,
|
|
21
23
|
} from "../internal/hooks";
|
|
22
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
FieldErrors,
|
|
26
|
+
TouchedFields,
|
|
27
|
+
ValidationResult,
|
|
28
|
+
} from "../validation/types";
|
|
23
29
|
|
|
24
30
|
export type FormState = {
|
|
25
31
|
fieldErrors: FieldErrors;
|
|
@@ -95,7 +101,7 @@ export type FormHelpers = {
|
|
|
95
101
|
/**
|
|
96
102
|
* Validate the whole form and populate any errors.
|
|
97
103
|
*/
|
|
98
|
-
validate: () => Promise<
|
|
104
|
+
validate: () => Promise<ValidationResult<unknown>>;
|
|
99
105
|
/**
|
|
100
106
|
* Clears all errors on the form.
|
|
101
107
|
*/
|
|
@@ -107,6 +113,16 @@ export type FormHelpers = {
|
|
|
107
113
|
* or clicking a button element with `type="reset"`.
|
|
108
114
|
*/
|
|
109
115
|
reset: () => void;
|
|
116
|
+
/**
|
|
117
|
+
* Submits the form, running all validations first.
|
|
118
|
+
*
|
|
119
|
+
* _Note_: This is equivalent to clicking a button element with `type="submit"` or calling formElement.submit().
|
|
120
|
+
*/
|
|
121
|
+
submit: () => void;
|
|
122
|
+
/**
|
|
123
|
+
* Returns the current form values as FormData
|
|
124
|
+
*/
|
|
125
|
+
getValues: () => FormData;
|
|
110
126
|
};
|
|
111
127
|
|
|
112
128
|
/**
|
|
@@ -122,6 +138,8 @@ export const useFormHelpers = (formId?: string): FormHelpers => {
|
|
|
122
138
|
const clearError = useClearError(formContext);
|
|
123
139
|
const setFieldErrors = useSetFieldErrors(formContext.formId);
|
|
124
140
|
const reset = useResetFormElement(formContext.formId);
|
|
141
|
+
const submit = useSubmitForm(formContext.formId);
|
|
142
|
+
const getValues = useFormValues(formContext.formId);
|
|
125
143
|
return useMemo(
|
|
126
144
|
() => ({
|
|
127
145
|
setTouched,
|
|
@@ -130,7 +148,18 @@ export const useFormHelpers = (formId?: string): FormHelpers => {
|
|
|
130
148
|
validate,
|
|
131
149
|
clearAllErrors: () => setFieldErrors({}),
|
|
132
150
|
reset,
|
|
151
|
+
submit,
|
|
152
|
+
getValues,
|
|
133
153
|
}),
|
|
134
|
-
[
|
|
154
|
+
[
|
|
155
|
+
clearError,
|
|
156
|
+
reset,
|
|
157
|
+
setFieldErrors,
|
|
158
|
+
setTouched,
|
|
159
|
+
submit,
|
|
160
|
+
validate,
|
|
161
|
+
validateField,
|
|
162
|
+
getValues,
|
|
163
|
+
]
|
|
135
164
|
);
|
|
136
165
|
};
|
|
@@ -4,7 +4,11 @@ import {
|
|
|
4
4
|
useRegisterReceiveFocus,
|
|
5
5
|
} from "./internal/hooks";
|
|
6
6
|
import { useFormHelpers, useFormState } from "./unreleased/formStateHooks";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
FieldErrors,
|
|
9
|
+
TouchedFields,
|
|
10
|
+
ValidationResult,
|
|
11
|
+
} from "./validation/types";
|
|
8
12
|
|
|
9
13
|
export type FormContextValue = {
|
|
10
14
|
/**
|
|
@@ -61,7 +65,7 @@ export type FormContextValue = {
|
|
|
61
65
|
/**
|
|
62
66
|
* Validate the whole form and populate any errors.
|
|
63
67
|
*/
|
|
64
|
-
validate: () => Promise<
|
|
68
|
+
validate: () => Promise<ValidationResult<unknown>>;
|
|
65
69
|
/**
|
|
66
70
|
* Clears all errors on the form.
|
|
67
71
|
*/
|
|
@@ -73,6 +77,16 @@ export type FormContextValue = {
|
|
|
73
77
|
* or clicking a button element with `type="reset"`.
|
|
74
78
|
*/
|
|
75
79
|
reset: () => void;
|
|
80
|
+
/**
|
|
81
|
+
* Submits the form, running all validations first.
|
|
82
|
+
*
|
|
83
|
+
* _Note_: This is equivalent to clicking a button element with `type="submit"` or calling formElement.submit().
|
|
84
|
+
*/
|
|
85
|
+
submit: () => void;
|
|
86
|
+
/**
|
|
87
|
+
* Returns the current form values as FormData
|
|
88
|
+
*/
|
|
89
|
+
getValues: () => FormData;
|
|
76
90
|
};
|
|
77
91
|
|
|
78
92
|
/**
|
|
@@ -89,6 +103,8 @@ export const useFormContext = (formId?: string): FormContextValue => {
|
|
|
89
103
|
clearAllErrors,
|
|
90
104
|
validate,
|
|
91
105
|
reset,
|
|
106
|
+
submit,
|
|
107
|
+
getValues,
|
|
92
108
|
} = useFormHelpers(formId);
|
|
93
109
|
|
|
94
110
|
const registerReceiveFocus = useRegisterReceiveFocus(context.formId);
|
|
@@ -112,6 +128,8 @@ export const useFormContext = (formId?: string): FormContextValue => {
|
|
|
112
128
|
clearAllErrors,
|
|
113
129
|
validate,
|
|
114
130
|
reset,
|
|
131
|
+
submit,
|
|
132
|
+
getValues,
|
|
115
133
|
}),
|
|
116
134
|
[
|
|
117
135
|
clearAllErrors,
|
|
@@ -120,8 +138,10 @@ export const useFormContext = (formId?: string): FormContextValue => {
|
|
|
120
138
|
reset,
|
|
121
139
|
setTouched,
|
|
122
140
|
state,
|
|
141
|
+
submit,
|
|
123
142
|
validate,
|
|
124
143
|
validateField,
|
|
144
|
+
getValues,
|
|
125
145
|
]
|
|
126
146
|
);
|
|
127
147
|
};
|
|
@@ -56,21 +56,21 @@ const validationTestCases: ValidationTestCase[] = [
|
|
|
56
56
|
name: "zod",
|
|
57
57
|
validator: withZod(
|
|
58
58
|
z.object({
|
|
59
|
-
firstName: z.string().
|
|
60
|
-
lastName: z.string().
|
|
59
|
+
firstName: z.string().min(1),
|
|
60
|
+
lastName: z.string().min(1),
|
|
61
61
|
age: z.optional(z.number()),
|
|
62
62
|
address: z.preprocess(
|
|
63
63
|
(value) => (value == null ? {} : value),
|
|
64
64
|
z.object({
|
|
65
|
-
streetAddress: z.string().
|
|
66
|
-
city: z.string().
|
|
67
|
-
country: z.string().
|
|
65
|
+
streetAddress: z.string().min(1),
|
|
66
|
+
city: z.string().min(1),
|
|
67
|
+
country: z.string().min(1),
|
|
68
68
|
})
|
|
69
69
|
),
|
|
70
70
|
pets: z
|
|
71
71
|
.object({
|
|
72
|
-
animal: z.string().
|
|
73
|
-
name: z.string().
|
|
72
|
+
animal: z.string().min(1),
|
|
73
|
+
name: z.string().min(1),
|
|
74
74
|
})
|
|
75
75
|
.array()
|
|
76
76
|
.optional(),
|