@trackunit/react-form-wizard 0.0.30 → 0.0.32

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
@@ -90,6 +90,14 @@ const cvaFormWizardSidebarStep = cssClassVarianceUtilities.cvaMerge(["p-2", "rou
90
90
  invalid: "text-warning-600",
91
91
  disabled: "pointer-events-none cursor-not-allowed opacity-50 hover:bg-transparent",
92
92
  },
93
+ isSubStep: {
94
+ true: "@3xl:pl-6",
95
+ false: "",
96
+ },
97
+ },
98
+ defaultVariants: {
99
+ state: "ready",
100
+ isSubStep: false,
93
101
  },
94
102
  });
95
103
  const cvaSectionHeaderContainer = cssClassVarianceUtilities.cvaMerge(["flex", "justify-between", "gap-2"], {
@@ -114,6 +122,20 @@ const cvaStepNumber = cssClassVarianceUtilities.cvaMerge(["flex", "justify-cente
114
122
  invalid: "bg-warning-500 text-white",
115
123
  disabled: "",
116
124
  },
125
+ isSubStep: {
126
+ true: "border-2 border-slate-200",
127
+ false: "",
128
+ },
129
+ },
130
+ compoundVariants: [
131
+ {
132
+ state: "active",
133
+ isSubStep: true,
134
+ className: "border-2 border-slate-400 bg-white",
135
+ },
136
+ ],
137
+ defaultVariants: {
138
+ state: "ready",
117
139
  },
118
140
  });
119
141
  const cvaTitleAndDescription = cssClassVarianceUtilities.cvaMerge(["justify-center", "flex-1", "flex-col", "hidden", "@3xl:flex"]); //use same container breakpoint as cvaWizardContainer so it collapses at the same time
