remix-validated-form 4.5.0 → 4.5.3

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 (38) hide show
  1. package/.turbo/turbo-build.log +8 -9
  2. package/browser/ValidatedForm.js +4 -4
  3. package/browser/internal/hooks.d.ts +1 -0
  4. package/browser/internal/hooks.js +1 -0
  5. package/browser/internal/logic/nestedObjectToPathObject.d.ts +0 -0
  6. package/browser/internal/logic/nestedObjectToPathObject.js +0 -0
  7. package/browser/internal/state/arrayUtil.d.ts +6 -0
  8. package/browser/internal/state/arrayUtil.js +236 -7
  9. package/browser/internal/state/createFormStore.d.ts +1 -0
  10. package/browser/internal/state/createFormStore.js +6 -0
  11. package/browser/internal/state/fieldArray.d.ts +18 -11
  12. package/browser/internal/state/fieldArray.js +44 -21
  13. package/browser/server.d.ts +2 -2
  14. package/browser/server.js +1 -1
  15. package/browser/unreleased/formStateHooks.d.ts +4 -0
  16. package/browser/unreleased/formStateHooks.js +4 -1
  17. package/browser/userFacingFormContext.d.ts +4 -0
  18. package/browser/userFacingFormContext.js +3 -1
  19. package/dist/remix-validated-form.cjs.js +12 -3
  20. package/dist/remix-validated-form.cjs.js.map +1 -1
  21. package/dist/remix-validated-form.es.js +45 -11
  22. package/dist/remix-validated-form.es.js.map +1 -1
  23. package/dist/remix-validated-form.umd.js +12 -3
  24. package/dist/remix-validated-form.umd.js.map +1 -1
  25. package/dist/types/internal/hooks.d.ts +1 -0
  26. package/dist/types/internal/state/createFormStore.d.ts +1 -0
  27. package/dist/types/server.d.ts +2 -2
  28. package/dist/types/unreleased/formStateHooks.d.ts +4 -0
  29. package/dist/types/userFacingFormContext.d.ts +4 -0
  30. package/package.json +2 -4
  31. package/src/ValidatedForm.tsx +5 -4
  32. package/src/internal/hooks.ts +3 -0
  33. package/src/internal/state/createFormStore.ts +12 -1
  34. package/src/server.ts +2 -2
  35. package/src/unreleased/formStateHooks.ts +8 -0
  36. package/src/userFacingFormContext.ts +7 -0
  37. package/src/validation/validation.test.ts +7 -7
  38. package/vite.config.ts +1 -1
@@ -31,3 +31,4 @@ export declare const useResetFormElement: (formId: InternalFormId) => () => void
31
31
  export declare const useSubmitForm: (formId: InternalFormId) => () => void;
32
32
  export declare const useFormActionProp: (formId: InternalFormId) => string | undefined;
33
33
  export declare const useFormSubactionProp: (formId: InternalFormId) => string | undefined;
34
+ export declare const useFormValues: (formId: InternalFormId) => () => FormData;
@@ -42,6 +42,7 @@ export declare type FormState = {
42
42
  validate: () => Promise<ValidationResult<unknown>>;
43
43
  resetFormElement: () => void;
44
44
  submit: () => void;
45
+ getValues: () => FormData;
45
46
  };
