remix-validated-form 3.3.1 → 4.0.0-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 +2 -2
- package/README.md +3 -1
- package/browser/ValidatedForm.d.ts +1 -1
- package/browser/ValidatedForm.js +89 -41
- package/browser/components.d.ts +10 -0
- package/browser/components.js +10 -0
- package/browser/hooks.d.ts +13 -1
- package/browser/hooks.js +32 -7
- package/browser/internal/flatten.d.ts +0 -3
- package/browser/internal/flatten.js +0 -33
- package/browser/internal/formContext.d.ts +2 -3
- package/browser/internal/formContext.js +1 -11
- package/browser/internal/getInputProps.d.ts +10 -4
- package/browser/internal/getInputProps.js +23 -3
- package/browser/server.d.ts +13 -4
- package/browser/server.js +18 -13
- package/browser/types.d.ts +1 -0
- package/browser/types.js +1 -0
- package/browser/validation/createValidator.d.ts +2 -2
- package/browser/validation/createValidator.js +15 -8
- package/browser/validation/types.d.ts +35 -12
- package/build/ValidatedForm.d.ts +1 -1
- package/build/ValidatedForm.js +107 -40
- package/build/hooks.d.ts +13 -1
- package/build/hooks.js +33 -7
- package/build/internal/flatten.d.ts +0 -3
- package/build/internal/flatten.js +1 -35
- package/build/internal/formContext.d.ts +2 -3
- package/build/internal/formContext.js +1 -11
- package/build/internal/getInputProps.d.ts +10 -4
- package/build/internal/getInputProps.js +23 -3
- package/build/server.d.ts +13 -4
- package/build/server.js +18 -13
- package/build/types.d.ts +1 -0
- package/build/types.js +2 -0
- package/build/validation/createValidator.d.ts +2 -2
- package/build/validation/createValidator.js +15 -8
- package/build/validation/types.d.ts +35 -12
- package/package.json +1 -1
- package/src/ValidatedForm.tsx +112 -42
- package/src/hooks.ts +43 -7
- package/src/internal/flatten.ts +0 -44
- package/src/internal/formContext.ts +2 -13
- package/src/internal/getInputProps.ts +36 -9
- package/src/server.ts +28 -21
- package/src/types.ts +1 -0
- package/src/validation/createValidator.ts +21 -10
- package/src/validation/types.ts +38 -7
@@ -17,12 +17,20 @@ export type CreateGetInputPropsOptions = {
|
|
17
17
|
name: string;
|
18
18
|
};
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
type OmitHandledProps = { name?: never; defaultValue?: never };
|
20
|
+
type HandledProps = "name" | "defaultValue" | "defaultChecked";
|
21
|
+
type Callbacks = "onChange" | "onBlur";
|
23
22
|
|
24
|
-
|
25
|
-
|
23
|
+
type MinimalInputProps = {
|
24
|
+
onChange?: (...args: any[]) => void;
|
25
|
+
onBlur?: (...args: any[]) => void;
|
26
|
+
defaultValue?: any;
|
27
|
+
defaultChecked?: boolean;
|
28
|
+
name?: string;
|
29
|
+
type?: string;
|
30
|
+
};
|
31
|
+
|
32
|
+
export type GetInputProps = <T extends MinimalInputProps>(
|
33
|
+
props?: Omit<T, HandledProps | Callbacks> & Partial<Pick<T, Callbacks>>
|
26
34
|
) => T;
|
27
35
|
|
28
36
|
const defaultValidationBehavior: ValidationBehaviorOptions = {
|
@@ -31,6 +39,13 @@ const defaultValidationBehavior: ValidationBehaviorOptions = {
|
|
31
39
|
whenSubmitted: "onChange",
|
32
40
|
};
|
33
41
|
|
42
|
+
const getCheckboxDefaultChecked = (value: string, defaultValue: any) => {
|
43
|
+
if (Array.isArray(defaultValue)) return defaultValue.includes(value);
|
44
|
+
if (typeof defaultValue === "boolean") return defaultValue;
|
45
|
+
if (typeof defaultValue === "string") return defaultValue === value;
|
46
|
+
return undefined;
|
47
|
+
};
|
48
|
+
|
34
49
|
export const createGetInputProps = ({
|
35
50
|
clearError,
|
36
51
|
validate,
|
@@ -46,14 +61,14 @@ export const createGetInputProps = ({
|
|
46
61
|
...validationBehavior,
|
47
62
|
};
|
48
63
|
|
49
|
-
return <T extends
|
64
|
+
return <T extends MinimalInputProps>(props = {} as any) => {
|
50
65
|
const behavior = hasBeenSubmitted
|
51
66
|
? validationBehaviors.whenSubmitted
|
52
67
|
: touched
|
53
68
|
? validationBehaviors.whenTouched
|
54
69
|
: validationBehaviors.initial;
|
55
70
|
|
56
|
-
const
|
71
|
+
const inputProps: T = {
|
57
72
|
...props,
|
58
73
|
onChange: (...args: unknown[]) => {
|
59
74
|
if (behavior === "onChange") validate();
|
@@ -65,10 +80,22 @@ export const createGetInputProps = ({
|
|
65
80
|
setTouched(true);
|
66
81
|
return props?.onBlur?.(...args);
|
67
82
|
},
|
68
|
-
defaultValue,
|
69
83
|
name,
|
70
84
|
};
|
71
85
|
|
72
|
-
|
86
|
+
if (inputProps.type === "checkbox") {
|
87
|
+
const value = props.value ?? "on";
|
88
|
+
inputProps.defaultChecked = getCheckboxDefaultChecked(
|
89
|
+
value,
|
90
|
+
defaultValue
|
91
|
+
);
|
92
|
+
} else if (inputProps.type === "radio") {
|
93
|
+
const value = props.value ?? "on";
|
94
|
+
inputProps.defaultChecked = defaultValue === value;
|
95
|
+
} else {
|
96
|
+
inputProps.defaultValue = defaultValue;
|
97
|
+
}
|
98
|
+
|
99
|
+
return inputProps;
|
73
100
|
};
|
74
101
|
};
|
package/src/server.ts
CHANGED
@@ -1,26 +1,33 @@
|
|
1
1
|
import { json } from "@remix-run/server-runtime";
|
2
|
-
import {
|
2
|
+
import {
|
3
|
+
ValidatorError,
|
4
|
+
ValidationErrorResponseData,
|
5
|
+
} from "./validation/types";
|
3
6
|
|
4
7
|
/**
|
5
8
|
* Takes the errors from a `Validator` and returns a `Response`.
|
6
|
-
*
|
7
|
-
*
|
9
|
+
* When you return this from your action, `ValidatedForm` on the frontend will automatically
|
10
|
+
* display the errors on the correct fields on the correct form.
|
11
|
+
*
|
12
|
+
* _Recommended_: You can also provide a second argument to `validationError`
|
13
|
+
* to specify how to repopulate the form when JS is disabled.
|
14
|
+
*
|
15
|
+
* @example
|
16
|
+
* ```ts
|
17
|
+
* const result = validator.validate(await request.formData());
|
18
|
+
* if (result.error) return validationError(result.error, result.submittedData);
|
19
|
+
* ```
|
8
20
|
*/
|
9
|
-
export
|
10
|
-
|
11
|
-
|
12
|
-
)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
);
|
23
|
-
}
|
24
|
-
|
25
|
-
return json({ fieldErrors: errors }, { status: 422 });
|
26
|
-
};
|
21
|
+
export function validationError(
|
22
|
+
error: ValidatorError,
|
23
|
+
repopulateFields?: unknown
|
24
|
+
): Response {
|
25
|
+
return json<ValidationErrorResponseData>(
|
26
|
+
{
|
27
|
+
fieldErrors: error.fieldErrors,
|
28
|
+
subaction: error.subaction,
|
29
|
+
repopulateFields,
|
30
|
+
},
|
31
|
+
{ status: 422 }
|
32
|
+
);
|
33
|
+
}
|
package/src/types.ts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export type ValidationState = "idle" | "validating" | "valid" | "invalid";
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { GenericObject, Validator } from "..";
|
1
|
+
import { CreateValidatorArg, GenericObject, Validator } from "..";
|
2
2
|
import { objectFromPathEntries } from "../internal/flatten";
|
3
3
|
|
4
4
|
const preprocessFormData = (data: GenericObject | FormData): GenericObject => {
|
@@ -14,19 +14,30 @@ const preprocessFormData = (data: GenericObject | FormData): GenericObject => {
|
|
14
14
|
* It provides built-in handling for unflattening nested objects and
|
15
15
|
* extracting the values from FormData.
|
16
16
|
*/
|
17
|
-
export function createValidator<T>(
|
17
|
+
export function createValidator<T>(
|
18
|
+
validator: CreateValidatorArg<T>
|
19
|
+
): Validator<T> {
|
18
20
|
return {
|
19
|
-
validate: (value
|
21
|
+
validate: async (value) => {
|
20
22
|
const data = preprocessFormData(value);
|
21
|
-
const result = validator.validate(data);
|
23
|
+
const result = await validator.validate(data);
|
24
|
+
|
22
25
|
if (result.error) {
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
return {
|
27
|
+
data: undefined,
|
28
|
+
error: {
|
29
|
+
fieldErrors: result.error,
|
30
|
+
subaction: data.subaction,
|
31
|
+
},
|
32
|
+
submittedData: data,
|
33
|
+
};
|
28
34
|
}
|
29
|
-
|
35
|
+
|
36
|
+
return {
|
37
|
+
data: result.data,
|
38
|
+
error: undefined,
|
39
|
+
submittedData: data,
|
40
|
+
};
|
30
41
|
},
|
31
42
|
validateField: (data: GenericObject | FormData, field: string) =>
|
32
43
|
validator.validateField(preprocessFormData(data), field),
|
package/src/validation/types.ts
CHANGED
@@ -2,16 +2,33 @@ export type FieldErrors = Record<string, string>;
|
|
2
2
|
|
3
3
|
export type TouchedFields = Record<string, boolean>;
|
4
4
|
|
5
|
-
export type FieldErrorsWithData = FieldErrors & { _submittedData: any };
|
6
|
-
|
7
5
|
export type GenericObject = { [key: string]: any };
|
8
6
|
|
7
|
+
export type ValidatorError = {
|
8
|
+
subaction?: string;
|
9
|
+
fieldErrors: FieldErrors;
|
10
|
+
};
|
11
|
+
|
12
|
+
export type ValidationErrorResponseData = {
|
13
|
+
subaction?: string;
|
14
|
+
fieldErrors: FieldErrors;
|
15
|
+
repopulateFields?: unknown;
|
16
|
+
};
|
17
|
+
|
18
|
+
export type BaseResult = { submittedData: GenericObject };
|
19
|
+
export type ErrorResult = BaseResult & {
|
20
|
+
error: ValidatorError;
|
21
|
+
data: undefined;
|
22
|
+
};
|
23
|
+
export type SuccessResult<DataType> = BaseResult & {
|
24
|
+
data: DataType;
|
25
|
+
error: undefined;
|
26
|
+
};
|
27
|
+
|
9
28
|
/**
|
10
29
|
* The result when validating a form.
|
11
30
|
*/
|
12
|
-
export type ValidationResult<DataType> =
|
13
|
-
| { data: DataType; error: undefined }
|
14
|
-
| { error: FieldErrors; data: undefined };
|
31
|
+
export type ValidationResult<DataType> = SuccessResult<DataType> | ErrorResult;
|
15
32
|
|
16
33
|
/**
|
17
34
|
* The result when validating an individual field in a form.
|
@@ -22,11 +39,25 @@ export type ValidateFieldResult = { error?: string };
|
|
22
39
|
* A `Validator` can be passed to the `validator` prop of a `ValidatedForm`.
|
23
40
|
*/
|
24
41
|
export type Validator<DataType> = {
|
25
|
-
validate: (
|
42
|
+
validate: (
|
43
|
+
unvalidatedData: GenericObject
|
44
|
+
) => Promise<ValidationResult<DataType>>;
|
45
|
+
validateField: (
|
46
|
+
unvalidatedData: GenericObject,
|
47
|
+
field: string
|
48
|
+
) => Promise<ValidateFieldResult>;
|
49
|
+
};
|
50
|
+
|
51
|
+
export type Valid<DataType> = { data: DataType; error: undefined };
|
52
|
+
export type Invalid = { error: FieldErrors; data: undefined };
|
53
|
+
export type CreateValidatorArg<DataType> = {
|
54
|
+
validate: (
|
55
|
+
unvalidatedData: GenericObject
|
56
|
+
) => Promise<Valid<DataType> | Invalid>;
|
26
57
|
validateField: (
|
27
58
|
unvalidatedData: GenericObject,
|
28
59
|
field: string
|
29
|
-
) => ValidateFieldResult
|
60
|
+
) => Promise<ValidateFieldResult>;
|
30
61
|
};
|
31
62
|
|
32
63
|
export type ValidatorData<T extends Validator<any>> = T extends Validator<
|