@trackunit/react-form-wizard 0.1.338 → 0.1.339

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/index.cjs.js CHANGED
@@ -209,10 +209,11 @@ const cvaFormWizardComponentStepForm = cssClassVarianceUtilities.cvaMerge(["grid
209
209
  * A wrapper component used by the FormWizard component to render each step.
210
210
  * In most cases you should not be using this component directly, but rather use the FormWizard component.
211
211
  */
212
- const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, component, nextStepPath, previousStepPath, lastStepPrimaryActionLabel, fullFormSchema, setFullFormState, fullFormState, stepForms, setStepForms, setStepStates, basePath, containerClassName, onPrimaryAction, }) => {
212
+ const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, component, nextStepPath, previousStepPath, lastStepPrimaryActionLabel, fullFormSchema, setFullFormState, fullFormState, stepForms, setStepForms, setStepStates, basePath, containerClassName, onPrimaryAction, initialFullFormState, }) => {
213
213
  const navigate = reactRouter.useNavigate();
214
214
  const [isSubmitting, setIsSubmitting] = react.useState(false);
215
215
  const [submitHandlerInternal, setSubmitHandlerInternal] = react.useState(null);
216
+ const [beforeSubmitInternal, setBeforeSubmitInternal] = react.useState(null);
216
217
  const form = reactHookForm.useForm({
217
218
  resolver: zod.zodResolver(fullFormSchema.shape[stepKey]),
218
219
  mode: "all",
@@ -239,8 +240,8 @@ const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, componen
239
240
  }, [form.formState.isValid, setStepStates, stepKey]);
240
241
  const isLastStep = nextStepPath === null;
241
242
  const handleCancel = react.useCallback(() => {
242
- return onCancel ? onCancel() : navigate({ to: "../../" });
243
- }, [onCancel, navigate]);
243
+ return onCancel ? onCancel(String(stepKey)) : navigate({ to: "../../" });
244
+ }, [onCancel, navigate, stepKey]);
244
245
  const setSubmitHandler = react.useCallback(callback => {
245
246
  const wrappedCallbackFunction = async (fullFormStateInner) => {
246
247
  setIsSubmitting(true);
@@ -249,8 +250,19 @@ const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, componen
249
250
  };
250
251
  setSubmitHandlerInternal(() => wrappedCallbackFunction);
251
252
  }, [setSubmitHandlerInternal]);
253
+ const beforeStepSubmit = react.useCallback(callback => {
254
+ const wrappedCallbackFunction = async (fullFormStateInner) => {
255
+ await callback(fullFormStateInner);
256
+ };
257
+ setBeforeSubmitInternal(() => wrappedCallbackFunction);
258
+ }, [setBeforeSubmitInternal]);
259
+ const resetFullFormState = react.useCallback(callback => {
260
+ setFullFormState(initialFullFormState);
261
+ callback();
262
+ }, [initialFullFormState, setFullFormState]);
252
263
  const isEmptyStep = sharedUtils.objectKeys(form.getValues()).length === 0;
