remix-validated-form 5.0.2 → 5.1.1-beta.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. package/.turbo/turbo-build.log +152 -8
  2. package/dist/index.cjs.js +898 -63
  3. package/dist/index.cjs.js.map +1 -1
  4. package/dist/index.d.ts +7 -2
  5. package/dist/index.esm.js +876 -15
  6. package/dist/index.esm.js.map +1 -1
  7. package/package.json +4 -4
  8. package/src/ValidatedForm.tsx +0 -427
  9. package/src/hooks.ts +0 -160
  10. package/src/index.ts +0 -12
  11. package/src/internal/MultiValueMap.ts +0 -44
  12. package/src/internal/constants.ts +0 -4
  13. package/src/internal/flatten.ts +0 -12
  14. package/src/internal/formContext.ts +0 -13
  15. package/src/internal/getInputProps.test.ts +0 -251
  16. package/src/internal/getInputProps.ts +0 -94
  17. package/src/internal/hooks.ts +0 -217
  18. package/src/internal/hydratable.ts +0 -28
  19. package/src/internal/logic/getCheckboxChecked.ts +0 -10
  20. package/src/internal/logic/getRadioChecked.ts +0 -18
  21. package/src/internal/logic/nestedObjectToPathObject.ts +0 -63
  22. package/src/internal/logic/requestSubmit.test.tsx +0 -24
  23. package/src/internal/logic/requestSubmit.ts +0 -103
  24. package/src/internal/state/arrayUtil.ts +0 -451
  25. package/src/internal/state/controlledFields.ts +0 -86
  26. package/src/internal/state/createFormStore.ts +0 -591
  27. package/src/internal/state/fieldArray.tsx +0 -197
  28. package/src/internal/state/storeHooks.ts +0 -9
  29. package/src/internal/state/types.ts +0 -1
  30. package/src/internal/submissionCallbacks.ts +0 -15
  31. package/src/internal/util.ts +0 -39
  32. package/src/server.ts +0 -53
  33. package/src/unreleased/formStateHooks.ts +0 -170
  34. package/src/userFacingFormContext.ts +0 -147
  35. package/src/validation/createValidator.ts +0 -53
  36. package/src/validation/types.ts +0 -72
  37. package/tsconfig.json +0 -8
