remix-validated-form 3.4.1 → 4.0.1-beta.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.
Files changed (40) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/README.md +1 -1
  3. package/browser/ValidatedForm.d.ts +2 -2
  4. package/browser/ValidatedForm.js +99 -43
  5. package/browser/components.d.ts +10 -0
  6. package/browser/components.js +10 -0
  7. package/browser/hooks.d.ts +4 -1
  8. package/browser/hooks.js +16 -5
  9. package/browser/internal/formContext.d.ts +2 -3
  10. package/browser/internal/formContext.js +1 -11
  11. package/browser/internal/getInputProps.js +1 -1
  12. package/browser/server.d.ts +13 -4
  13. package/browser/server.js +18 -13
  14. package/browser/types.d.ts +1 -0
  15. package/browser/types.js +1 -0
  16. package/browser/validation/createValidator.d.ts +2 -2
  17. package/browser/validation/createValidator.js +15 -8
  18. package/browser/validation/types.d.ts +35 -12
  19. package/build/ValidatedForm.d.ts +2 -2
  20. package/build/ValidatedForm.js +117 -42
  21. package/build/hooks.d.ts +4 -1
  22. package/build/hooks.js +18 -6
  23. package/build/internal/formContext.d.ts +2 -3
  24. package/build/internal/formContext.js +1 -11
  25. package/build/internal/getInputProps.js +1 -1
  26. package/build/server.d.ts +13 -4
  27. package/build/server.js +18 -13
  28. package/build/types.d.ts +1 -0
  29. package/build/types.js +2 -0
  30. package/build/validation/createValidator.d.ts +2 -2
  31. package/build/validation/createValidator.js +15 -8
  32. package/build/validation/types.d.ts +35 -12
  33. package/package.json +1 -1
  34. package/src/ValidatedForm.tsx +126 -43
  35. package/src/hooks.ts +21 -5
  36. package/src/internal/formContext.ts +2 -13
  37. package/src/internal/getInputProps.ts +1 -1
  38. package/src/server.ts +28 -21
  39. package/src/validation/createValidator.ts +21 -10
  40. package/src/validation/types.ts +38 -7
@@ -1,4 +1,23 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
2
21
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
23
  };
@@ -6,26 +25,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
25
  exports.ValidatedForm = void 0;
7
26
  const jsx_runtime_1 = require("react/jsx-runtime");
8
27
  const react_1 = require("@remix-run/react");
9
- const react_2 = require("react");
28
+ const uniq_1 = __importDefault(require("lodash/uniq"));
29
+ const react_2 = __importStar(require("react"));
10
30
  const tiny_invariant_1 = __importDefault(require("tiny-invariant"));
11
31
  const formContext_1 = require("./internal/formContext");
12
32
  const MultiValueMap_1 = require("./internal/MultiValueMap");
13
33
  const submissionCallbacks_1 = require("./internal/submissionCallbacks");
14
34
  const util_1 = require("./internal/util");