253
264
  const emptyStepSubmitHandler = react.useCallback(() => {
265
+ beforeSubmitInternal === null || beforeSubmitInternal === void 0 ? void 0 : beforeSubmitInternal(fullFormState);
254
266
  if (isLastStep) {
255
267
  submitHandlerInternal === null || submitHandlerInternal === void 0 ? void 0 : submitHandlerInternal(fullFormState);
256
268
  }
@@ -258,10 +270,11 @@ const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, componen
258
270
  // eslint-disable-next-line local-rules/no-typescript-assertion
259
271
  navigate({ to: basePath, params: { stepPath: nextStepPath } });
260
272
  }
261
- }, [basePath, fullFormState, isLastStep, navigate, nextStepPath, submitHandlerInternal]);
273
+ }, [basePath, beforeSubmitInternal, fullFormState, isLastStep, navigate, nextStepPath, submitHandlerInternal]);
262
274
  const stepSubmitHandler = react.useMemo(() => form.handleSubmit(async (data) => {
263
275
  //? This state is not updated for the submit handler below; therefore, there is duplication.
264
276
  setFullFormState(prev => ({ ...prev, [stepKey]: data }));
277
+ beforeSubmitInternal === null || beforeSubmitInternal === void 0 ? void 0 : beforeSubmitInternal({ ...fullFormState, [stepKey]: data });
265
278
  if (isLastStep) {
266
279
  //? This state is not updated for the submit handler below; therefore, there is duplication.
267
280
  await (submitHandlerInternal === null || submitHandlerInternal === void 0 ? void 0 : submitHandlerInternal({ ...fullFormState, [stepKey]: data }));
@@ -272,6 +285,7 @@ const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, componen
272
285
  }
273
286
  }), [
274
287
  basePath,
288
+ beforeSubmitInternal,
275
289
  form,
276
290
  fullFormState,
277
291
  isLastStep,
@@ -310,6 +324,8 @@ const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, componen
310
324
  form,
311
325
  fullForm,
312
326
  setSubmitHandler: isLastStep ? setSubmitHandler : null,
327
+ beforeStepSubmit: beforeStepSubmit,
328
+ resetFullFormState,
313
329
  }) }) }), jsxRuntime.jsx(FormWizardFooter, { stepForms,
314
330
  onBack: previousStepPath
315
331
  ? // eslint-disable-next-line local-rules/no-typescript-assertion
@@ -338,16 +354,18 @@ const cvaStepContainer = cssClassVarianceUtilities.cvaMerge(["h-full", "grid", "
338
354
  const FormWizard = ({ lastStepPrimaryActionLabel, isEdit, steps, fullFormSchema, onCancel, className, dataTestId, basePath, }) => {
339
355
  const navigate = reactRouter.useNavigate();
340
356
  const { stepPath, subStepPath } = reactRouter.useParams({ strict: false });
357
+ const initialFullFormState = react.useMemo(() =>
358
+ // eslint-disable-next-line local-rules/no-typescript-assertion
359
+ sharedUtils.objectFromEntries(sharedUtils.objectEntries(steps)
360
+ .filter(([_, { defaultValues }]) => defaultValues)
361
+ .map(([key, { defaultValues }]) => [key, defaultValues])), [steps]);
341
362
  const [stepStates, setStepStates] = react.useState({});
342
363
  const [stepForms, setStepForms] = react.useState({});
343
364
  const [fullFormState, setFullFormState] = react.useState(
344
365
  // DeepPartial does not allow empty object and properly working around it would severely complicate the code (DefaultValues uses DeepPartial under the hood)
345
366
  // A possible workaround could be to type it Partial<<DeepPartial<...>>> everywhere
346
367
  // Any weakening of the type in a union would be worse and likely just push the issue forward until it hits react-hook-form
347
- // eslint-disable-next-line local-rules/no-typescript-assertion
348
- sharedUtils.objectFromEntries(sharedUtils.objectEntries(steps)
349
- .filter(([_, { defaultValues }]) => defaultValues)
350
- .map(([key, { defaultValues }]) => [key, defaultValues])));
368
+ initialFullFormState);
351
369
  // If URL does not match a key from the schema, navigate to the first step
352
370
  const firstSchemaKey = String(sharedUtils.objectKeys(fullFormSchema.shape)[0]);
353
371
  const keyToCheck = subStepPath ? `${stepPath}/${subStepPath}` : stepPath;
@@ -466,7 +484,8 @@ const FormWizard = ({ lastStepPrimaryActionLabel, isEdit, steps, fullFormSchema,
466
484
  fullFormSchema,
467
485
  lastStepPrimaryActionLabel,
468
486
  nextStepPath: (_b = (_a = entriesOfRenderableSteps[index + 1]) === null || _a === void 0 ? void 0 : _a[0].toString()) !== null && _b !== void 0 ? _b : null,
469
- previousStepPath: (_d = (_c = entriesOfRenderableSteps[index - 1]) === null || _c === void 0 ? void 0 : _c[0].toString()) !== null && _d !== void 0 ? _d : null }, stepKey.toString())) : null;
487
+ previousStepPath: (_d = (_c = entriesOfRenderableSteps[index - 1]) === null || _c === void 0 ? void 0 : _c[0].toString()) !== null && _d !== void 0 ? _d : null,
488
+ initialFullFormState }, stepKey.toString())) : null;
470
489
  });
471
490
  }, [
472
491
  basePath,
@@ -478,6 +497,7 @@ const FormWizard = ({ lastStepPrimaryActionLabel, isEdit, steps, fullFormSchema,
478
497
  onCancel,
479
498
  stepForms,
480
499
  steps,
500
+ initialFullFormState,
481
501
  ]);
482
502
  return (jsxRuntime.jsx("div", { className: cvaFormWizardContainer({ className }), "data-testid": dataTestId, children: jsxRuntime.jsxs("div", { className: cvaFormWizardLayout(), children: [jsxRuntime.jsx(FormWizardSidebar, { children: sidebarSteps }), jsxRuntime.jsx("div", { className: cvaStepContainer(), children: stepWrapper })] }) }));
483
503
  };