46
47
  export declare const useRootFormStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<FormStoreState>, "setState"> & {
47
48
  setState(nextStateOrUpdater: FormStoreState | Partial<FormStoreState> | ((state: WritableDraft<FormStoreState>) => void), shouldReplace?: boolean | undefined): void;
@@ -1,5 +1,5 @@
1
1
  import { FORM_DEFAULTS_FIELD } from "./internal/constants";
2
- import { ValidatorError } from "./validation/types";
2
+ import { ValidatorError, ValidationErrorResponseData } from "./validation/types";
3
3
  /**
4
4
  * Takes the errors from a `Validator` and returns a `Response`.
5
5
  * When you return this from your action, `ValidatedForm` on the frontend will automatically
@@ -14,7 +14,7 @@ import { ValidatorError } from "./validation/types";
14
14
  * if (result.error) return validationError(result.error, result.submittedData);
15
15
  * ```
16
16
  */
17
- export declare function validationError(error: ValidatorError, repopulateFields?: unknown, init?: ResponseInit): Response;
17
+ export declare function validationError(error: ValidatorError, repopulateFields?: unknown, init?: ResponseInit): import("@remix-run/server-runtime").TypedResponse<ValidationErrorResponseData>;
18
18
  export declare type FormDefaults = {
19
19
  [formDefaultsKey: `${typeof FORM_DEFAULTS_FIELD}_${string}`]: any;
20
20
  };
@@ -51,6 +51,10 @@ export declare type FormHelpers = {
51
51
  * _Note_: This is equivalent to clicking a button element with `type="submit"` or calling formElement.submit().
52
52
  */
53
53
  submit: () => void;
54
+ /**
55
+ * Returns the current form values as FormData
56
+ */
57
+ getValues: () => FormData;
54
58
  };
55
59
  /**
56
60
  * Returns helpers that can be used to update the form state.
@@ -74,6 +74,10 @@ export declare type FormContextValue = {
74
74
  * _Note_: This is equivalent to clicking a button element with `type="submit"` or calling formElement.submit().
75
75
  */
76
76
  submit: () => void;
77
+ /**
78
+ * Returns the current form values as FormData
79
+ */
80
+ getValues: () => FormData;
77
81
  };
78
82
  /**
79
83
  * Provides access to some of the internal state of the form.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remix-validated-form",
3
- "version": "4.5.0",
3
+ "version": "4.5.3",
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",
@@ -34,12 +34,10 @@
34
34
  ],
35
35
  "peerDependencies": {
36
36
  "@remix-run/react": "1.x",
37
- "@remix-run/server-runtime": "1.x",
38
37
  "react": "^17.0.2 || ^18.0.0"
39
38
  },
40
39
  "devDependencies": {
41
- "@remix-run/react": "^1.2.1",
42
- "@remix-run/server-runtime": "^1.2.1",
40
+ "@remix-run/react": "^1.6.5",
43
41
  "@types/lodash": "^4.14.178",
44
42
  "@types/react": "^18.0.9",
45
43
  "fetch-blob": "^3.1.3",
@@ -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 HTMLInputElement) return input.name;
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 HTMLInputElement) {
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
 
@@ -329,7 +330,7 @@ export function ValidatedForm<DataType>({
329
330
  // If we use `event.currentTarget` here, it will break because `currentTarget`
330
331
  // will have changed since the start of the submission.
331
332
  if (fetcher) fetcher.submit(submitter || e.currentTarget);
332
- else submit(submitter || target, { method, replace });
333
+ else submit(submitter || target, { replace });
333
334
  }
334
335
  };
335
336
 
@@ -204,3 +204,6 @@ export const useFormActionProp = (formId: InternalFormId) =>
204
204
 
205
205
  export const useFormSubactionProp = (formId: InternalFormId) =>
206
206
  useFormStore(formId, (state) => state.formProps?.subaction);
207
+
208
+ export const useFormValues = (formId: InternalFormId) =>
209
+ useFormStore(formId, (state) => state.getValues);
@@ -51,6 +51,7 @@ export type FormState = {
51
51
  validate: () => Promise<ValidationResult<unknown>>;
52
52
  resetFormElement: () => void;
53
53
  submit: () => void;
54
+ getValues: () => FormData;
54
55
  };
55
56
 
56
57
  const noOp = () => {};
@@ -84,6 +85,7 @@ const defaultFormState: FormState = {
84
85
  },
85
86
 
86
87
  resetFormElement: noOp,
88
+ getValues: () => new FormData(),
87
89
  };
88
90
 
89
91
  const createFormState = (
@@ -125,7 +127,6 @@ const createFormState = (
125
127
  set((state) => {
126
128
  delete state.fieldErrors[fieldName];
127
129
  }),
128
-
129
130
  reset: () =>
130
131
  set((state) => {
131
132
  state.fieldErrors = {};
@@ -208,6 +209,16 @@ const createFormState = (
208
209
  formElement.submit();
209
210
  },
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);
220
+ },
221
+
211
222
  resetFormElement: () => get().formElement?.reset(),
212
223
  });
213
224
 
package/src/server.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { json } from "@remix-run/server-runtime";
1
+ import { json } from "remix";
2
2
  import {
3
3
  formDefaultValuesKey,
4
4
  FORM_DEFAULTS_FIELD,
@@ -26,7 +26,7 @@ export function validationError(
26
26
  error: ValidatorError,
27
27
  repopulateFields?: unknown,
28
28
  init?: ResponseInit
29
- ): Response {
29
+ ) {
30
30
  return json<ValidationErrorResponseData>(
31
31
  {
32
32
  fieldErrors: error.fieldErrors,
@@ -19,6 +19,7 @@ import {
19
19
  useFormActionProp,
20
20
  useFormSubactionProp,
21
21
  useSubmitForm,
22
+ useFormValues,
22
23
  } from "../internal/hooks";
23
24
  import {
24
25
  FieldErrors,
@@ -118,6 +119,10 @@ export type FormHelpers = {
118
119
  * _Note_: This is equivalent to clicking a button element with `type="submit"` or calling formElement.submit().
119
120
  */