15
- function useFieldErrorsFromBackend(fetcher, subaction) {
16
- var _a, _b;
35
+ function useErrorResponseForThisForm(fetcher, subaction) {
36
+ var _a;
17
37
  const actionData = (0, react_1.useActionData)();
18
- if (fetcher)
19
- return (_a = fetcher.data) === null || _a === void 0 ? void 0 : _a.fieldErrors;
20
- if (!actionData)
38
+ if (fetcher) {
39
+ if ((_a = fetcher.data) === null || _a === void 0 ? void 0 : _a.fieldErrors)
40
+ return fetcher.data;
21
41
  return null;
22
- if (actionData.fieldErrors) {
23
- const submittedData = (_b = actionData.fieldErrors) === null || _b === void 0 ? void 0 : _b._submittedData;
24
- const subactionsMatch = subaction
25
- ? subaction === (submittedData === null || submittedData === void 0 ? void 0 : submittedData.subaction)
26
- : !(submittedData === null || submittedData === void 0 ? void 0 : submittedData.subaction);
27
- return subactionsMatch ? actionData.fieldErrors : null;
28
42
  }
43
+ if (!(actionData === null || actionData === void 0 ? void 0 : actionData.fieldErrors))
44
+ return null;
45
+ if ((!subaction && !actionData.subaction) ||
46
+ actionData.subaction === subaction)
47
+ return actionData;
29
48
  return null;
30
49
  }
31
50
  function useFieldErrors(fieldErrorsFromBackend) {
@@ -67,47 +86,70 @@ const getDataFromForm = (el) => new FormData(el);
67
86
  * It will only ever be a problem if the form includes a `<button type="reset" />`
68
87
  * and only if JS is disabled.
69
88
  */
70
- function useDefaultValues(fieldErrors, defaultValues) {
71
- const defaultsFromValidationError = fieldErrors === null || fieldErrors === void 0 ? void 0 : fieldErrors._submittedData;
72
- return defaultsFromValidationError !== null && defaultsFromValidationError !== void 0 ? defaultsFromValidationError : defaultValues;
89
+ function useDefaultValues(repopulateFieldsFromBackend, defaultValues) {
90
+ return repopulateFieldsFromBackend !== null && repopulateFieldsFromBackend !== void 0 ? repopulateFieldsFromBackend : defaultValues;
91
+ }
92
+ function nonNull(value) {
93
+ return value !== null;
73
94
  }
74
95
  const focusFirstInvalidInput = (fieldErrors, customFocusHandlers, formElement) => {
75
- const invalidInputSelector = Object.keys(fieldErrors)
76
- .map((fieldName) => `input[name="${fieldName}"]`)
77
- .join(",");
78
- const invalidInputs = formElement.querySelectorAll(invalidInputSelector);
79
- for (const element of invalidInputs) {
80
- const input = element;
81
- if (customFocusHandlers.has(input.name)) {
82
- customFocusHandlers.getAll(input.name).forEach((handler) => {
96
+ var _a;
97
+ const namesInOrder = [...formElement.elements]
98
+ .map((el) => {
99
+ const input = el instanceof RadioNodeList ? el[0] : el;
100
+ if (input instanceof HTMLInputElement)
101
+ return input.name;
102
+ return null;
103
+ })
104
+ .filter(nonNull)
105
+ .filter((name) => name in fieldErrors);
106
+ const uniqueNamesInOrder = (0, uniq_1.default)(namesInOrder);
107
+ for (const fieldName of uniqueNamesInOrder) {
108
+ if (customFocusHandlers.has(fieldName)) {
109
+ customFocusHandlers.getAll(fieldName).forEach((handler) => {
83
110
  handler();
84
111
  });
85
112
  break;
86
113
  }
87
- // We don't filter these out ahead of time because
88
- // they could have a custom focus handler
89
- if (input.type === "hidden") {
114
+ const elem = formElement.elements.namedItem(fieldName);
115
+ if (!elem)
90
116
  continue;
117
+ if (elem instanceof RadioNodeList) {
118
+ const selectedRadio = (_a = [...elem]
119
+ .filter((item) => item instanceof HTMLInputElement)
120
+ .find((item) => item.value === elem.value)) !== null && _a !== void 0 ? _a : elem[0];
121
+ if (selectedRadio && selectedRadio instanceof HTMLInputElement) {
122
+ selectedRadio.focus();
123
+ break;
124
+ }
125
+ }
126
+ if (elem instanceof HTMLInputElement) {
127
+ if (elem.type === "hidden") {
128
+ continue;
129
+ }
130
+ elem.focus();
131
+ break;
91
132
  }
92
- input.focus();
93
- break;
94
133
  }
95
134
  };
96
135
  /**
97
136
  * The primary form component of `remix-validated-form`.
98
137
  */
99
- function ValidatedForm({ validator, onSubmit, children, fetcher, action, defaultValues, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, ...rest }) {
138
+ function ValidatedForm({ validator, onSubmit, children, fetcher, action, defaultValues, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, method, replace, ...rest }) {
100
139
  var _a;
101
- const fieldErrorsFromBackend = useFieldErrorsFromBackend(fetcher, subaction);
102
- const [fieldErrors, setFieldErrors] = useFieldErrors(fieldErrorsFromBackend);
140
+ const backendError = useErrorResponseForThisForm(fetcher, subaction);
141
+ const [fieldErrors, setFieldErrors] = useFieldErrors(backendError === null || backendError === void 0 ? void 0 : backendError.fieldErrors);
103
142
  const isSubmitting = useIsSubmitting(action, subaction, fetcher);
104
- const defaultsToUse = useDefaultValues(fieldErrorsFromBackend, defaultValues);
143
+ const [isValidating, setIsValidating] = (0, react_2.useState)(false);
144
+ const defaultsToUse = useDefaultValues(backendError === null || backendError === void 0 ? void 0 : backendError.repopulateFields, defaultValues);
105
145
  const [touchedFields, setTouchedFields] = (0, react_2.useState)({});
106
146
  const [hasBeenSubmitted, setHasBeenSubmitted] = (0, react_2.useState)(false);
147
+ const submit = (0, react_1.useSubmit)();
107
148
  const formRef = (0, react_2.useRef)(null);
108
149
  (0, submissionCallbacks_1.useSubmitComplete)(isSubmitting, () => {
109
150
  var _a;
110
- if (!fieldErrorsFromBackend && resetAfterSubmit) {
151
+ setIsValidating(false);
152
+ if (!backendError && resetAfterSubmit) {
111
153
  (_a = formRef.current) === null || _a === void 0 ? void 0 : _a.reset();
112
154
  }
113
155
  });
@@ -116,7 +158,7 @@ function ValidatedForm({ validator, onSubmit, children, fetcher, action, default
116
158
  fieldErrors,
117
159
  action,
118
160
  defaultValues: defaultsToUse,
119
- isSubmitting: isSubmitting !== null && isSubmitting !== void 0 ? isSubmitting : false,
161
+ isSubmitting: isValidating || isSubmitting,
120
162
  isValid: Object.keys(fieldErrors).length === 0,
121
163
  touchedFields,
122
164
  setFieldTouched: (fieldName, touched) => setTouchedFields((prev) => ({
@@ -126,9 +168,9 @@ function ValidatedForm({ validator, onSubmit, children, fetcher, action, default
126
168
  clearError: (fieldName) => {
127
169
  setFieldErrors((prev) => (0, util_1.omit)(prev, fieldName));
128
170
  },
129
- validateField: (fieldName) => {
171
+ validateField: async (fieldName) => {
130
172
  (0, tiny_invariant_1.default)(formRef.current, "Cannot find reference to form");
131
- const { error } = validator.validateField(getDataFromForm(formRef.current), fieldName);
173
+ const { error } = await validator.validateField(getDataFromForm(formRef.current), fieldName);
132
174
  // By checking and returning `prev` here, we can avoid a re-render
133
175
  // if the validation state is the same.
134
176
  if (error) {
@@ -140,6 +182,7 @@ function ValidatedForm({ validator, onSubmit, children, fetcher, action, default
140
182
  [fieldName]: error,
141
183
  };
142
184
  });
185
+ return error;
143
186
  }
144
187
  else {
145
188
  setFieldErrors((prev) => {
@@ -147,6 +190,7 @@ function ValidatedForm({ validator, onSubmit, children, fetcher, action, default
147
190
  return prev;
148
191
  return (0, util_1.omit)(prev, fieldName);
149
192
  });
193
+ return null;
150
194
  }
151
195
  },
152
196
  registerReceiveFocus: (fieldName, handler) => {
@@ -160,6 +204,7 @@ function ValidatedForm({ validator, onSubmit, children, fetcher, action, default
160
204
  fieldErrors,
161
205
  action,
162
206
  defaultsToUse,
207
+ isValidating,
163
208
  isSubmitting,
164
209
  touchedFields,
165
210
  hasBeenSubmitted,
@@ -168,18 +213,48 @@ function ValidatedForm({ validator, onSubmit, children, fetcher, action, default
168
213
  customFocusHandlers,
169
214
  ]);
170
215
  const Form = (_a = fetcher === null || fetcher === void 0 ? void 0 : fetcher.Form) !== null && _a !== void 0 ? _a : react_1.Form;
171
- return ((0, jsx_runtime_1.jsx)(Form, { ref: (0, util_1.mergeRefs)([formRef, formRefProp]), ...rest, action: action, onSubmit: (event) => {
216
+ let clickedButtonRef = react_2.default.useRef();
217
+ (0, react_2.useEffect)(() => {
218
+ let form = formRef.current;
219
+ if (!form)
220
+ return;
221
+ function handleClick(event) {
222
+ if (!(event.target instanceof HTMLElement))
223
+ return;
224
+ let submitButton = event.target.closest("button,input[type=submit]");
225
+ if (submitButton &&
226
+ submitButton.form === form &&
227
+ submitButton.type === "submit") {
228
+ clickedButtonRef.current = submitButton;
229
+ }
230
+ }
231
+ window.addEventListener("click", handleClick);
232
+ return () => {
233
+ window.removeEventListener("click", handleClick);
234
+ };
235
+ }, []);
236
+ return ((0, jsx_runtime_1.jsx)(Form, { ref: (0, util_1.mergeRefs)([formRef, formRefProp]), ...rest, action: action, method: method, replace: replace, onSubmit: async (e) => {
237
+ e.preventDefault();
172
238
  setHasBeenSubmitted(true);
173
- const result = validator.validate(getDataFromForm(event.currentTarget));
239
+ setIsValidating(true);
240
+ const result = await validator.validate(getDataFromForm(e.currentTarget));
174
241
  if (result.error) {
175
- event.preventDefault();
176
- setFieldErrors(result.error);
242
+ setIsValidating(false);
243
+ setFieldErrors(result.error.fieldErrors);
177
244
  if (!disableFocusOnError) {
178
- focusFirstInvalidInput(result.error, customFocusHandlers(), formRef.current);
245
+ focusFirstInvalidInput(result.error.fieldErrors, customFocusHandlers(), formRef.current);
179
246
  }
180
247
  }
181
248
  else {
182
- onSubmit === null || onSubmit === void 0 ? void 0 : onSubmit(result.data, event);
249
+ onSubmit && onSubmit(result.data, e);
250
+ if (fetcher)
251
+ fetcher.submit(clickedButtonRef.current || e.currentTarget);
252
+ else
253
+ submit(clickedButtonRef.current || e.currentTarget, {
254
+ method,
255
+ replace,
256
+ });
257
+ clickedButtonRef.current = null;
183
258
  }
184
259
  }, onReset: (event) => {
185
260
  onReset === null || onReset === void 0 ? void 0 : onReset(event);
package/build/hooks.d.ts CHANGED
@@ -46,7 +46,6 @@ export declare const useField: (name: string, options?: {
46
46
  } | undefined) => FieldProps;
47
47
  /**
48
48
  * Provides access to the entire form context.
49
- * This is not usually necessary, but can be useful for advanced use cases.
50
49
  */
51
50
  export declare const useFormContext: () => import("./internal/formContext").FormContextValue;
52
51
  /**
@@ -55,3 +54,7 @@ export declare const useFormContext: () => import("./internal/formContext").Form
55
54
  * is aware of what form it's in and when _that_ form is being submitted.
56
55
  */
57
56
  export declare const useIsSubmitting: () => boolean;
57
+ /**
58
+ * Returns whether or not the current form is valid.
59
+ */
60
+ export declare const useIsValid: () => boolean;
package/build/hooks.js CHANGED
@@ -3,17 +3,23 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.useIsSubmitting = exports.useFormContext = exports.useField = void 0;
6
+ exports.useIsValid = exports.useIsSubmitting = exports.useFormContext = exports.useField = void 0;
7
7
  const get_1 = __importDefault(require("lodash/get"));
8
8
  const toPath_1 = __importDefault(require("lodash/toPath"));
9
9
  const react_1 = require("react");
10
10
  const formContext_1 = require("./internal/formContext");
11
11
  const getInputProps_1 = require("./internal/getInputProps");
12
+ const useInternalFormContext = (hookName) => {
13
+ const context = (0, react_1.useContext)(formContext_1.FormContext);
14
+ if (!context)
15
+ throw new Error(`${hookName} must be used within a ValidatedForm component`);
16
+ return context;
17
+ };
12
18
  /**
13
19
  * Provides the data and helpers necessary to set up a field.
14
20
  */
15
21
  const useField = (name, options) => {
16
- const { fieldErrors, clearError, validateField, defaultValues, registerReceiveFocus, touchedFields, setFieldTouched, hasBeenSubmitted, } = (0, react_1.useContext)(formContext_1.FormContext);
22
+ const { fieldErrors, clearError, validateField, defaultValues, registerReceiveFocus, touchedFields, setFieldTouched, hasBeenSubmitted, } = useInternalFormContext("useField");
17
23
  const isTouched = !!touchedFields[name];
18
24
  const { handleReceiveFocus } = options !== null && options !== void 0 ? options : {};
19
25
  (0, react_1.useEffect)(() => {
@@ -26,7 +32,9 @@ const useField = (name, options) => {
26
32
  clearError: () => {
27
33
  clearError(name);
28
34
  },
29
- validate: () => validateField(name),
35
+ validate: () => {
36
+ validateField(name);
37
+ },
30
38
  defaultValue: defaultValues
31
39
  ? (0, get_1.default)(defaultValues, (0, toPath_1.default)(name), undefined)
32
40
  : undefined,
@@ -59,14 +67,18 @@ const useField = (name, options) => {
59
67
  exports.useField = useField;
60
68
  /**
61
69
  * Provides access to the entire form context.
62
- * This is not usually necessary, but can be useful for advanced use cases.
63
70
  */
64
- const useFormContext = () => (0, react_1.useContext)(formContext_1.FormContext);
71
+ const useFormContext = () => useInternalFormContext("useFormContext");
65
72
  exports.useFormContext = useFormContext;
66
73
  /**
67
74
  * Returns whether or not the parent form is currently being submitted.
68
75
  * This is different from remix's `useTransition().submission` in that it
69
76
  * is aware of what form it's in and when _that_ form is being submitted.
70
77
  */
71
- const useIsSubmitting = () => (0, exports.useFormContext)().isSubmitting;
78
+ const useIsSubmitting = () => useInternalFormContext("useIsSubmitting").isSubmitting;
72
79
  exports.useIsSubmitting = useIsSubmitting;
80
+ /**
81
+ * Returns whether or not the current form is valid.
82
+ */
83
+ const useIsValid = () => useInternalFormContext("useIsValid").isValid;
84
+ exports.useIsValid = useIsValid;
@@ -12,7 +12,7 @@ export declare type FormContextValue = {
12
12
  /**
13
13
  * Validate the specified field.
14
14
  */
15
- validateField: (fieldName: string) => void;
15
+ validateField: (fieldName: string) => Promise<string | null>;
16
16
  /**
17
17
  * The `action` prop of the form.
18
18
  */
@@ -29,7 +29,6 @@ export declare type FormContextValue = {
29
29
  hasBeenSubmitted: boolean;
30
30
  /**
31
31
  * Whether or not the form is valid.
32
- * This is a shortcut for `Object.keys(fieldErrors).length === 0`.
33
32
  */
34
33
  isValid: boolean;
35
34
  /**
@@ -52,4 +51,4 @@ export declare type FormContextValue = {
52
51
  */
53
52
  setFieldTouched: (fieldName: string, touched: boolean) => void;
54
53
  };
55
- export declare const FormContext: import("react").Context<FormContextValue>;
54
+ export declare const FormContext: import("react").Context<FormContextValue | null>;
@@ -2,14 +2,4 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FormContext = void 0;
4
4
  const react_1 = require("react");
5
- exports.FormContext = (0, react_1.createContext)({
6
- fieldErrors: {},
7
- clearError: () => { },
8
- validateField: () => { },
9
- isSubmitting: false,
10
- hasBeenSubmitted: false,
11
- isValid: true,
12
- registerReceiveFocus: () => () => { },
13
- touchedFields: {},
14
- setFieldTouched: () => { },
15
- });
5
+ exports.FormContext = (0, react_1.createContext)(null);
@@ -8,7 +8,7 @@ const defaultValidationBehavior = {
8
8
  };
9
9
  const getCheckboxDefaultChecked = (value, defaultValue) => {
10
10
  if (Array.isArray(defaultValue))
11
- defaultValue.includes(value);
11
+ return defaultValue.includes(value);
12
12
  if (typeof defaultValue === "boolean")
13
13
  return defaultValue;
14
14
  if (typeof defaultValue === "string")
package/build/server.d.ts CHANGED
@@ -1,7 +1,16 @@
1
- import { FieldErrors } from "./validation/types";
1
+ import { ValidatorError } from "./validation/types";
2
2
  /**
3
3
  * Takes the errors from a `Validator` and returns a `Response`.
4
- * The `ValidatedForm` on the frontend will automatically display the errors
5
- * if this is returned from the action.
4
+ * When you return this from your action, `ValidatedForm` on the frontend will automatically
5
+ * display the errors on the correct fields on the correct form.
6
+ *
7
+ * You can also provide a second argument to `validationError`
8
+ * to specify how to repopulate the form when JS is disabled.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const result = validator.validate(await request.formData());
13
+ * if (result.error) return validationError(result.error, result.submittedData);
14
+ * ```
6
15
  */
7
- export declare const validationError: (errors: FieldErrors, submittedData?: unknown) => Response;
16
+ export declare function validationError(error: ValidatorError, repopulateFields?: unknown): Response;
package/build/server.js CHANGED
@@ -4,18 +4,23 @@ exports.validationError = void 0;
4
4
  const server_runtime_1 = require("@remix-run/server-runtime");
5
5
  /**
6
6
  * Takes the errors from a `Validator` and returns a `Response`.
7
- * The `ValidatedForm` on the frontend will automatically display the errors
8
- * if this is returned from the action.
7
+ * When you return this from your action, `ValidatedForm` on the frontend will automatically
8
+ * display the errors on the correct fields on the correct form.
9
+ *
10
+ * You can also provide a second argument to `validationError`
11
+ * to specify how to repopulate the form when JS is disabled.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const result = validator.validate(await request.formData());
16
+ * if (result.error) return validationError(result.error, result.submittedData);
17
+ * ```
9
18
  */
10
- const validationError = (errors, submittedData) => {
11
- if (submittedData) {
12
- return (0, server_runtime_1.json)({
13
- fieldErrors: {
14
- ...errors,
15
- _submittedData: submittedData,
16
- },
17
- }, { status: 422 });
18
- }
19
- return (0, server_runtime_1.json)({ fieldErrors: errors }, { status: 422 });
20
- };
19
+ function validationError(error, repopulateFields) {
20
+ return (0, server_runtime_1.json)({
21
+ fieldErrors: error.fieldErrors,
22
+ subaction: error.subaction,
23
+ repopulateFields,
24
+ }, { status: 422 });
25
+ }
21
26
  exports.validationError = validationError;
@@ -0,0 +1 @@
1
+ export declare type ValidationState = "idle" | "validating" | "valid" | "invalid";
package/build/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,7 +1,7 @@
1
- import { Validator } from "..";
1
+ import { CreateValidatorArg, Validator } from "..";
2
2
  /**
3
3
  * Used to create a validator for a form.
4
4
  * It provides built-in handling for unflattening nested objects and
5
5
  * extracting the values from FormData.
6
6
  */
7
- export declare function createValidator<T>(validator: Validator<T>): Validator<T>;
7
+ export declare function createValidator<T>(validator: CreateValidatorArg<T>): Validator<T>;
@@ -16,17 +16,24 @@ const preprocessFormData = (data) => {
16
16
  */
17
17
  function createValidator(validator) {
18
18
  return {
19
- validate: (value) => {
19
+ validate: async (value) => {
20
20
  const data = preprocessFormData(value);
21
- const result = validator.validate(data);
21
+ const result = await validator.validate(data);
22
22
  if (result.error) {
23
- // Ideally, we should probably be returning a nested object like
24
- // { fieldErrors: {}, submittedData: {} }
25
- // We should do this in the next major version of the library
26
- // but for now, we can sneak it in with the fieldErrors.
27
- result.error._submittedData = data;
23
+ return {
24
+ data: undefined,
25
+ error: {
26
+ fieldErrors: result.error,
27
+ subaction: data.subaction,
28
+ },
29
+ submittedData: data,
30
+ };
28
31
  }
29
- return result;
32
+ return {
33
+ data: result.data,
34
+ error: undefined,
35
+ submittedData: data,
36
+ };
30
37
  },
31
38
  validateField: (data, field) => validator.validateField(preprocessFormData(data), field),
32
39
  };
@@ -1,21 +1,32 @@
1
1
  export declare type FieldErrors = Record<string, string>;
2
2
  export declare type TouchedFields = Record<string, boolean>;
3
- export declare type FieldErrorsWithData = FieldErrors & {
4
- _submittedData: any;
5
- };
6
3
  export declare type GenericObject = {
7
4
  [key: string]: any;
8
5
  };
9
- /**
10
- * The result when validating a form.
11
- */
12
- export declare type ValidationResult<DataType> = {
6
+ export declare type ValidatorError = {
7
+ subaction?: string;
8
+ fieldErrors: FieldErrors;
9
+ };
10
+ export declare type ValidationErrorResponseData = {
11
+ subaction?: string;
12
+ fieldErrors: FieldErrors;
13
+ repopulateFields?: unknown;
14
+ };
15
+ export declare type BaseResult = {
16
+ submittedData: GenericObject;
17
+ };
18
+ export declare type ErrorResult = BaseResult & {
19
+ error: ValidatorError;
20
+ data: undefined;
21
+ };
22
+ export declare type SuccessResult<DataType> = BaseResult & {
13
23
  data: DataType;
14
24
  error: undefined;
15
- } | {
16
- error: FieldErrors;
17
- data: undefined;
18
25
  };
26
+ /**
27
+ * The result when validating a form.
28
+ */
29
+ export declare type ValidationResult<DataType> = SuccessResult<DataType> | ErrorResult;
19
30
  /**
20
31
  * The result when validating an individual field in a form.
21
32
  */
@@ -26,7 +37,19 @@ export declare type ValidateFieldResult = {
26
37
  * A `Validator` can be passed to the `validator` prop of a `ValidatedForm`.
27
38
  */
28
39
  export declare type Validator<DataType> = {
29
- validate: (unvalidatedData: GenericObject) => ValidationResult<DataType>;
30
- validateField: (unvalidatedData: GenericObject, field: string) => ValidateFieldResult;
40
+ validate: (unvalidatedData: GenericObject) => Promise<ValidationResult<DataType>>;
41
+ validateField: (unvalidatedData: GenericObject, field: string) => Promise<ValidateFieldResult>;
42
+ };
43
+ export declare type Valid<DataType> = {
44
+ data: DataType;
45
+ error: undefined;
46
+ };
47
+ export declare type Invalid = {
48
+ error: FieldErrors;
49
+ data: undefined;
50
+ };
51
+ export declare type CreateValidatorArg<DataType> = {
52
+ validate: (unvalidatedData: GenericObject) => Promise<Valid<DataType> | Invalid>;
53
+ validateField: (unvalidatedData: GenericObject, field: string) => Promise<ValidateFieldResult>;
31
54
  };
32
55
  export declare type ValidatorData<T extends Validator<any>> = T extends Validator<infer U> ? U : never;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remix-validated-form",
3
- "version": "3.4.1",
3
+ "version": "4.0.1-beta.1",
4
4
  "description": "Form component and utils for easy form validation in remix",
5
5
  "browser": "./browser/index.js",
6
6
  "main": "./build/index.js",