package/index.esm.js CHANGED
@@ -207,10 +207,11 @@ const cvaFormWizardComponentStepForm = cvaMerge(["grid-rows-min-fr-min", "grid",
207
207
  * A wrapper component used by the FormWizard component to render each step.
208
208
  * In most cases you should not be using this component directly, but rather use the FormWizard component.
209
209
  */
210
- const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, component, nextStepPath, previousStepPath, lastStepPrimaryActionLabel, fullFormSchema, setFullFormState, fullFormState, stepForms, setStepForms, setStepStates, basePath, containerClassName, onPrimaryAction, }) => {
210
+ const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, component, nextStepPath, previousStepPath, lastStepPrimaryActionLabel, fullFormSchema, setFullFormState, fullFormState, stepForms, setStepForms, setStepStates, basePath, containerClassName, onPrimaryAction, initialFullFormState, }) => {
211
211
  const navigate = useNavigate();
212
212
  const [isSubmitting, setIsSubmitting] = useState(false);
213
213
  const [submitHandlerInternal, setSubmitHandlerInternal] = useState(null);
214
+ const [beforeSubmitInternal, setBeforeSubmitInternal] = useState(null);
214
215
  const form = useForm({
215
216
  resolver: zodResolver(fullFormSchema.shape[stepKey]),
216
217
  mode: "all",
@@ -237,8 +238,8 @@ const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, componen
237
238
  }, [form.formState.isValid, setStepStates, stepKey]);
238
239
  const isLastStep = nextStepPath === null;
239
240
  const handleCancel = useCallback(() => {
240
- return onCancel ? onCancel() : navigate({ to: "../../" });
241
- }, [onCancel, navigate]);
241
+ return onCancel ? onCancel(String(stepKey)) : navigate({ to: "../../" });
242
+ }, [onCancel, navigate, stepKey]);
242
243
  const setSubmitHandler = useCallback(callback => {
243
244
  const wrappedCallbackFunction = async (fullFormStateInner) => {
244
245
  setIsSubmitting(true);
@@ -247,8 +248,19 @@ const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, componen
247
248
  };
248
249
  setSubmitHandlerInternal(() => wrappedCallbackFunction);
249
250
  }, [setSubmitHandlerInternal]);
251
+ const beforeStepSubmit = useCallback(callback => {
252
+ const wrappedCallbackFunction = async (fullFormStateInner) => {
253
+ await callback(fullFormStateInner);
254
+ };
255
+ setBeforeSubmitInternal(() => wrappedCallbackFunction);
256
+ }, [setBeforeSubmitInternal]);
257
+ const resetFullFormState = useCallback(callback => {
258
+ setFullFormState(initialFullFormState);
259
+ callback();
260
+ }, [initialFullFormState, setFullFormState]);
250
261
  const isEmptyStep = objectKeys(form.getValues()).length === 0;