@@ -132,9 +154,9 @@ const getStateIcon = (state) => {
132
154
  * A sidebar step component used by the FormWizard component to render each step.
133
155
  * This component is generally used internally by the FormWizard component.
134
156
  */
135
- const FormWizardSidebarStep = ({ stepNumber, state, title, subTitle, path, dataTestId, onBeforeNavigate, }) => {
157
+ const FormWizardSidebarStep = ({ stepNumber, state, title, subTitle, path, dataTestId, onNavigate, isSubStep, }) => {
136
158
  const stateIcon = getStateIcon(state);
137
- return (jsxRuntime.jsx(reactRouterDom.NavLink, { className: cvaFormWizardSidebarStep({ state }), "data-testid": dataTestId !== null && dataTestId !== void 0 ? dataTestId : `form-wizard-sidebar-step-${path}`, onClick: () => onBeforeNavigate(), to: path, children: jsxRuntime.jsxs("div", { className: cvaSectionHeaderContainer(), children: [jsxRuntime.jsx("div", { children: stateIcon !== null && stateIcon !== void 0 ? stateIcon : jsxRuntime.jsx("span", { className: cvaStepNumber({ state }), children: stepNumber }) }), jsxRuntime.jsxs("div", { className: cvaTitleAndDescription(), children: [jsxRuntime.jsx("h1", { className: cvaHeader(), children: title }), subTitle ? jsxRuntime.jsx("p", { className: cvaSubtitle(), children: subTitle }) : null] })] }) }));
159
+ return (jsxRuntime.jsx(reactRouterDom.NavLink, { className: cvaFormWizardSidebarStep({ state, isSubStep }), "data-testid": dataTestId !== null && dataTestId !== void 0 ? dataTestId : `form-wizard-sidebar-step-${path}`, onClick: () => onNavigate(), to: path, children: jsxRuntime.jsxs("div", { className: cvaSectionHeaderContainer(), children: [jsxRuntime.jsx("div", { children: stateIcon !== null && stateIcon !== void 0 ? stateIcon : jsxRuntime.jsx("span", { className: cvaStepNumber({ state, isSubStep }), children: stepNumber }) }), jsxRuntime.jsxs("div", { className: cvaTitleAndDescription(), children: [jsxRuntime.jsx("h1", { className: cvaHeader(), children: title }), subTitle ? jsxRuntime.jsx("p", { className: cvaSubtitle(), children: subTitle }) : null] })] }) }));
138
160
  };
139
161
 
140
162
  /******************************************************************************
@@ -220,6 +242,14 @@ const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, componen
220
242
  mode: "all",
221
243
  defaultValues: fullFormState,
222
244
  });
245
+ const watchedValue = reactHookForm.useWatch({
246
+ // Must use useWatch instead of form.watch()
247
+ // to limit re-renders to only be caused by this component
248
+ control: form.control,
249
+ });
250
+ react.useEffect(() => {
251
+ setFullFormState(prev => (Object.assign(Object.assign({}, prev), { [stepKey]: watchedValue })));
252
+ }, [setFullFormState, stepKey, watchedValue]);
223
253
  react.useEffect(() => {
224
254
  setStepForms(prev => (Object.assign(Object.assign({}, prev), { [stepKey]: form })));
225
255
  }, [form, setStepForms, stepKey]);
@@ -291,13 +321,24 @@ const FormWizard = ({ lastStepPrimaryActionLabel, steps, fullFormSchema, onCance
291
321
  const location = reactRouterDom.useLocation();
292
322
  const [stepStates, setStepStates] = react.useState({});
293
323
  const [stepForms, setStepForms] = react.useState({});
294
- const [fullFormState, setFullFormState] = react.useState({});
324
+ const [fullFormState, setFullFormState] = react.useState(
325
+ // DeepPartial does not allow empty object and properly working around it would severely complicate the code
326
+ // A possible workaround could be to type it Partial<<DeepPartial<...>>> everywhere
327
+ // Any weakening of the type in a union would be worse and likely just push the issue forward until it hits react-hook-form
328
+ // eslint-disable-next-line local-rules/no-typescript-assertion
329
+ {});
295
330
  const currentVisibleStepKey = (_a = sharedUtils.objectEntries(steps).find(([, { path }]) => location.pathname === basePath + path)) === null || _a === void 0 ? void 0 : _a[0];
296
331
  const currentVisibleStepIndex = sharedUtils.objectKeys(steps).findIndex(key => key === currentVisibleStepKey);
297
- return (jsxRuntime.jsx("div", { className: cvaFormWizardContainer({ className }), "data-testid": dataTestId, children: jsxRuntime.jsxs("div", { className: cvaFormWizardLayout(), children: [jsxRuntime.jsx(FormWizardSidebar, { children: sharedUtils.objectEntries(steps).map(([key, { path, title, sidebarDescription }], index) => {
332
+ return (jsxRuntime.jsx("div", { className: cvaFormWizardContainer({ className }), "data-testid": dataTestId, children: jsxRuntime.jsxs("div", { className: cvaFormWizardLayout(), children: [jsxRuntime.jsx(FormWizardSidebar, { children: sharedUtils.objectEntries(steps)
333
+ .filter(([_, step]) => { var _a, _b; return (_b = (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState)) !== null && _b !== void 0 ? _b : true; })
334
+ .map(([key, { path, title, sidebarDescription, parent }], index) => {
298
335
  const isPastStep = index < currentVisibleStepIndex;
299
336
  const isFutureStep = index > currentVisibleStepIndex;
300
337
  const isCurrentStep = key === currentVisibleStepKey;
338
+ const isSubStep = parent !== undefined;
339
+ const onlyParentSteps = sharedUtils.objectEntries(steps)
340
+ .filter(([_, step]) => step.parent === undefined)
341
+ .map(([k]) => k);
301
342
  const previousStepKey = sharedUtils.objectKeys(steps)[index - 1];
302
343
  const stepState = () => {
303
344
  if (isCurrentStep) {
@@ -308,7 +349,7 @@ const FormWizard = ({ lastStepPrimaryActionLabel, steps, fullFormSchema, onCance
308
349
  }
309
350
  if (isFutureStep) {
310
351
  return sharedUtils.objectKeys(steps)
311
- .filter((_, stepIndex) => stepIndex < index)
352
+ .filter((stepKey, stepIndex) => { var _a, _b; return stepIndex < index && ((_b = (_a = steps[stepKey]).shouldRender) === null || _b === void 0 ? void 0 : _b.call(_a, fullFormState)); })
312
353
  .every((pastStepKey) => stepStates[pastStepKey] === "valid")
313
354
  ? "ready"
314
355
  : "disabled";
@@ -317,9 +358,9 @@ const FormWizard = ({ lastStepPrimaryActionLabel, steps, fullFormSchema, onCance
317
358
  };
318
359
  return (jsxRuntime.jsx(FormWizardSidebarStep, { key: String(key),
319
360
  state: stepState(),
320
- stepNumber: index + 1,
361
+ stepNumber: isSubStep ? undefined : onlyParentSteps.indexOf(key) + 1,
321
362
  title,
322
- onBeforeNavigate: () => {
363
+ onNavigate: () => {
323
364
  previousStepKey &&
324
365
  setFullFormState(prev => {
325
366
  var _a;
@@ -327,10 +368,19 @@ const FormWizard = ({ lastStepPrimaryActionLabel, steps, fullFormSchema, onCance
327
368
  });
328
369
  },
329
370
  path: basePath + path,
330
- subTitle: sidebarDescription }));
331
- }) }), jsxRuntime.jsx("div", { className: cvaStepContainer(), children: jsxRuntime.jsx(reactRouterDom.Routes, { children: sharedUtils.objectEntries(steps).map(([stepKey, props], index) => {
371
+ subTitle: sidebarDescription,
372
+ isSubStep }));
373
+ }) }), jsxRuntime.jsx("div", { className: cvaStepContainer(), children: jsxRuntime.jsx(reactRouterDom.Routes, { children: sharedUtils.objectEntries(steps)
374
+ .filter(([_, step]) => {
375
+ var _a, _b;
376
+ return (_b = (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState)) !== null && _b !== void 0 ? _b : true;
377
+ })
378
+ .map(([stepKey, props], index) => {
332
379
  var _a, _b, _c, _d;
333
- return (jsxRuntime.jsx(reactRouterDom.Route, { index: false, path: props.path, key: stepKey.toString(), element: jsxRuntime.jsx(FormWizardStepWrapper, Object.assign({}, props, { basePath,
380
+ return (jsxRuntime.jsx(reactRouterDom.Route, { index: false, path: props.path, key: stepKey.toString(), element: jsxRuntime.jsx(FormWizardStepWrapper, Object.assign({}, props, {
381
+ // eslint-disable-next-line local-rules/no-typescript-assertion
382
+ formSchema: props.formSchema,
383
+ basePath,
334
384
  stepKey: stepKey.toString(),
335
385
  stepForms,
336
386
  setStepForms,
@@ -340,22 +390,18 @@ const FormWizard = ({ lastStepPrimaryActionLabel, steps, fullFormSchema, onCance
340
390
  setStepStates,
341
391
  fullFormSchema,
342
392
  lastStepPrimaryActionLabel,
343
- nextStepPath: (_b = (_a = sharedUtils.objectValues(steps)[index + 1]) === null || _a === void 0 ? void 0 : _a.path) !== null && _b !== void 0 ? _b : null,
344
- previousStepPath: (_d = (_c = sharedUtils.objectValues(steps)[index - 1]) === null || _c === void 0 ? void 0 : _c.path) !== null && _d !== void 0 ? _d : null }), stepKey.toString()) }));
393
+ nextStepPath: (_b = (_a = sharedUtils.objectValues(steps).filter(step => { var _a, _b; return (_b = (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState)) !== null && _b !== void 0 ? _b : true; })[index + 1]) === null || _a === void 0 ? void 0 : _a.path) !== null && _b !== void 0 ? _b : null,
394
+ previousStepPath: (_d = (_c = sharedUtils.objectValues(steps).filter(step => { var _a, _b; return (_b = (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState)) !== null && _b !== void 0 ? _b : true; })[index - 1]) === null || _c === void 0 ? void 0 : _c.path) !== null && _d !== void 0 ? _d : null }), stepKey.toString()) }));
345
395
  }) }) })] }) }));
346
396
  };
347
397
 
348
398
  /**
349
- *
399
+ * A hook to generate the props for the FormWizard component.
350
400
  */
351
401
  const useFormWizard = (props) => {
352
402
  return react.useMemo(() => (Object.assign(Object.assign({}, props), {
353
- // Object.fromEntries is not super type safe... This is a workaround...
354
403
  // eslint-disable-next-line local-rules/no-typescript-assertion
355
- steps: sharedUtils.objectFromEntries(Object.entries(props.steps).map(([key, step]) => [
356
- key,
357
- Object.assign(Object.assign({}, step), { formSchema: props.fullFormSchema.shape[key] }),
358
- ])) })), [props]);
404
+ steps: sharedUtils.objectFromEntries(sharedUtils.objectEntries(props.steps).map(([key, step]) => [key, Object.assign(Object.assign({}, step), { formSchema: props.fullFormSchema.shape[key] })])) })), [props]);
359
405
  };