@@ -1,251 +0,0 @@
1
- import { describe, it, expect, vi } from "vitest";
2
- import {
3
- createGetInputProps,
4
- CreateGetInputPropsOptions,
5
- } from "./getInputProps";
6
-
7
- const fakeEvent = { fake: "event" } as any;
8
-
9
- describe("getInputProps", () => {
10
- describe("initial", () => {
11
- it("should validate on blur by default", () => {
12
- const options: CreateGetInputPropsOptions = {
13
- name: "some-field",
14
- defaultValue: "test default value",
15
- touched: false,
16
- hasBeenSubmitted: false,
17
- setTouched: vi.fn(),
18
- clearError: vi.fn(),
19
- validate: vi.fn(),
20
- };
21
- const getInputProps = createGetInputProps(options);
22
-
23
- const provided = {
24
- onBlur: vi.fn(),
25
- onChange: vi.fn(),
26
- };
27
- const { onChange, onBlur } = getInputProps(provided);
28
-
29
- onChange!(fakeEvent);
30
- expect(provided.onChange).toBeCalledTimes(1);
31
- expect(provided.onChange).toBeCalledWith(fakeEvent);
32
- expect(options.setTouched).not.toBeCalled();
33
- expect(options.validate).not.toBeCalled();
34
-
35
- onBlur!(fakeEvent);
36
- expect(provided.onBlur).toBeCalledTimes(1);
37
- expect(provided.onBlur).toBeCalledWith(fakeEvent);
38
- expect(options.setTouched).toBeCalledTimes(1);
39
- expect(options.setTouched).toBeCalledWith(true);
40
- expect(options.validate).toBeCalledTimes(1);
41
- });
42
-
43
- it("should respect provided validation behavior", () => {
44
- const options: CreateGetInputPropsOptions = {
45
- name: "some-field",
46
- defaultValue: "test default value",
47
- touched: false,
48
- hasBeenSubmitted: false,
49
- setTouched: vi.fn(),
50
- clearError: vi.fn(),
51
- validate: vi.fn(),
52
- validationBehavior: {
53
- initial: "onChange",
54
- },
55
- };
56
- const getInputProps = createGetInputProps(options);
57
-
58
- const provided = {
59
- onBlur: vi.fn(),
60
- onChange: vi.fn(),
61
- };
62
- const { onChange, onBlur } = getInputProps(provided);
63
-
64
- onChange!(fakeEvent);
65
- expect(provided.onChange).toBeCalledTimes(1);
66
- expect(provided.onChange).toBeCalledWith(fakeEvent);
67
- expect(options.setTouched).not.toBeCalled();
68
- expect(options.validate).toBeCalledTimes(1);
69
-
70
- onBlur!(fakeEvent);
71
- expect(provided.onBlur).toBeCalledTimes(1);
72
- expect(provided.onBlur).toBeCalledWith(fakeEvent);
73
- expect(options.setTouched).toBeCalledTimes(1);
74
- expect(options.setTouched).toBeCalledWith(true);
75
- expect(options.validate).toBeCalledTimes(1);
76
- });
77
-
78
- it("should not validate when behavior is onSubmit", () => {
79
- const options: CreateGetInputPropsOptions = {
80
- name: "some-field",
81
- defaultValue: "test default value",
82
- touched: false,
83
- hasBeenSubmitted: false,
84
- setTouched: vi.fn(),
85
- clearError: vi.fn(),
86
- validate: vi.fn(),
87
- validationBehavior: {
88
- initial: "onSubmit",
89
- },
90
- };
91
- const getInputProps = createGetInputProps(options);
92
-
93
- const provided = {
94
- onBlur: vi.fn(),
95
- onChange: vi.fn(),
96
- };
97
- const { onChange, onBlur } = getInputProps(provided);
98
-
99
- onChange!(fakeEvent);
100
- expect(provided.onChange).toBeCalledTimes(1);
101
- expect(provided.onChange).toBeCalledWith(fakeEvent);
102
- expect(options.setTouched).not.toBeCalled();
103
- expect(options.validate).not.toBeCalled();
104
-
105
- onBlur!(fakeEvent);
106
- expect(provided.onBlur).toBeCalledTimes(1);
107
- expect(provided.onBlur).toBeCalledWith(fakeEvent);
108
- expect(options.setTouched).toBeCalledTimes(1);
109
- expect(options.setTouched).toBeCalledWith(true);
110
- expect(options.validate).not.toBeCalled();
111
- });
112
- });
113
-
114
- describe("whenTouched", () => {
115
- it("should validate on change by default", () => {
116
- const options: CreateGetInputPropsOptions = {
117
- name: "some-field",
118
- defaultValue: "test default value",
119
- touched: true,
120
- hasBeenSubmitted: false,
121
- setTouched: vi.fn(),
122
- clearError: vi.fn(),
123
- validate: vi.fn(),
124
- };
125
- const getInputProps = createGetInputProps(options);
126
-
127
- const provided = {
128
- onBlur: vi.fn(),
129
- onChange: vi.fn(),
130
- };
131
- const { onChange, onBlur } = getInputProps(provided);
132
-
133
- onChange!(fakeEvent);
134
- expect(provided.onChange).toBeCalledTimes(1);
135
- expect(provided.onChange).toBeCalledWith(fakeEvent);
136
- expect(options.setTouched).not.toBeCalled();
137
- expect(options.validate).toBeCalledTimes(1);
138
-
139
- onBlur!(fakeEvent);
140
- expect(provided.onBlur).toBeCalledTimes(1);
141
- expect(provided.onBlur).toBeCalledWith(fakeEvent);
142
- expect(options.setTouched).toBeCalledTimes(1);
143
- expect(options.setTouched).toBeCalledWith(true);
144
- expect(options.validate).toBeCalledTimes(1);
145
- });
146
-
147
- it("should respect provided validation behavior", () => {
148
- const options: CreateGetInputPropsOptions = {
149
- name: "some-field",
150
- defaultValue: "test default value",
151
- touched: true,
152
- hasBeenSubmitted: false,
153
- setTouched: vi.fn(),
154
- clearError: vi.fn(),
155
- validate: vi.fn(),
156
- validationBehavior: {
157
- whenTouched: "onBlur",
158
- },
159
- };
160
- const getInputProps = createGetInputProps(options);
161
-
162
- const provided = {
163
- onBlur: vi.fn(),
164
- onChange: vi.fn(),
165
- };
166
- const { onChange, onBlur } = getInputProps(provided);
167
-
168
- onChange!(fakeEvent);
169
- expect(provided.onChange).toBeCalledTimes(1);
170
- expect(provided.onChange).toBeCalledWith(fakeEvent);
171
- expect(options.setTouched).not.toBeCalled();
172
- expect(options.validate).not.toBeCalled();
173
-
174
- onBlur!(fakeEvent);
175
- expect(provided.onBlur).toBeCalledTimes(1);
176
- expect(provided.onBlur).toBeCalledWith(fakeEvent);
177
- expect(options.setTouched).toBeCalledTimes(1);
178
- expect(options.setTouched).toBeCalledWith(true);
179
- expect(options.validate).toBeCalledTimes(1);
180
- });
181
- });
182
-
183
- describe("whenSubmitted", () => {
184
- it("should validate on change by default", () => {
185
- const options: CreateGetInputPropsOptions = {
186
- name: "some-field",
187
- defaultValue: "test default value",
188
- touched: true,
189
- hasBeenSubmitted: true,
190
- setTouched: vi.fn(),
191
- clearError: vi.fn(),
192
- validate: vi.fn(),
193
- };
194
- const getInputProps = createGetInputProps(options);
195
-
196
- const provided = {
197
- onBlur: vi.fn(),
198
- onChange: vi.fn(),
199
- };
200
- const { onChange, onBlur } = getInputProps(provided);
201
-
202
- onChange!(fakeEvent);
203
- expect(provided.onChange).toBeCalledTimes(1);
204
- expect(provided.onChange).toBeCalledWith(fakeEvent);
205
- expect(options.setTouched).not.toBeCalled();
206
- expect(options.validate).toBeCalledTimes(1);
207
-
208
- onBlur!(fakeEvent);
209
- expect(provided.onBlur).toBeCalledTimes(1);
210
- expect(provided.onBlur).toBeCalledWith(fakeEvent);
211
- expect(options.setTouched).toBeCalledTimes(1);
212
- expect(options.setTouched).toBeCalledWith(true);
213
- expect(options.validate).toBeCalledTimes(1);
214
- });
215
-
216
- it("should respect provided validation behavior", () => {
217
- const options: CreateGetInputPropsOptions = {
218
- name: "some-field",
219
- defaultValue: "test default value",
220
- touched: true,
221
- hasBeenSubmitted: true,
222
- setTouched: vi.fn(),
223
- clearError: vi.fn(),
224
- validate: vi.fn(),
225
- validationBehavior: {
226
- whenSubmitted: "onBlur",
227
- },
228
- };
229
- const getInputProps = createGetInputProps(options);
230
-
231
- const provided = {
232
- onBlur: vi.fn(),
233
- onChange: vi.fn(),
234
- };
235
- const { onChange, onBlur } = getInputProps(provided);
236
-
237
- onChange!(fakeEvent);
238
- expect(provided.onChange).toBeCalledTimes(1);
239
- expect(provided.onChange).toBeCalledWith(fakeEvent);
240
- expect(options.setTouched).not.toBeCalled();
241
- expect(options.validate).not.toBeCalled();
242
-
243
- onBlur!(fakeEvent);
244
- expect(provided.onBlur).toBeCalledTimes(1);
245
- expect(provided.onBlur).toBeCalledWith(fakeEvent);
246
- expect(options.setTouched).toBeCalledTimes(1);
247
- expect(options.setTouched).toBeCalledWith(true);
248
- expect(options.validate).toBeCalledTimes(1);
249
- });
250
- });
251
- });
@@ -1,94 +0,0 @@
1
- import * as R from "remeda";
2
- import { getCheckboxChecked } from "./logic/getCheckboxChecked";
3
- import { getRadioChecked } from "./logic/getRadioChecked";
4
-
5
- export type ValidationBehavior = "onBlur" | "onChange" | "onSubmit";
6
-
7
- export type ValidationBehaviorOptions = {
8
- initial: ValidationBehavior;
9
- whenTouched: ValidationBehavior;
10
- whenSubmitted: ValidationBehavior;
11
- };
12
-
13
- export type CreateGetInputPropsOptions = {
14
- clearError: () => void;
15
- validate: () => void;
16
- defaultValue?: any;
17
- touched: boolean;
18
- setTouched: (touched: boolean) => void;
19
- hasBeenSubmitted: boolean;
20
- validationBehavior?: Partial<ValidationBehaviorOptions>;
21
- name: string;
22
- };
23
-
24
- type HandledProps = "name" | "defaultValue" | "defaultChecked";
25
- type Callbacks = "onChange" | "onBlur";
26
-
27
- type MinimalInputProps = {
28
- onChange?: ((...args: any[]) => void) | undefined;
29
- onBlur?: ((...args: any[]) => void) | undefined;
30
- defaultValue?: any;
31
- defaultChecked?: boolean | undefined;
32
- name?: string | undefined;
33
- type?: string | undefined;
34
- };
35
-
36
- export type GetInputProps = <T extends MinimalInputProps>(
37
- props?: Omit<T, HandledProps | Callbacks> & Partial<Pick<T, Callbacks>>
38
- ) => T;
39
-
40
- const defaultValidationBehavior: ValidationBehaviorOptions = {
41
- initial: "onBlur",
42
- whenTouched: "onChange",
43
- whenSubmitted: "onChange",
44
- };
45
-
46
- export const createGetInputProps = ({
47
- clearError,
48
- validate,
49
- defaultValue,
50
- touched,
51
- setTouched,
52
- hasBeenSubmitted,
53
- validationBehavior,
54
- name,
55
- }: CreateGetInputPropsOptions): GetInputProps => {
56
- const validationBehaviors = {
57
- ...defaultValidationBehavior,
58
- ...validationBehavior,
59
- };
60
-
61
- return <T extends MinimalInputProps>(props = {} as any) => {
62
- const behavior = hasBeenSubmitted
63
- ? validationBehaviors.whenSubmitted
64
- : touched
65
- ? validationBehaviors.whenTouched
66
- : validationBehaviors.initial;
67
-
68
- const inputProps: MinimalInputProps = {
69
- ...props,
70
- onChange: (...args: unknown[]) => {
71
- if (behavior === "onChange") validate();
72
- else clearError();
73
- return props?.onChange?.(...args);
74
- },
75
- onBlur: (...args: unknown[]) => {
76
- if (behavior === "onBlur") validate();
77
- setTouched(true);
78
- return props?.onBlur?.(...args);
79
- },
80
- name,
81
- };
82
-
83
- if (props.type === "checkbox") {
84
- inputProps.defaultChecked = getCheckboxChecked(props.value, defaultValue);
85
- } else if (props.type === "radio") {
86
- inputProps.defaultChecked = getRadioChecked(props.value, defaultValue);
87
- } else if (props.value === undefined) {
88
- // We should only set the defaultValue if the input is uncontrolled.
89
- inputProps.defaultValue = defaultValue;
90
- }
91
-
92
- return R.omitBy(inputProps, (value) => value === undefined) as T;
93
- };
94
- };
@@ -1,217 +0,0 @@
1
- import { useActionData, useMatches, useNavigation } from "@remix-run/react";
2
- import { useCallback, useContext } from "react";
3
- import { getPath } from "set-get";
4
- import invariant from "tiny-invariant";
5
- import { FieldErrors, ValidationErrorResponseData } from "..";
6
- import { formDefaultValuesKey } from "./constants";
7
- import { InternalFormContext, InternalFormContextValue } from "./formContext";
8
- import { Hydratable, hydratable } from "./hydratable";
9
- import { useFormStore } from "./state/storeHooks";
10
- import { InternalFormId } from "./state/types";
11
-
12
- export const useInternalFormContext = (
13
- formId?: string | symbol,
14
- hookName?: string
15
- ) => {
16
- const formContext = useContext(InternalFormContext);
17
-
18
- if (formId) return { formId };
19
- if (formContext) return formContext;
20
-
21
- throw new Error(
22
- `Unable to determine form for ${hookName}. Please use it inside a ValidatedForm or pass a 'formId'.`
23
- );
24
- };
25
-
26
- export function useErrorResponseForForm({
27
- fetcher,
28
- subaction,
29
- formId,
30
- }: InternalFormContextValue): ValidationErrorResponseData | null {
31
- const actionData = useActionData<any>();
32
- if (fetcher) {
33
- if ((fetcher.data as any)?.fieldErrors) return fetcher.data as any;
34
- return null;
35
- }
36
-
37
- if (!actionData?.fieldErrors) return null;
38
-
39
- // If there's an explicit id, we should ignore data that has the wrong id
40
- if (typeof formId === "string" && actionData.formId)
41
- return actionData.formId === formId ? actionData : null;
42
-
43
- if (
44
- (!subaction && !actionData.subaction) ||
45
- actionData.subaction === subaction
46
- )
47
- return actionData;
48
-
49
- return null;
50
- }
51
-
52
- export const useFieldErrorsForForm = (
53
- context: InternalFormContextValue
54
- ): Hydratable<FieldErrors | undefined> => {
55
- const response = useErrorResponseForForm(context);
56
- const hydrated = useFormStore(context.formId, (state) => state.isHydrated);
57
- return hydratable.from(response?.fieldErrors, hydrated);
58
- };
59
-
60
- export const useDefaultValuesFromLoader = ({
61
- formId,
62
- }: InternalFormContextValue) => {
63
- const matches = useMatches();
64
- if (typeof formId === "string") {
65
- const dataKey = formDefaultValuesKey(formId);
66
- // If multiple loaders declare the same default values,
67
- // we should use the data from the deepest route.
68
- const match = matches
69
- .reverse()
70
- .find(
71
- (match) =>
72
- match.data && typeof match.data === "object" && dataKey in match.data
73
- );
74
- return match?.data[dataKey];
75
- }
76
-
77
- return null;
78
- };
79
-
80
- export const useDefaultValuesForForm = (
81
- context: InternalFormContextValue
82
- ): Hydratable<{ [fieldName: string]: any }> => {
83
- const { formId, defaultValuesProp } = context;
84
- const hydrated = useFormStore(formId, (state) => state.isHydrated);
85
- const errorResponse = useErrorResponseForForm(context);
86
- const defaultValuesFromLoader = useDefaultValuesFromLoader(context);
87
-
88
- // Typical flow is:
89
- // - Default values only available from props or server
90
- // - Props have a higher priority than server
91
- // - State gets hydrated with default values
92
- // - After submit, we may need to use values from the error
93
-
94
- if (hydrated) return hydratable.hydratedData();
95
- if (errorResponse?.repopulateFields) {
96
- invariant(
97
- typeof errorResponse.repopulateFields === "object",
98
- "repopulateFields returned something other than an object"
99
- );
100
- return hydratable.serverData(errorResponse.repopulateFields);
101
- }
102
- if (defaultValuesProp) return hydratable.serverData(defaultValuesProp);
103
-
104
- return hydratable.serverData(defaultValuesFromLoader);
105
- };
106
-
107
- export const useHasActiveFormSubmit = ({
108
- fetcher,
109
- }: InternalFormContextValue): boolean => {
110
- let navigation = useNavigation();
111
- const hasActiveSubmission = fetcher
112
- ? fetcher.state === "submitting"
113
- : navigation.state === "submitting" || navigation.state === "loading";
114
- return hasActiveSubmission;
115
- };
116
-
117
- export const useFieldTouched = (
118
- field: string,
119
- { formId }: InternalFormContextValue
120
- ) => {
121
- const touched = useFormStore(formId, (state) => state.touchedFields[field]);
122
- const setFieldTouched = useFormStore(formId, (state) => state.setTouched);
123
- const setTouched = useCallback(
124
- (touched: boolean) => setFieldTouched(field, touched),
125
- [field, setFieldTouched]
126
- );
127
- return [touched, setTouched] as const;
128
- };
129
-
130
- export const useFieldError = (
131
- name: string,
132
- context: InternalFormContextValue
133
- ) => {
134
- const fieldErrors = useFieldErrorsForForm(context);
135
- const state = useFormStore(
136
- context.formId,
137
- (state) => state.fieldErrors[name]
138
- );
139
- return fieldErrors.map((fieldErrors) => fieldErrors?.[name]).hydrateTo(state);
140
- };
141
-
142
- export const useClearError = (context: InternalFormContextValue) => {
143
- const { formId } = context;
144
- return useFormStore(formId, (state) => state.clearFieldError);
145
- };
146
-
147
- export const useCurrentDefaultValueForField = (
148
- formId: InternalFormId,
149
- field: string
150
- ) =>
151
- useFormStore(formId, (state) => getPath(state.currentDefaultValues, field));
152
-
153
- export const useFieldDefaultValue = (
154
- name: string,
155
- context: InternalFormContextValue
156
- ) => {
157
- const defaultValues = useDefaultValuesForForm(context);
158
- const state = useCurrentDefaultValueForField(context.formId, name);
159
-
160
- return defaultValues.map((val) => getPath(val, name)).hydrateTo(state);
161
- };
162
-
163
- export const useInternalIsSubmitting = (formId: InternalFormId) =>
164
- useFormStore(formId, (state) => state.isSubmitting);
165
-
166
- export const useInternalIsValid = (formId: InternalFormId) =>
167
- useFormStore(formId, (state) => state.isValid());
168
-
169
- export const useInternalHasBeenSubmitted = (formId: InternalFormId) =>
170
- useFormStore(formId, (state) => state.hasBeenSubmitted);
171
-
172
- export const useSmartValidate = (formId: InternalFormId) =>
173
- useFormStore(formId, (state) => state.smartValidate);
174
-
175
- export const useValidate = (formId: InternalFormId) =>
176
- useFormStore(formId, (state) => state.validate);
177
-
178
- const noOpReceiver = () => () => {};
179
- export const useRegisterReceiveFocus = (formId: InternalFormId) =>
180
- useFormStore(
181
- formId,
182
- (state) => state.formProps?.registerReceiveFocus ?? noOpReceiver
183
- );
184
-
185
- const defaultDefaultValues = {};
186
- export const useSyncedDefaultValues = (formId: InternalFormId) =>
187
- useFormStore(
188
- formId,
189
- (state) => state.formProps?.defaultValues ?? defaultDefaultValues
190
- );
191
-
192
- export const useSetTouched = ({ formId }: InternalFormContextValue) =>
193
- useFormStore(formId, (state) => state.setTouched);
194
-
195
- export const useTouchedFields = (formId: InternalFormId) =>
196
- useFormStore(formId, (state) => state.touchedFields);
197
-
198
- export const useFieldErrors = (formId: InternalFormId) =>
199
- useFormStore(formId, (state) => state.fieldErrors);
200
-
201
- export const useSetFieldErrors = (formId: InternalFormId) =>
202
- useFormStore(formId, (state) => state.setFieldErrors);
203
-
204
- export const useResetFormElement = (formId: InternalFormId) =>
205
- useFormStore(formId, (state) => state.resetFormElement);
206
-
207
- export const useSubmitForm = (formId: InternalFormId) =>
208
- useFormStore(formId, (state) => state.submit);
209
-
210
- export const useFormActionProp = (formId: InternalFormId) =>
211
- useFormStore(formId, (state) => state.formProps?.action);
212
-
213
- export const useFormSubactionProp = (formId: InternalFormId) =>
214
- useFormStore(formId, (state) => state.formProps?.subaction);
215
-
216
- export const useFormValues = (formId: InternalFormId) =>
217
- useFormStore(formId, (state) => state.getValues);
@@ -1,28 +0,0 @@
1
- /**
2
- * The purpose of this type is to simplify the logic
3
- * around data that needs to come from the server initially,
4
- * but from the internal state after hydration.
5
- */
6
- export type Hydratable<T> = {
7
- hydrateTo: (data: T) => T;
8
- map: <U>(fn: (data: T) => U) => Hydratable<U>;
9
- };
10
-
11
- const serverData = <T>(data: T): Hydratable<T> => ({
12
- hydrateTo: () => data,
13
- map: (fn) => serverData(fn(data)),
14
- });
15
-
16
- const hydratedData = <T>(): Hydratable<T> => ({
17
- hydrateTo: (hydratedData: T) => hydratedData,
18
- map: <U>() => hydratedData<U>(),
19
- });
20
-
21
- const from = <T>(data: T, hydrated: boolean): Hydratable<T> =>
22
- hydrated ? hydratedData<T>() : serverData<T>(data);
23
-
24
- export const hydratable = {
25
- serverData,
26
- hydratedData,
27
- from,
28
- };
@@ -1,10 +0,0 @@
1
- export const getCheckboxChecked = (
2
- checkboxValue: string | undefined = "on",
3
- newValue: unknown
4
- ): boolean | undefined => {
5
- if (Array.isArray(newValue))
6
- return newValue.some((val) => val === true || val === checkboxValue);
7
- if (typeof newValue === "boolean") return newValue;
8
- if (typeof newValue === "string") return newValue === checkboxValue;
9
- return undefined;
10
- };
@@ -1,18 +0,0 @@
1
- export const getRadioChecked = (
2
- radioValue: string | undefined = "on",
3
- newValue: unknown
4
- ) => {
5
- if (typeof newValue === "string") return newValue === radioValue;
6
- return undefined;
7
- };
8
-
9
- if (import.meta.vitest) {
10
- const { it, expect } = import.meta.vitest;
11
- it("getRadioChecked", () => {
12
- expect(getRadioChecked("on", "on")).toBe(true);
13
- expect(getRadioChecked("on", undefined)).toBe(undefined);
14
- expect(getRadioChecked("trueValue", undefined)).toBe(undefined);
15
- expect(getRadioChecked("trueValue", "bob")).toBe(false);
16
- expect(getRadioChecked("trueValue", "trueValue")).toBe(true);
17
- });
18
- }
@@ -1,63 +0,0 @@
1
- export const nestedObjectToPathObject = (
2
- val: any,
3
- acc: Record<string, any>,
4
- path: string
5
- ): any => {
6
- if (Array.isArray(val)) {
7
- val.forEach((v, index) =>
8
- nestedObjectToPathObject(v, acc, `${path}[${index}]`)
9
- );
10
- return acc;
11
- }
12
-
13
- if (typeof val === "object") {
14
- Object.entries(val).forEach(([key, value]) => {
15
- const nextPath = path ? `${path}.${key}` : key;
16
- nestedObjectToPathObject(value, acc, nextPath);
17
- });
18
- return acc;
19
- }
20
-
21
- if (val !== undefined) {
22
- acc[path] = val;
23
- }
24
-
25
- return acc;
26
- };
27
-
28
- if (import.meta.vitest) {
29
- const { describe, expect, it } = import.meta.vitest;
30
-
31
- describe("nestedObjectToPathObject", () => {
32
- it("should return an object with the correct path", () => {
33
- const result = nestedObjectToPathObject(
34
- {
35
- a: 1,
36
- b: 2,
37
- c: { foo: "bar", baz: [true, false] },
38
- d: [
39
- { foo: "bar", baz: [true, false] },
40
- { e: true, f: "hi" },
41
- ],
42
- g: undefined,
43
- },
44
- {},
45
- ""
46
- );
47
-
48
- expect(result).toEqual({
49
- a: 1,
50
- b: 2,
51
- "c.foo": "bar",
52
- "c.baz[0]": true,
53
- "c.baz[1]": false,
54
- "d[0].foo": "bar",
55
- "d[0].baz[0]": true,
56
- "d[0].baz[1]": false,
57
- "d[1].e": true,
58
- "d[1].f": "hi",
59
- });
60
- expect(Object.keys(result)).toHaveLength(10);
61
- });
62
- });
63
- }