251
262
  const emptyStepSubmitHandler = useCallback(() => {
263
+ beforeSubmitInternal === null || beforeSubmitInternal === void 0 ? void 0 : beforeSubmitInternal(fullFormState);
252
264
  if (isLastStep) {
253
265
  submitHandlerInternal === null || submitHandlerInternal === void 0 ? void 0 : submitHandlerInternal(fullFormState);
254
266
  }
@@ -256,10 +268,11 @@ const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, componen
256
268
  // eslint-disable-next-line local-rules/no-typescript-assertion
257
269
  navigate({ to: basePath, params: { stepPath: nextStepPath } });
258
270
  }
259
- }, [basePath, fullFormState, isLastStep, navigate, nextStepPath, submitHandlerInternal]);
271
+ }, [basePath, beforeSubmitInternal, fullFormState, isLastStep, navigate, nextStepPath, submitHandlerInternal]);
260
272
  const stepSubmitHandler = useMemo(() => form.handleSubmit(async (data) => {
261
273
  //? This state is not updated for the submit handler below; therefore, there is duplication.
262
274
  setFullFormState(prev => ({ ...prev, [stepKey]: data }));
275
+ beforeSubmitInternal === null || beforeSubmitInternal === void 0 ? void 0 : beforeSubmitInternal({ ...fullFormState, [stepKey]: data });
263
276
  if (isLastStep) {
264
277
  //? This state is not updated for the submit handler below; therefore, there is duplication.
265
278
  await (submitHandlerInternal === null || submitHandlerInternal === void 0 ? void 0 : submitHandlerInternal({ ...fullFormState, [stepKey]: data }));
@@ -270,6 +283,7 @@ const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, componen
270
283
  }
271
284
  }), [
272
285
  basePath,
286
+ beforeSubmitInternal,
273
287
  form,
274
288
  fullFormState,
275
289
  isLastStep,
@@ -308,6 +322,8 @@ const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, componen
308
322
  form,
309
323
  fullForm,
310
324
  setSubmitHandler: isLastStep ? setSubmitHandler : null,
325
+ beforeStepSubmit: beforeStepSubmit,
326
+ resetFullFormState,
311
327
  }) }) }), jsx(FormWizardFooter, { stepForms,
312
328
  onBack: previousStepPath
313
329
  ? // eslint-disable-next-line local-rules/no-typescript-assertion
@@ -336,16 +352,18 @@ const cvaStepContainer = cvaMerge(["h-full", "grid", "bg-white", "overflow-hidde
336
352
  const FormWizard = ({ lastStepPrimaryActionLabel, isEdit, steps, fullFormSchema, onCancel, className, dataTestId, basePath, }) => {
337
353
  const navigate = useNavigate();
338
354
  const { stepPath, subStepPath } = useParams({ strict: false });
355
+ const initialFullFormState = useMemo(() =>
356
+ // eslint-disable-next-line local-rules/no-typescript-assertion
357
+ objectFromEntries(objectEntries(steps)
358
+ .filter(([_, { defaultValues }]) => defaultValues)
359
+ .map(([key, { defaultValues }]) => [key, defaultValues])), [steps]);
339
360
  const [stepStates, setStepStates] = useState({});
340
361
  const [stepForms, setStepForms] = useState({});
341
362
  const [fullFormState, setFullFormState] = useState(
342
363
  // DeepPartial does not allow empty object and properly working around it would severely complicate the code (DefaultValues uses DeepPartial under the hood)
343
364
  // A possible workaround could be to type it Partial<<DeepPartial<...>>> everywhere
344
365
  // Any weakening of the type in a union would be worse and likely just push the issue forward until it hits react-hook-form
345
- // eslint-disable-next-line local-rules/no-typescript-assertion
346
- objectFromEntries(objectEntries(steps)
347
- .filter(([_, { defaultValues }]) => defaultValues)
348
- .map(([key, { defaultValues }]) => [key, defaultValues])));
366
+ initialFullFormState);
349
367
  // If URL does not match a key from the schema, navigate to the first step
350
368
  const firstSchemaKey = String(objectKeys(fullFormSchema.shape)[0]);
351
369
  const keyToCheck = subStepPath ? `${stepPath}/${subStepPath}` : stepPath;
@@ -464,7 +482,8 @@ const FormWizard = ({ lastStepPrimaryActionLabel, isEdit, steps, fullFormSchema,
464
482
  fullFormSchema,
465
483
  lastStepPrimaryActionLabel,
466
484
  nextStepPath: (_b = (_a = entriesOfRenderableSteps[index + 1]) === null || _a === void 0 ? void 0 : _a[0].toString()) !== null && _b !== void 0 ? _b : null,
467
- previousStepPath: (_d = (_c = entriesOfRenderableSteps[index - 1]) === null || _c === void 0 ? void 0 : _c[0].toString()) !== null && _d !== void 0 ? _d : null }, stepKey.toString())) : null;
485
+ previousStepPath: (_d = (_c = entriesOfRenderableSteps[index - 1]) === null || _c === void 0 ? void 0 : _c[0].toString()) !== null && _d !== void 0 ? _d : null,
486
+ initialFullFormState }, stepKey.toString())) : null;
468
487
  });