360
406
 
361
407
  /**
package/index.esm.js CHANGED
@@ -6,7 +6,7 @@ import { NavLink, useNavigate, useLocation, Routes, Route } from 'react-router-d
6
6
  import { cvaMerge } from '@trackunit/css-class-variance-utilities';
7
7
  import { Icon, Button, Heading } from '@trackunit/react-components';
8
8
  import { zodResolver } from '@hookform/resolvers/zod';
9
- import { useForm } from 'react-hook-form';
9
+ import { useForm, useWatch } from 'react-hook-form';
10
10
 
11
11
  var defaultTranslations = {
12
12
  "wizard.defaults.back": "Back",
@@ -86,6 +86,14 @@ const cvaFormWizardSidebarStep = cvaMerge(["p-2", "rounded-md", "hover:bg-slate-
86
86
  invalid: "text-warning-600",
87
87
  disabled: "pointer-events-none cursor-not-allowed opacity-50 hover:bg-transparent",
88
88
  },
89
+ isSubStep: {
90
+ true: "@3xl:pl-6",
91
+ false: "",
92
+ },
93
+ },
94
+ defaultVariants: {
95
+ state: "ready",
96
+ isSubStep: false,
89
97
  },
90
98
  });
91
99
  const cvaSectionHeaderContainer = cvaMerge(["flex", "justify-between", "gap-2"], {
@@ -110,6 +118,20 @@ const cvaStepNumber = cvaMerge(["flex", "justify-center", "text-xs", "items-cent
110
118
  invalid: "bg-warning-500 text-white",
111
119
  disabled: "",
112
120
  },
121
+ isSubStep: {
122
+ true: "border-2 border-slate-200",
123
+ false: "",
124
+ },
125
+ },
126
+ compoundVariants: [
127
+ {
128
+ state: "active",
129
+ isSubStep: true,
130
+ className: "border-2 border-slate-400 bg-white",
131
+ },
132
+ ],
133
+ defaultVariants: {
134
+ state: "ready",
113
135
  },
114
136
  });
115
137
  const cvaTitleAndDescription = cvaMerge(["justify-center", "flex-1", "flex-col", "hidden", "@3xl:flex"]); //use same container breakpoint as cvaWizardContainer so it collapses at the same time
@@ -128,9 +150,9 @@ const getStateIcon = (state) => {
128
150
  * A sidebar step component used by the FormWizard component to render each step.
129
151
  * This component is generally used internally by the FormWizard component.
130
152
  */
