remix-validated-form 4.6.0-beta.0 → 4.6.1-beta.0
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 +10 -10
- package/browser/ValidatedForm.js +13 -5
- package/browser/internal/hooks.d.ts +1 -3
- package/browser/internal/logic/requestSubmit.d.ts +5 -0
- package/browser/internal/logic/requestSubmit.js +66 -0
- package/browser/internal/state/createFormStore.js +2 -1
- package/browser/internal/state/fieldArray.d.ts +1 -1
- package/browser/internal/state/fieldArray.js +2 -1
- package/browser/validation/createValidator.js +3 -1
- package/dist/remix-validated-form.cjs.js +4 -4
- package/dist/remix-validated-form.cjs.js.map +1 -1
- package/dist/remix-validated-form.es.js +796 -1734
- package/dist/remix-validated-form.es.js.map +1 -1
- package/dist/remix-validated-form.umd.js +4 -4
- package/dist/remix-validated-form.umd.js.map +1 -1
- package/dist/types/internal/flatten.d.ts +1 -1
- package/dist/types/internal/logic/requestSubmit.d.ts +5 -0
- package/dist/types/internal/state/fieldArray.d.ts +1 -1
- package/package.json +6 -4
- package/src/ValidatedForm.tsx +22 -8
- package/src/internal/flatten.ts +4 -3
- package/src/internal/getInputProps.ts +2 -2
- package/src/internal/hooks.ts +3 -3
- package/src/internal/logic/requestSubmit.test.tsx +24 -0
- package/src/internal/logic/requestSubmit.ts +103 -0
- package/src/internal/state/arrayUtil.ts +5 -6
- package/src/internal/state/createFormStore.ts +9 -10
- package/src/internal/state/fieldArray.tsx +1 -1
- package/src/internal/util.ts +2 -2
- package/src/validation/createValidator.ts +5 -1
- package/src/validation/validation.test.ts +26 -0
- package/stats.html +4044 -0
@@ -1 +1 @@
|
|
1
|
-
export declare const objectFromPathEntries: (entries: [string, any][]) =>
|
1
|
+
export declare const objectFromPathEntries: (entries: [string, any][]) => Record<string, any>;
|
@@ -0,0 +1,5 @@
|
|
1
|
+
/**
|
2
|
+
* Ponyfill of the HTMLFormElement.requestSubmit() method.
|
3
|
+
* Based on polyfill from: https://github.com/javan/form-request-submit-polyfill/blob/main/form-request-submit-polyfill.js
|
4
|
+
*/
|
5
|
+
export declare const requestSubmit: (element: HTMLFormElement, submitter?: HTMLElement | undefined) => void;
|
@@ -25,4 +25,4 @@ export declare type FieldArrayProps = {
|
|
25
25
|
formId?: string;
|
26
26
|
validationBehavior?: FieldArrayValidationBehaviorOptions;
|
27
27
|
};
|
28
|
-
export declare const FieldArray: ({ name, children, formId, validationBehavior, }: FieldArrayProps) =>
|
28
|
+
export declare const FieldArray: ({ name, children, formId, validationBehavior, }: FieldArrayProps) => JSX.Element;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "remix-validated-form",
|
3
|
-
"version": "4.6.
|
3
|
+
"version": "4.6.1-beta.0",
|
4
4
|
"description": "Form component and utils for easy form validation in remix",
|
5
5
|
"browser": "./dist/remix-validated-form.cjs.js",
|
6
6
|
"main": "./dist/remix-validated-form.umd.js",
|
@@ -38,17 +38,19 @@
|
|
38
38
|
},
|
39
39
|
"devDependencies": {
|
40
40
|
"@remix-run/react": "^1.6.5",
|
41
|
-
"@
|
41
|
+
"@testing-library/react": "^13.3.0",
|
42
42
|
"@types/react": "^18.0.9",
|
43
43
|
"fetch-blob": "^3.1.3",
|
44
44
|
"react": "^18.1.0",
|
45
|
+
"ts-toolbelt": "^9.6.0",
|
45
46
|
"tsconfig": "*",
|
46
|
-
"typescript": "^4.
|
47
|
+
"typescript": "^4.8.4",
|
47
48
|
"vite-config": "*"
|
48
49
|
},
|
49
50
|
"dependencies": {
|
50
51
|
"immer": "^9.0.12",
|
51
|
-
"
|
52
|
+
"remeda": "^1.2.0",
|
53
|
+
"setGet": "*",
|
52
54
|
"tiny-invariant": "^1.2.0",
|
53
55
|
"zustand": "^4.0.0-rc.1"
|
54
56
|
}
|
package/src/ValidatedForm.tsx
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
-
import {
|
2
|
-
|
1
|
+
import {
|
2
|
+
Form as RemixForm,
|
3
|
+
FormMethod,
|
4
|
+
useFetcher,
|
5
|
+
useSubmit,
|
6
|
+
} from "@remix-run/react";
|
3
7
|
import React, {
|
4
8
|
ComponentProps,
|
5
9
|
FormEvent,
|
@@ -10,6 +14,7 @@ import React, {
|
|
10
14
|
useRef,
|
11
15
|
useState,
|
12
16
|
} from "react";
|
17
|
+
import * as R from "remeda";
|
13
18
|
import { useIsSubmitting, useIsValid } from "./hooks";
|
14
19
|
import { FORM_ID_FIELD } from "./internal/constants";
|
15
20
|
import {
|
@@ -103,7 +108,7 @@ const focusFirstInvalidInput = (
|
|
103
108
|
})
|
104
109
|
.filter(nonNull)
|
105
110
|
.filter((name) => name in fieldErrors);
|
106
|
-
const uniqueNamesInOrder = uniq(namesInOrder);
|
111
|
+
const uniqueNamesInOrder = R.uniq(namesInOrder);
|
107
112
|
|
108
113
|
for (const fieldName of uniqueNamesInOrder) {
|
109
114
|
if (customFocusHandlers.has(fieldName)) {
|
@@ -301,10 +306,16 @@ export function ValidatedForm<DataType>({
|
|
301
306
|
nativeEvent: HTMLSubmitEvent["nativeEvent"]
|
302
307
|
) => {
|
303
308
|
startSubmit();
|
304
|
-
const
|
309
|
+
const submitter = nativeEvent.submitter as HTMLFormSubmitter | null;
|
310
|
+
const formDataToValidate = getDataFromForm(e.currentTarget);
|
311
|
+
if (submitter?.name) {
|
312
|
+
formDataToValidate.append(submitter.name, submitter.value);
|
313
|
+
}
|
314
|
+
|
315
|
+
const result = await validator.validate(formDataToValidate);
|
305
316
|
if (result.error) {
|
306
|
-
endSubmit();
|
307
317
|
setFieldErrors(result.error.fieldErrors);
|
318
|
+
endSubmit();
|
308
319
|
if (!disableFocusOnError) {
|
309
320
|
focusFirstInvalidInput(
|
310
321
|
result.error.fieldErrors,
|
@@ -313,6 +324,7 @@ export function ValidatedForm<DataType>({
|
|
313
324
|
);
|
314
325
|
}
|
315
326
|
} else {
|
327
|
+
setFieldErrors({});
|
316
328
|
const eventProxy = formEventProxy(e);
|
317
329
|
await onSubmit?.(result.data, eventProxy);
|
318
330
|
if (eventProxy.defaultPrevented) {
|
@@ -320,15 +332,17 @@ export function ValidatedForm<DataType>({
|
|
320
332
|
return;
|
321
333
|
}
|
322
334
|
|
323
|
-
const submitter = nativeEvent.submitter as HTMLFormSubmitter | null;
|
324
|
-
|
325
335
|
// We deviate from the remix code here a bit because of our async submit.
|
326
336
|
// In remix's `FormImpl`, they use `event.currentTarget` to get the form,
|
327
337
|
// but we already have the form in `formRef.current` so we can just use that.
|
328
338
|
// If we use `event.currentTarget` here, it will break because `currentTarget`
|
329
339
|
// will have changed since the start of the submission.
|
330
340
|
if (fetcher) fetcher.submit(submitter || e.currentTarget);
|
331
|
-
else
|
341
|
+
else
|
342
|
+
submit(submitter || target, {
|
343
|
+
replace,
|
344
|
+
method: (submitter?.formMethod as FormMethod) || method,
|
345
|
+
});
|
332
346
|
}
|
333
347
|
};
|
334
348
|
|
package/src/internal/flatten.ts
CHANGED
@@ -1,11 +1,12 @@
|
|
1
|
-
import
|
1
|
+
import * as R from "remeda";
|
2
2
|
import { MultiValueMap } from "./MultiValueMap";
|
3
3
|
|
4
4
|
export const objectFromPathEntries = (entries: [string, any][]) => {
|
5
5
|
const map = new MultiValueMap<string, any>();
|
6
6
|
entries.forEach(([key, value]) => map.add(key, value));
|
7
7
|
return [...map.entries()].reduce(
|
8
|
-
(acc, [key, value]) =>
|
9
|
-
|
8
|
+
(acc, [key, value]) =>
|
9
|
+
R.set(acc, key, value.length === 1 ? value[0] : value),
|
10
|
+
{} as Record<string, any>
|
10
11
|
);
|
11
12
|
};
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import
|
1
|
+
import * as R from "remeda";
|
2
2
|
import { getCheckboxChecked } from "./logic/getCheckboxChecked";
|
3
3
|
import { getRadioChecked } from "./logic/getRadioChecked";
|
4
4
|
|
@@ -89,6 +89,6 @@ export const createGetInputProps = ({
|
|
89
89
|
inputProps.defaultValue = defaultValue;
|
90
90
|
}
|
91
91
|
|
92
|
-
return omitBy(inputProps, (value) => value === undefined) as T;
|
92
|
+
return R.omitBy(inputProps, (value) => value === undefined) as T;
|
93
93
|
};
|
94
94
|
};
|
package/src/internal/hooks.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { useActionData, useMatches, useTransition } from "@remix-run/react";
|
2
|
-
import lodashGet from "lodash/get";
|
3
2
|
import { useCallback, useContext } from "react";
|
3
|
+
import { getPath } from "setGet";
|
4
4
|
import invariant from "tiny-invariant";
|
5
5
|
import { FieldErrors, ValidationErrorResponseData } from "..";
|
6
6
|
import { formDefaultValuesKey } from "./constants";
|
@@ -145,7 +145,7 @@ export const useCurrentDefaultValueForField = (
|
|
145
145
|
formId: InternalFormId,
|
146
146
|
field: string
|
147
147
|
) =>
|
148
|
-
useFormStore(formId, (state) =>
|
148
|
+
useFormStore(formId, (state) => getPath(state.currentDefaultValues, field));
|
149
149
|
|
150
150
|
export const useFieldDefaultValue = (
|
151
151
|
name: string,
|
@@ -154,7 +154,7 @@ export const useFieldDefaultValue = (
|
|
154
154
|
const defaultValues = useDefaultValuesForForm(context);
|
155
155
|
const state = useCurrentDefaultValueForField(context.formId, name);
|
156
156
|
|
157
|
-
return defaultValues.map((val) =>
|
157
|
+
return defaultValues.map((val) => getPath(val, name)).hydrateTo(state);
|
158
158
|
};
|
159
159
|
|
160
160
|
export const useInternalIsSubmitting = (formId: InternalFormId) =>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import { render } from "@testing-library/react";
|
2
|
+
import React, { createRef } from "react";
|
3
|
+
import { describe, expect, it, vi } from "vitest";
|
4
|
+
import { requestSubmit } from "./requestSubmit";
|
5
|
+
|
6
|
+
describe("requestSubmit polyfill", () => {
|
7
|
+
it("should polyfill requestSubmit", () => {
|
8
|
+
const submit = vi.fn();
|
9
|
+
const ref = createRef<HTMLFormElement>();
|
10
|
+
render(
|
11
|
+
<form
|
12
|
+
onSubmit={(event) => {
|
13
|
+
event.preventDefault();
|
14
|
+
submit();
|
15
|
+
}}
|
16
|
+
ref={ref}
|
17
|
+
>
|
18
|
+
<input name="test" value="testing" />
|
19
|
+
</form>
|
20
|
+
);
|
21
|
+
requestSubmit(ref.current!);
|
22
|
+
expect(submit).toHaveBeenCalledTimes(1);
|
23
|
+
});
|
24
|
+
});
|
@@ -0,0 +1,103 @@
|
|
1
|
+
/**
|
2
|
+
* Ponyfill of the HTMLFormElement.requestSubmit() method.
|
3
|
+
* Based on polyfill from: https://github.com/javan/form-request-submit-polyfill/blob/main/form-request-submit-polyfill.js
|
4
|
+
*/
|
5
|
+
export const requestSubmit = (
|
6
|
+
element: HTMLFormElement,
|
7
|
+
submitter?: HTMLElement
|
8
|
+
) => {
|
9
|
+
// In vitest, let's test the polyfill.
|
10
|
+
// Cypress will test the native implementation by nature of using chrome.
|
11
|
+
if (
|
12
|
+
typeof Object.getPrototypeOf(element).requestSubmit === "function" &&
|
13
|
+
!import.meta.vitest
|
14
|
+
) {
|
15
|
+
element.requestSubmit(submitter);
|
16
|
+
return;
|
17
|
+
}
|
18
|
+
|
19
|
+
if (submitter) {
|
20
|
+
validateSubmitter(element, submitter);
|
21
|
+
submitter.click();
|
22
|
+
return;
|
23
|
+
}
|
24
|
+
|
25
|
+
const dummySubmitter = document.createElement("input");
|
26
|
+
dummySubmitter.type = "submit";
|
27
|
+
dummySubmitter.hidden = true;
|
28
|
+
element.appendChild(dummySubmitter);
|
29
|
+
dummySubmitter.click();
|
30
|
+
element.removeChild(dummySubmitter);
|
31
|
+
};
|
32
|
+
|
33
|
+
function validateSubmitter(element: HTMLFormElement, submitter: HTMLElement) {
|
34
|
+
// Should be redundant, but here for completeness
|
35
|
+
const isHtmlElement = submitter instanceof HTMLElement;
|
36
|
+
if (!isHtmlElement) {
|
37
|
+
raise(TypeError, "parameter 1 is not of type 'HTMLElement'");
|
38
|
+
}
|
39
|
+
|
40
|
+
const hasSubmitType =
|
41
|
+
"type" in submitter && (submitter as HTMLInputElement).type === "submit";
|
42
|
+
if (!hasSubmitType)
|
43
|
+
raise(TypeError, "The specified element is not a submit button");
|
44
|
+
|
45
|
+
const isForCorrectForm =
|
46
|
+
"form" in submitter && (submitter as HTMLInputElement).form === element;
|
47
|
+
if (!isForCorrectForm)
|
48
|
+
raise(
|
49
|
+
DOMException,
|
50
|
+
"The specified element is not owned by this form element",
|
51
|
+
"NotFoundError"
|
52
|
+
);
|
53
|
+
}
|
54
|
+
|
55
|
+
interface ErrorConstructor {
|
56
|
+
new (message: string, name?: string): Error;
|
57
|
+
}
|
58
|
+
|
59
|
+
function raise(
|
60
|
+
errorConstructor: ErrorConstructor,
|
61
|
+
message: string,
|
62
|
+
name?: string
|
63
|
+
): never {
|
64
|
+
throw new errorConstructor(
|
65
|
+
"Failed to execute 'requestSubmit' on 'HTMLFormElement': " + message + ".",
|
66
|
+
name
|
67
|
+
);
|
68
|
+
}
|
69
|
+
|
70
|
+
if (import.meta.vitest) {
|
71
|
+
const { it, expect } = import.meta.vitest;
|
72
|
+
it("should validate the submitter", () => {
|
73
|
+
const form = document.createElement("form");
|
74
|
+
document.body.appendChild(form);
|
75
|
+
|
76
|
+
const submitter = document.createElement("input");
|
77
|
+
expect(() => validateSubmitter(null as any, null as any)).toThrow();
|
78
|
+
expect(() => validateSubmitter(form, null as any)).toThrow();
|
79
|
+
expect(() => validateSubmitter(form, submitter)).toThrow();
|
80
|
+
expect(() =>
|
81
|
+
validateSubmitter(form, document.createElement("div"))
|
82
|
+
).toThrow();
|
83
|
+
|
84
|
+
submitter.type = "submit";
|
85
|
+
expect(() => validateSubmitter(form, submitter)).toThrow();
|
86
|
+
|
87
|
+
form.appendChild(submitter);
|
88
|
+
expect(() => validateSubmitter(form, submitter)).not.toThrow();
|
89
|
+
|
90
|
+
form.removeChild(submitter);
|
91
|
+
expect(() => validateSubmitter(form, submitter)).toThrow();
|
92
|
+
|
93
|
+
document.body.appendChild(submitter);
|
94
|
+
form.id = "test-form";
|
95
|
+
submitter.setAttribute("form", "test-form");
|
96
|
+
expect(() => validateSubmitter(form, submitter)).not.toThrow();
|
97
|
+
|
98
|
+
const button = document.createElement("button");
|
99
|
+
button.type = "submit";
|
100
|
+
form.appendChild(button);
|
101
|
+
expect(() => validateSubmitter(form, button)).not.toThrow();
|
102
|
+
});
|
103
|
+
}
|
@@ -1,5 +1,4 @@
|
|
1
|
-
import
|
2
|
-
import lodashSet from "lodash/set";
|
1
|
+
import { getPath, setPath } from "setGet";
|
3
2
|
import invariant from "tiny-invariant";
|
4
3
|
|
5
4
|
////
|
@@ -8,10 +7,10 @@ import invariant from "tiny-invariant";
|
|
8
7
|
////
|
9
8
|
|
10
9
|
export const getArray = (values: any, field: string): unknown[] => {
|
11
|
-
const value =
|
10
|
+
const value = getPath(values, field);
|
12
11
|
if (value === undefined || value === null) {
|
13
12
|
const newValue: unknown[] = [];
|
14
|
-
|
13
|
+
setPath(values, field, newValue);
|
15
14
|
return newValue;
|
16
15
|
}
|
17
16
|
invariant(
|
@@ -94,8 +93,8 @@ export const mutateAsArray = (
|
|
94
93
|
for (const [key, value] of Object.entries(obj)) {
|
95
94
|
if (key.startsWith(field) && key !== field) {
|
96
95
|
beforeKeys.add(key);
|
96
|
+
setPath(arr, key.substring(field.length), value);
|
97
97
|
}
|
98
|
-
lodashSet(arr, key.substring(field.length), value);
|
99
98
|
}
|
100
99
|
|
101
100
|
mutate(arr);
|
@@ -105,7 +104,7 @@ export const mutateAsArray = (
|
|
105
104
|
|
106
105
|
const newKeys = getDeepArrayPaths(arr);
|
107
106
|
for (const key of newKeys) {
|
108
|
-
const val =
|
107
|
+
const val = getPath(arr, key);
|
109
108
|
obj[`${field}${key}`] = val;
|
110
109
|
}
|
111
110
|
};
|
@@ -1,6 +1,5 @@
|
|
1
1
|
import { WritableDraft } from "immer/dist/internal";
|
2
|
-
import
|
3
|
-
import lodashSet from "lodash/set";
|
2
|
+
import { getPath, setPath } from "setGet";
|
4
3
|
import invariant from "tiny-invariant";
|
5
4
|
import create, { GetState } from "zustand";
|
6
5
|
import { immer } from "zustand/middleware/immer";
|
@@ -10,6 +9,7 @@ import {
|
|
10
9
|
ValidationResult,
|
11
10
|
Validator,
|
12
11
|
} from "../../validation/types";
|
12
|
+
import { requestSubmit } from "../logic/requestSubmit";
|
13
13
|
import * as arrayUtil from "./arrayUtil";
|
14
14
|
import { InternalFormId } from "./types";
|
15
15
|
|
@@ -264,7 +264,7 @@ const createFormState = (
|
|
264
264
|
"Cannot find reference to form. This is probably a bug in remix-validated-form."
|
265
265
|
);
|
266
266
|
|
267
|
-
|
267
|
+
requestSubmit(formElement);
|
268
268
|
},
|
269
269
|
|
270
270
|
getValues: () => new FormData(get().formElement ?? undefined),
|
@@ -300,26 +300,25 @@ const createFormState = (
|
|
300
300
|
|
301
301
|
// When nested within a field array, we should leave resetting up to the field array
|
302
302
|
if (!isNested) {
|
303
|
-
|
303
|
+
setPath(
|
304
304
|
state.controlledFields.values,
|
305
305
|
fieldName,
|
306
|
-
|
306
|
+
getPath(state.formProps?.defaultValues, fieldName)
|
307
307
|
);
|
308
|
-
|
308
|
+
setPath(
|
309
309
|
state.currentDefaultValues,
|
310
310
|
fieldName,
|
311
|
-
|
311
|
+
getPath(state.formProps?.defaultValues, fieldName)
|
312
312
|
);
|
313
313
|
}
|
314
314
|
|
315
315
|
delete state.controlledFields.refCounts[fieldName];
|
316
316
|
});
|
317
317
|
},
|
318
|
-
getValue: (fieldName) =>
|
319
|
-
lodashGet(get().controlledFields.values, fieldName),
|
318
|
+
getValue: (fieldName) => getPath(get().controlledFields.values, fieldName),
|
320
319
|
setValue: (fieldName, value) => {
|
321
320
|
set((state) => {
|
322
|
-
|
321
|
+
setPath(state.controlledFields.values, fieldName, value);
|
323
322
|
});
|
324
323
|
get().controlledFields.kickoffValueUpdate(fieldName);
|
325
324
|
},
|
package/src/internal/util.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
import isEqual from "lodash/isEqual";
|
2
1
|
import type React from "react";
|
3
2
|
import { useEffect, useLayoutEffect, useRef } from "react";
|
3
|
+
import * as R from "remeda";
|
4
4
|
|
5
5
|
export const omit = (obj: any, ...keys: string[]) => {
|
6
6
|
const result = { ...obj };
|
@@ -29,7 +29,7 @@ export const useIsomorphicLayoutEffect =
|
|
29
29
|
|
30
30
|
export const useDeepEqualsMemo = <T>(item: T): T => {
|
31
31
|
const ref = useRef<T>(item);
|
32
|
-
const areEqual = ref.current === item ||
|
32
|
+
const areEqual = ref.current === item || R.equals(ref.current, item);
|
33
33
|
useEffect(() => {
|
34
34
|
if (!areEqual) {
|
35
35
|
ref.current = item;
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import * as R from "remeda";
|
1
2
|
import { CreateValidatorArg, GenericObject, Validator } from "..";
|
2
3
|
import { FORM_ID_FIELD } from "../internal/constants";
|
3
4
|
import { objectFromPathEntries } from "../internal/flatten";
|
@@ -10,6 +11,9 @@ const preprocessFormData = (data: GenericObject | FormData): GenericObject => {
|
|
10
11
|
return objectFromPathEntries(Object.entries(data));
|
11
12
|
};
|
12
13
|
|
14
|
+
const omitInternalFields = (data: GenericObject): GenericObject =>
|
15
|
+
R.omit(data, [FORM_ID_FIELD]);
|
16
|
+
|
13
17
|
/**
|
14
18
|
* Used to create a validator for a form.
|
15
19
|
* It provides built-in handling for unflattening nested objects and
|
@@ -21,7 +25,7 @@ export function createValidator<T>(
|
|
21
25
|
return {
|
22
26
|
validate: async (value) => {
|
23
27
|
const data = preprocessFormData(value);
|
24
|
-
const result = await validator.validate(data);
|
28
|
+
const result = await validator.validate(omitInternalFields(data));
|
25
29
|
|
26
30
|
if (result.error) {
|
27
31
|
return {
|
@@ -1,11 +1,13 @@
|
|
1
1
|
import { anyString, TestFormData } from "@remix-validated-form/test-utils";
|
2
2
|
import { withYup } from "@remix-validated-form/with-yup/src";
|
3
3
|
import { withZod } from "@remix-validated-form/with-zod";
|
4
|
+
import * as R from "remeda";
|
4
5
|
import { Validator } from "remix-validated-form/src";
|
5
6
|
import { objectFromPathEntries } from "remix-validated-form/src/internal/flatten";
|
6
7
|
import { describe, it, expect } from "vitest";
|
7
8
|
import * as yup from "yup";
|
8
9
|
import { z } from "zod";
|
10
|
+
import { FORM_ID_FIELD } from "../internal/constants";
|
9
11
|
|
10
12
|
// If adding an adapter, write a validator that validates this shape
|
11
13
|
type Person = {
|
@@ -101,6 +103,30 @@ describe("Validation", () => {
|
|
101
103
|
});
|
102
104
|
});
|
103
105
|
|
106
|
+
it("should omit internal fields", async () => {
|
107
|
+
const person: Person = {
|
108
|
+
firstName: "John",
|
109
|
+
lastName: "Doe",
|
110
|
+
age: 30,
|
111
|
+
address: {
|
112
|
+
streetAddress: "123 Main St",
|
113
|
+
city: "Anytown",
|
114
|
+
country: "USA",
|
115
|
+
},
|
116
|
+
pets: [{ animal: "dog", name: "Fido" }],
|
117
|
+
|
118
|
+
// @ts-expect-error
|
119
|
+
// internal filed technically not part of person type
|
120
|
+
[FORM_ID_FIELD]: "something",
|
121
|
+
};
|
122
|
+
expect(await validator.validate(person)).toEqual({
|
123
|
+
data: R.omit(person as any, [FORM_ID_FIELD]),
|
124
|
+
error: undefined,
|
125
|
+
submittedData: person,
|
126
|
+
formId: "something",
|
127
|
+
});
|
128
|
+
});
|
129
|
+
|
104
130
|
it("should return field errors when invalid", async () => {
|
105
131
|
const obj = { age: "hi!", pets: [{ animal: "dog" }] };
|
106
132
|
expect(await validator.validate(obj)).toEqual({
|