469
488
  }, [
470
489
  basePath,
@@ -476,6 +495,7 @@ const FormWizard = ({ lastStepPrimaryActionLabel, isEdit, steps, fullFormSchema,
476
495
  onCancel,
477
496
  stepForms,
478
497
  steps,
498
+ initialFullFormState,
479
499
  ]);
480
500
  return (jsx("div", { className: cvaFormWizardContainer({ className }), "data-testid": dataTestId, children: jsxs("div", { className: cvaFormWizardLayout(), children: [jsx(FormWizardSidebar, { children: sidebarSteps }), jsx("div", { className: cvaStepContainer(), children: stepWrapper })] }) }));
481
501
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-form-wizard",
3
- "version": "0.1.338",
3
+ "version": "0.1.339",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "dependencies": {
@@ -13,6 +13,8 @@ export type StepComponentPropsInner<TStepSchema extends TFullSchemaValue[keyof T
13
13
  form: UseFormReturn<TStepSchema>;
14
14
  fullForm: UseFormReturn<TFullSchemaValue>;
15
15
  setSubmitHandler: ((onFinalSubmit: (fullFormState: TFullSchemaValue) => void) => void) | null;
16
+ beforeStepSubmit?: ((beforeSubmit: (fullFormState: TFullSchemaValue) => void) => void) | null;
17
+ resetFullFormState?: ((reset: () => void) => void) | null;
16
18
  };
17
19
  export interface SimpleStepProps {
18
20
  title: string;
@@ -38,7 +40,7 @@ export type StringWithStepKey<TPath> = TPath extends `${infer _Prefix}/$stepPath
38
40
  export interface UseFormWizardProps<TFullSchema extends SomeZodObject, TStepDefinitions extends StepDefinitions<TFullSchema>> {
39
41
  fullFormSchema: TFullSchema;
40
42
  lastStepPrimaryActionLabel: string;
41
- onCancel?: () => void;
43
+ onCancel?: (stepKey: string) => void;
42
44
  basePath: StringWithStepKey<`${RouteIds<RegisteredRouter["routeTree"]>}`>;
43
45
  steps: TStepDefinitions;
44
46
  isEdit?: boolean;
@@ -11,7 +11,7 @@ export interface FormWizardStepWrapperProps<TStepKey extends keyof z.TypeOf<TFul
11
11
  nextStepPath: string | null;
12
12
  basePath: StringWithStepKey<`${RoutePaths<RegisteredRouter["routeTree"]>}`>;
13
13
  previousStepPath: string | null;
14
- onCancel?: () => void;
14
+ onCancel?: (stepKey: string) => void;
15
15
  fullFormSchema: TFullSchema;
16
16
  fullFormState: DefaultValues<z.TypeOf<TFullSchema>>;
17
17
  setFullFormState: Dispatch<SetStateAction<DefaultValues<z.TypeOf<TFullSchema>>>>;
@@ -23,9 +23,10 @@ export interface FormWizardStepWrapperProps<TStepKey extends keyof z.TypeOf<TFul
23
23
  [TStepKey2 in keyof z.TypeOf<TFullSchema>]: UseFormReturn<z.TypeOf<TFullSchema>[TStepKey2]>;
24
24
  }>>>;
25
25
  onPrimaryAction?: (form: UseFormReturn<DeepPartial<z.TypeOf<TFullSchema>[TStepKey]>>) => Promise<unknown> | void;
26
+ initialFullFormState: DefaultValues<z.TypeOf<TFullSchema>>;
26
27
  }
27
28
  /**
28
29
  * A wrapper component used by the FormWizard component to render each step.
29
30
  * In most cases you should not be using this component directly, but rather use the FormWizard component.
30
31
  */
31
- export declare const FormWizardStepWrapper: <TFullSchema extends AnyZodObject, TStepKey extends keyof z.TypeOf<TFullSchema>>({ stepKey, onCancel, description, title, component, nextStepPath, previousStepPath, lastStepPrimaryActionLabel, fullFormSchema, setFullFormState, fullFormState, stepForms, setStepForms, setStepStates, basePath, containerClassName, onPrimaryAction, }: FormWizardStepWrapperProps<TStepKey, TFullSchema>) => import("react/jsx-runtime").JSX.Element;
32
+ export declare const FormWizardStepWrapper: <TFullSchema extends AnyZodObject, TStepKey extends keyof z.TypeOf<TFullSchema>>({ stepKey, onCancel, description, title, component, nextStepPath, previousStepPath, lastStepPrimaryActionLabel, fullFormSchema, setFullFormState, fullFormState, stepForms, setStepForms, setStepStates, basePath, containerClassName, onPrimaryAction, initialFullFormState, }: FormWizardStepWrapperProps<TStepKey, TFullSchema>) => import("react/jsx-runtime").JSX.Element;