131
- const FormWizardSidebarStep = ({ stepNumber, state, title, subTitle, path, dataTestId, onBeforeNavigate, }) => {
153
+ const FormWizardSidebarStep = ({ stepNumber, state, title, subTitle, path, dataTestId, onNavigate, isSubStep, }) => {
132
154
  const stateIcon = getStateIcon(state);
133
- return (jsx(NavLink, { className: cvaFormWizardSidebarStep({ state }), "data-testid": dataTestId !== null && dataTestId !== void 0 ? dataTestId : `form-wizard-sidebar-step-${path}`, onClick: () => onBeforeNavigate(), to: path, children: jsxs("div", { className: cvaSectionHeaderContainer(), children: [jsx("div", { children: stateIcon !== null && stateIcon !== void 0 ? stateIcon : jsx("span", { className: cvaStepNumber({ state }), children: stepNumber }) }), jsxs("div", { className: cvaTitleAndDescription(), children: [jsx("h1", { className: cvaHeader(), children: title }), subTitle ? jsx("p", { className: cvaSubtitle(), children: subTitle }) : null] })] }) }));
155
+ return (jsx(NavLink, { className: cvaFormWizardSidebarStep({ state, isSubStep }), "data-testid": dataTestId !== null && dataTestId !== void 0 ? dataTestId : `form-wizard-sidebar-step-${path}`, onClick: () => onNavigate(), to: path, children: jsxs("div", { className: cvaSectionHeaderContainer(), children: [jsx("div", { children: stateIcon !== null && stateIcon !== void 0 ? stateIcon : jsx("span", { className: cvaStepNumber({ state, isSubStep }), children: stepNumber }) }), jsxs("div", { className: cvaTitleAndDescription(), children: [jsx("h1", { className: cvaHeader(), children: title }), subTitle ? jsx("p", { className: cvaSubtitle(), children: subTitle }) : null] })] }) }));
134
156
  };
135
157
 
136
158
  /******************************************************************************
@@ -216,6 +238,14 @@ const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, componen
216
238
  mode: "all",
217
239
  defaultValues: fullFormState,
218
240
  });
241
+ const watchedValue = useWatch({
242
+ // Must use useWatch instead of form.watch()
243
+ // to limit re-renders to only be caused by this component
244
+ control: form.control,
245
+ });
246
+ useEffect(() => {
247
+ setFullFormState(prev => (Object.assign(Object.assign({}, prev), { [stepKey]: watchedValue })));
248
+ }, [setFullFormState, stepKey, watchedValue]);
219
249
  useEffect(() => {
220
250
  setStepForms(prev => (Object.assign(Object.assign({}, prev), { [stepKey]: form })));
221
251
  }, [form, setStepForms, stepKey]);
@@ -287,13 +317,24 @@ const FormWizard = ({ lastStepPrimaryActionLabel, steps, fullFormSchema, onCance
287
317
  const location = useLocation();
288
318
  const [stepStates, setStepStates] = useState({});
289
319
  const [stepForms, setStepForms] = useState({});
290
- const [fullFormState, setFullFormState] = useState({});
320
+ const [fullFormState, setFullFormState] = useState(
321
+ // DeepPartial does not allow empty object and properly working around it would severely complicate the code
322
+ // A possible workaround could be to type it Partial<<DeepPartial<...>>> everywhere
323
+ // Any weakening of the type in a union would be worse and likely just push the issue forward until it hits react-hook-form
324
+ // eslint-disable-next-line local-rules/no-typescript-assertion
325
+ {});
291
326
  const currentVisibleStepKey = (_a = objectEntries(steps).find(([, { path }]) => location.pathname === basePath + path)) === null || _a === void 0 ? void 0 : _a[0];
292
327
  const currentVisibleStepIndex = objectKeys(steps).findIndex(key => key === currentVisibleStepKey);
293
- return (jsx("div", { className: cvaFormWizardContainer({ className }), "data-testid": dataTestId, children: jsxs("div", { className: cvaFormWizardLayout(), children: [jsx(FormWizardSidebar, { children: objectEntries(steps).map(([key, { path, title, sidebarDescription }], index) => {
328
+ return (jsx("div", { className: cvaFormWizardContainer({ className }), "data-testid": dataTestId, children: jsxs("div", { className: cvaFormWizardLayout(), children: [jsx(FormWizardSidebar, { children: objectEntries(steps)
329
+ .filter(([_, step]) => { var _a, _b; return (_b = (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState)) !== null && _b !== void 0 ? _b : true; })
330
+ .map(([key, { path, title, sidebarDescription, parent }], index) => {
294
331
  const isPastStep = index < currentVisibleStepIndex;
295
332
  const isFutureStep = index > currentVisibleStepIndex;
296
333
  const isCurrentStep = key === currentVisibleStepKey;
334
+ const isSubStep = parent !== undefined;
335
+ const onlyParentSteps = objectEntries(steps)
336
+ .filter(([_, step]) => step.parent === undefined)
337
+ .map(([k]) => k);
297
338
  const previousStepKey = objectKeys(steps)[index - 1];
298
339
  const stepState = () => {
299
340
  if (isCurrentStep) {
@@ -304,7 +345,7 @@ const FormWizard = ({ lastStepPrimaryActionLabel, steps, fullFormSchema, onCance
304
345
  }
305
346
  if (isFutureStep) {
306
347
  return objectKeys(steps)
307
- .filter((_, stepIndex) => stepIndex < index)
348
+ .filter((stepKey, stepIndex) => { var _a, _b; return stepIndex < index && ((_b = (_a = steps[stepKey]).shouldRender) === null || _b === void 0 ? void 0 : _b.call(_a, fullFormState)); })
308
349
  .every((pastStepKey) => stepStates[pastStepKey] === "valid")
309
350
  ? "ready"
310
351
  : "disabled";
@@ -313,9 +354,9 @@ const FormWizard = ({ lastStepPrimaryActionLabel, steps, fullFormSchema, onCance
313
354
  };
314
355
  return (jsx(FormWizardSidebarStep, { key: String(key),
315
356
  state: stepState(),
316
- stepNumber: index + 1,
357
+ stepNumber: isSubStep ? undefined : onlyParentSteps.indexOf(key) + 1,
317
358
  title,
318
- onBeforeNavigate: () => {
359
+ onNavigate: () => {
319
360
  previousStepKey &&
320
361
  setFullFormState(prev => {
321
362
  var _a;
@@ -323,10 +364,19 @@ const FormWizard = ({ lastStepPrimaryActionLabel, steps, fullFormSchema, onCance
323
364
  });
324
365
  },
325
366
  path: basePath + path,
326
- subTitle: sidebarDescription }));
327
- }) }), jsx("div", { className: cvaStepContainer(), children: jsx(Routes, { children: objectEntries(steps).map(([stepKey, props], index) => {
367
+ subTitle: sidebarDescription,
368
+ isSubStep }));
369
+ }) }), jsx("div", { className: cvaStepContainer(), children: jsx(Routes, { children: objectEntries(steps)
370
+ .filter(([_, step]) => {
371
+ var _a, _b;
372
+ return (_b = (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState)) !== null && _b !== void 0 ? _b : true;
373
+ })
374
+ .map(([stepKey, props], index) => {
328
375
  var _a, _b, _c, _d;
329
- return (jsx(Route, { index: false, path: props.path, key: stepKey.toString(), element: jsx(FormWizardStepWrapper, Object.assign({}, props, { basePath,
376
+ return (jsx(Route, { index: false, path: props.path, key: stepKey.toString(), element: jsx(FormWizardStepWrapper, Object.assign({}, props, {
377
+ // eslint-disable-next-line local-rules/no-typescript-assertion
378
+ formSchema: props.formSchema,
379
+ basePath,
330
380
  stepKey: stepKey.toString(),
331
381
  stepForms,
332
382
  setStepForms,
@@ -336,22 +386,18 @@ const FormWizard = ({ lastStepPrimaryActionLabel, steps, fullFormSchema, onCance
336
386
  setStepStates,
337
387
  fullFormSchema,
338
388
  lastStepPrimaryActionLabel,
339
- nextStepPath: (_b = (_a = objectValues(steps)[index + 1]) === null || _a === void 0 ? void 0 : _a.path) !== null && _b !== void 0 ? _b : null,
340
- previousStepPath: (_d = (_c = objectValues(steps)[index - 1]) === null || _c === void 0 ? void 0 : _c.path) !== null && _d !== void 0 ? _d : null }), stepKey.toString()) }));
389
+ nextStepPath: (_b = (_a = objectValues(steps).filter(step => { var _a, _b; return (_b = (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState)) !== null && _b !== void 0 ? _b : true; })[index + 1]) === null || _a === void 0 ? void 0 : _a.path) !== null && _b !== void 0 ? _b : null,
390
+ previousStepPath: (_d = (_c = objectValues(steps).filter(step => { var _a, _b; return (_b = (_a = step.shouldRender) === null || _a === void 0 ? void 0 : _a.call(step, fullFormState)) !== null && _b !== void 0 ? _b : true; })[index - 1]) === null || _c === void 0 ? void 0 : _c.path) !== null && _d !== void 0 ? _d : null }), stepKey.toString()) }));
341
391
  }) }) })] }) }));
342
392
  };
343
393
 
344
394
  /**
345
- *
395
+ * A hook to generate the props for the FormWizard component.
346
396
  */
347
397
  const useFormWizard = (props) => {
348
398
  return useMemo(() => (Object.assign(Object.assign({}, props), {
349
- // Object.fromEntries is not super type safe... This is a workaround...
350
399
  // eslint-disable-next-line local-rules/no-typescript-assertion
351
- steps: objectFromEntries(Object.entries(props.steps).map(([key, step]) => [
352
- key,
353
- Object.assign(Object.assign({}, step), { formSchema: props.fullFormSchema.shape[key] }),
354
- ])) })), [props]);
400
+ steps: objectFromEntries(objectEntries(props.steps).map(([key, step]) => [key, Object.assign(Object.assign({}, step), { formSchema: props.fullFormSchema.shape[key] })])) })), [props]);
355
401
  };