120
121
  submit: () => void;
122
+ /**
123
+ * Returns the current form values as FormData
124
+ */
125
+ getValues: () => FormData;
121
126
  };
122
127
 
123
128
  /**
@@ -134,6 +139,7 @@ export const useFormHelpers = (formId?: string): FormHelpers => {
134
139
  const setFieldErrors = useSetFieldErrors(formContext.formId);
135
140
  const reset = useResetFormElement(formContext.formId);
136
141
  const submit = useSubmitForm(formContext.formId);
142
+ const getValues = useFormValues(formContext.formId);
137
143
  return useMemo(
138
144
  () => ({
139
145
  setTouched,
@@ -143,6 +149,7 @@ export const useFormHelpers = (formId?: string): FormHelpers => {
143
149
  clearAllErrors: () => setFieldErrors({}),
144
150
  reset,
145
151
  submit,
152
+ getValues,
146
153
  }),
147
154
  [
148
155
  clearError,
@@ -152,6 +159,7 @@ export const useFormHelpers = (formId?: string): FormHelpers => {
152
159
  submit,
153
160
  validate,
154
161
  validateField,
162
+ getValues,
155
163
  ]
156
164
  );
157
165
  };
@@ -83,6 +83,10 @@ export type FormContextValue = {
83
83
  * _Note_: This is equivalent to clicking a button element with `type="submit"` or calling formElement.submit().
84
84
  */
85
85
  submit: () => void;
86
+ /**
87
+ * Returns the current form values as FormData
88
+ */
89
+ getValues: () => FormData;
86
90
  };
87
91
 
88
92
  /**
@@ -100,6 +104,7 @@ export const useFormContext = (formId?: string): FormContextValue => {
100
104
  validate,
101
105
  reset,
102
106
  submit,
107
+ getValues,
103
108
  } = useFormHelpers(formId);
104
109
 
105
110
  const registerReceiveFocus = useRegisterReceiveFocus(context.formId);
@@ -124,6 +129,7 @@ export const useFormContext = (formId?: string): FormContextValue => {
124
129
  validate,
125
130
  reset,
126
131
  submit,
132
+ getValues,
127
133
  }),
128
134
  [
129
135
  clearAllErrors,
@@ -135,6 +141,7 @@ export const useFormContext = (formId?: string): FormContextValue => {
135
141
  submit,
136
142
  validate,
137
143
  validateField,
144
+ getValues,
138
145
  ]
139
146
  );
140
147
  };
@@ -56,21 +56,21 @@ const validationTestCases: ValidationTestCase[] = [
56
56
  name: "zod",
57
57
  validator: withZod(
58
58
  z.object({
59
- firstName: z.string().nonempty(),
60
- lastName: z.string().nonempty(),
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().nonempty(),
66
- city: z.string().nonempty(),
67
- country: z.string().nonempty(),
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().nonempty(),
73
- name: z.string().nonempty(),
72
+ animal: z.string().min(1),
73
+ name: z.string().min(1),
74
74
  })
75
75
  .array()
76
76
  .optional(),
package/vite.config.ts CHANGED
@@ -2,6 +2,6 @@ import { makeConfig } from "vite-config";
2
2
 
3
3
  export default makeConfig({
4
4
  lib: "remix-validated-form",
5
- external: ["react", "@remix-run/react", "@remix-run/server-runtime"],
5
+ external: ["react", "@remix-run/react"],
6
6
  dir: __dirname,
7
7
  });