356
402
 
357
403
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-form-wizard",
3
- "version": "0.0.30",
3
+ "version": "0.0.32",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "dependencies": {
@@ -1,12 +1,12 @@
1
1
  /// <reference types="react" />
2
2
  import { UseFormReturn } from "react-hook-form";
3
- import { ZodTypeAny, z } from "zod";
3
+ import { AnyZodObject, z } from "zod";
4
4
  import { ComponentStepDefinitions, FromWizardComponentProps } from "./useFormWizard";
5
- export type FormWizardStepFormReturnMap<TFullSchema extends ZodTypeAny> = Partial<{
5
+ export type FormWizardStepFormReturnMap<TFullSchema extends AnyZodObject> = Partial<{
6
6
  [TStepKey in keyof z.infer<TFullSchema>]: UseFormReturn<z.TypeOf<TFullSchema>[TStepKey]>;
7
7
  }>;
8
8
  /**
9
9
  * The FormWizard component is used for forms in multiple steps.
10
10
  * Use the useFormWizard hook to generate the props for this component.
11
11
  */
12
- export declare const FormWizard: <TFullSchema extends ZodTypeAny, TComponentStepDefinitions extends ComponentStepDefinitions<TFullSchema>>({ lastStepPrimaryActionLabel, steps, fullFormSchema, onCancel, className, dataTestId, basePath, }: FromWizardComponentProps<TFullSchema, TComponentStepDefinitions>) => JSX.Element;
12
+ export declare const FormWizard: <TFullSchema extends AnyZodObject, TComponentStepDefinitions extends ComponentStepDefinitions<TFullSchema>>({ lastStepPrimaryActionLabel, steps, fullFormSchema, onCancel, className, dataTestId, basePath, }: FromWizardComponentProps<TFullSchema, TComponentStepDefinitions>) => JSX.Element;
@@ -1,15 +1,15 @@
1
1
  import { CommonProps } from "@trackunit/react-components";
2
2
  import { ReactNode } from "react";
3
3
  import { UseFormReturn } from "react-hook-form";
4
- import { AnyZodObject, ZodTypeAny, z } from "zod";
4
+ import { AnyZodObject, SomeZodObject, z } from "zod";
5
5
  export type StepState = "active" | "done" | "invalid" | "disabled" | "ready";
6
- export type StepDefinitions<TFullSchema extends ZodTypeAny> = {
7
- [KStepKey in keyof z.infer<TFullSchema>]: DefinedStep<z.infer<TFullSchema>[KStepKey], z.infer<TFullSchema>>;
6
+ export type StepDefinitions<TFullSchema extends AnyZodObject> = {
7
+ [KStepKey in keyof z.TypeOf<TFullSchema>]: DefinedStep<z.TypeOf<TFullSchema>[KStepKey], z.TypeOf<TFullSchema>>;
8
8
  };
9
- export type StepComponentPropsInner<TStepSchema extends ZodTypeAny, TFullSchema extends ZodTypeAny> = {
9
+ export type StepComponentPropsInner<TStepSchema extends TFullSchemaValue[keyof TFullSchemaValue], TFullSchemaValue extends z.TypeOf<AnyZodObject>> = {
10
10
  form: UseFormReturn<TStepSchema>;
11
- fullForm: UseFormReturn<TFullSchema>;
12
- setSubmitHandler: ((onFinalSubmit: (fullFormState: TFullSchema) => void) => void) | null;
11
+ fullForm: UseFormReturn<TFullSchemaValue>;
12
+ setSubmitHandler: ((onFinalSubmit: (fullFormState: TFullSchemaValue) => void) => void) | null;
13
13
  };
14
14
  export interface SimpleStepProps {
15
15
  title: string;
@@ -17,20 +17,22 @@ export interface SimpleStepProps {
17
17
  sidebarDescription?: string;
18
18
  path: string;
19
19
  }
20
- interface DefinedStep<TStepSchema extends ZodTypeAny, TFullSchema extends ZodTypeAny> extends SimpleStepProps {
21
- component: (props: StepComponentPropsInner<TStepSchema, TFullSchema>) => React.ReactNode;
20
+ interface DefinedStep<TStepSchema extends TFullSchemaValue[keyof TFullSchemaValue], TFullSchemaValue extends z.TypeOf<AnyZodObject>> extends SimpleStepProps {
21
+ component: (props: StepComponentPropsInner<TStepSchema, TFullSchemaValue>) => React.ReactNode;
22
+ parent?: keyof TFullSchemaValue;
23
+ shouldRender?: (fullFormState: Partial<TFullSchemaValue>) => boolean;
22
24
  }
23
- export type StepComponentProps<TFullSchemaHook extends () => ZodTypeAny, TStepKey extends keyof z.infer<ReturnType<TFullSchemaHook>>> = StepComponentPropsInner<z.infer<ReturnType<TFullSchemaHook>>[TStepKey], z.infer<ReturnType<TFullSchemaHook>>>;
24
- export type ComponentStepDefinitions<TFullSchema extends ZodTypeAny> = {
25
- [KStepKey in keyof z.infer<TFullSchema>]: DefinedComponentStep<z.infer<TFullSchema>[KStepKey], TFullSchema>;
25
+ export type StepComponentProps<TFullSchemaHook extends () => AnyZodObject, TStepKey extends keyof z.TypeOf<ReturnType<TFullSchemaHook>>> = StepComponentPropsInner<z.TypeOf<ReturnType<TFullSchemaHook>>[TStepKey], z.TypeOf<ReturnType<TFullSchemaHook>>>;
26
+ export type ComponentStepDefinitions<TFullSchema extends AnyZodObject> = {
27
+ [KStepKey in keyof z.TypeOf<TFullSchema>]: DefinedComponentStep<z.TypeOf<TFullSchema>[KStepKey], z.TypeOf<TFullSchema>>;
26
28
  };
27
- interface DefinedComponentStep<TStepSchema extends ZodTypeAny, TFullSchema extends ZodTypeAny> extends DefinedStep<TStepSchema, TFullSchema> {
29
+ interface DefinedComponentStep<TStepSchema extends TFullSchemaValue[keyof TFullSchemaValue], TFullSchemaValue extends z.TypeOf<AnyZodObject>> extends DefinedStep<TStepSchema, TFullSchemaValue> {
28
30
  formSchema: TStepSchema;
29
31
  }
30
- export interface FromWizardComponentProps<TFullSchema extends ZodTypeAny, TComponentStepDefinitions extends ComponentStepDefinitions<TFullSchema>> extends Omit<UseFormWizardProps<TFullSchema, TComponentStepDefinitions>, "steps">, CommonProps {
32
+ export interface FromWizardComponentProps<TFullSchema extends AnyZodObject, TComponentStepDefinitions extends ComponentStepDefinitions<TFullSchema>> extends Omit<UseFormWizardProps<TFullSchema, TComponentStepDefinitions>, "steps">, CommonProps {
31
33
  steps: TComponentStepDefinitions;
32
34
  }
33
- export interface UseFormWizardProps<TFullSchema extends ZodTypeAny, TStepDefinitions extends StepDefinitions<TFullSchema>> {
35
+ export interface UseFormWizardProps<TFullSchema extends SomeZodObject, TStepDefinitions extends StepDefinitions<TFullSchema>> {
34
36
  fullFormSchema: TFullSchema;
35
37
  lastStepPrimaryActionLabel: string;
36
38
  onCancel?: () => void;
@@ -38,7 +40,7 @@ export interface UseFormWizardProps<TFullSchema extends ZodTypeAny, TStepDefinit
38
40
  steps: TStepDefinitions;
39
41
  }
40
42
  /**
41
- *
43
+ * A hook to generate the props for the FormWizard component.
42
44
  */
43
45
  export declare const useFormWizard: <TStepDefinition extends StepDefinitions<TFullSchema>, TFullSchema extends AnyZodObject>(props: UseFormWizardProps<TFullSchema, TStepDefinition>) => FromWizardComponentProps<TFullSchema, ComponentStepDefinitions<TFullSchema>>;
44
46
  export {};
@@ -7,11 +7,12 @@ interface FormWizardSidebarStepProps extends CommonProps {
7
7
  title: string;
8
8
  subTitle?: string;
9
9
  path: string;
10
- onBeforeNavigate: () => void;
10
+ onNavigate: () => void;
11
+ isSubStep?: boolean;
11
12
  }
12
13
  /**
13
14
  * A sidebar step component used by the FormWizard component to render each step.
14
15
  * This component is generally used internally by the FormWizard component.
15
16
  */
16
- export declare const FormWizardSidebarStep: ({ stepNumber, state, title, subTitle, path, dataTestId, onBeforeNavigate, }: FormWizardSidebarStepProps) => JSX.Element;
17
+ export declare const FormWizardSidebarStep: ({ stepNumber, state, title, subTitle, path, dataTestId, onNavigate, isSubStep, }: FormWizardSidebarStepProps) => JSX.Element;
17
18
  export {};
@@ -1,5 +1,6 @@
1
1
  export declare const cvaFormWizardSidebarStep: (props?: ({
2
2
  state?: "ready" | "active" | "done" | "invalid" | "disabled" | null | undefined;
3
+ isSubStep?: boolean | null | undefined;
3
4
  } & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
4
5
  export declare const cvaSectionHeaderContainer: (props?: ({
5
6
  cursor?: "default" | "withCursor" | null | undefined;
@@ -8,5 +9,6 @@ export declare const cvaHeader: (props?: import("class-variance-authority/dist/t
8
9
  export declare const cvaSubtitle: (props?: import("class-variance-authority/dist/types").ClassProp | undefined) => string;
9
10
  export declare const cvaStepNumber: (props?: ({
10
11
  state?: "ready" | "active" | "done" | "invalid" | "disabled" | null | undefined;
12
+ isSubStep?: boolean | null | undefined;
11
13
  } & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
12
14
  export declare const cvaTitleAndDescription: (props?: import("class-variance-authority/dist/types").ClassProp | undefined) => string;
@@ -1,10 +1,10 @@
1
1
  import { Dispatch, ReactNode, SetStateAction } from "react";
2
- import { UseFormReturn } from "react-hook-form";
3
- import { ZodTypeAny, z } from "zod";
2
+ import { DeepPartial, UseFormReturn } from "react-hook-form";
3
+ import { AnyZodObject, z } from "zod";
4
4
  import { FormWizardStepFormReturnMap } from "../FormWizard/FormWizard";
5
5
  import { ComponentStepDefinitions, SimpleStepProps, StepComponentPropsInner } from "../FormWizard/useFormWizard";
6
6
  export type StepFormValidity = "valid" | "invalid";
7
- export interface FormWizardStepWrapperProps<TStepKey extends keyof z.infer<TFullSchema>, TFullSchema extends ZodTypeAny> extends SimpleStepProps {
7
+ export interface FormWizardStepWrapperProps<TStepKey extends keyof z.TypeOf<TFullSchema>, TFullSchema extends AnyZodObject> extends SimpleStepProps {
8
8
  description: string | ReactNode;
9
9
  lastStepPrimaryActionLabel: string;
10
10
  nextStepPath: string | null;
@@ -12,12 +12,12 @@ export interface FormWizardStepWrapperProps<TStepKey extends keyof z.infer<TFull
12
12
  previousStepPath: string | null;
13
13
  onCancel?: () => void;
14
14
  fullFormSchema: TFullSchema;
15
- fullFormState: z.TypeOf<TFullSchema>;
16
- setFullFormState: Dispatch<SetStateAction<Partial<z.TypeOf<TFullSchema>>>>;
15
+ fullFormState: DeepPartial<z.TypeOf<TFullSchema>>;
16
+ setFullFormState: Dispatch<SetStateAction<DeepPartial<z.TypeOf<TFullSchema>>>>;
17
17
  stepKey: TStepKey;
18
- formSchema: z.infer<TFullSchema>[TStepKey];
18
+ formSchema: z.TypeOf<TFullSchema>[TStepKey];
19
19
  setStepStates: Dispatch<SetStateAction<Partial<Record<keyof ComponentStepDefinitions<TFullSchema>, StepFormValidity>>>>;
20
- component: (props: StepComponentPropsInner<z.infer<TFullSchema>[TStepKey], TFullSchema>) => ReactNode;
20
+ component: (props: StepComponentPropsInner<DeepPartial<z.TypeOf<TFullSchema>[TStepKey]>, z.TypeOf<TFullSchema>>) => ReactNode;
21
21
  stepForms: FormWizardStepFormReturnMap<TFullSchema>;
22
22
  setStepForms: Dispatch<SetStateAction<Partial<{
23
23
  [TStepKey2 in keyof z.TypeOf<TFullSchema>]: UseFormReturn<z.TypeOf<TFullSchema>[TStepKey2]>;
@@ -27,4 +27,4 @@ export interface FormWizardStepWrapperProps<TStepKey extends keyof z.infer<TFull
27
27
  * A wrapper component used by the FormWizard component to render each step.
28
28
  * In most cases you should not be using this component directly, but rather use the FormWizard component.
29
29
  */
30
- export declare const FormWizardStepWrapper: <TFullSchema extends ZodTypeAny, TStepKey extends keyof z.TypeOf<TFullSchema>>({ stepKey, onCancel, description, title, component, formSchema, nextStepPath, previousStepPath, lastStepPrimaryActionLabel, fullFormSchema, setFullFormState, fullFormState, stepForms, setStepForms, setStepStates, basePath, }: FormWizardStepWrapperProps<TStepKey, TFullSchema>) => JSX.Element;
30
+ export declare const FormWizardStepWrapper: <TFullSchema extends AnyZodObject, TStepKey extends keyof z.TypeOf<TFullSchema>>({ stepKey, onCancel, description, title, component, formSchema, nextStepPath, previousStepPath, lastStepPrimaryActionLabel, fullFormSchema, setFullFormState, fullFormState, stepForms, setStepForms, setStepStates, basePath, }: FormWizardStepWrapperProps<TStepKey, TFullSchema>) => JSX.Element;