@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 +64 -18
- package/index.esm.js +65 -19
- package/package.json +1 -1
- package/src/FormWizard/FormWizard.d.ts +3 -3
- package/src/FormWizard/useFormWizard.d.ts +17 -15
- package/src/FormWizardSidebarStep/FormWizardSidebarStep.d.ts +3 -2
- package/src/FormWizardSidebarStep/FormWizardSidebarStep.variants.d.ts +2 -0
- package/src/FormWizardStepWrapper/FormWizardStepWrapper.d.ts +8 -8
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,
|
|
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: () =>
|
|
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)
|
|
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((
|
|
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:
|
|
361
|
+
stepNumber: isSubStep ? undefined : onlyParentSteps.indexOf(key) + 1,
|
|
321
362
|
title,
|
|
322
|
-
|
|
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
|
-
|
|
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, {
|
|
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(
|
|
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,
|
|
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: () =>
|
|
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)
|
|
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((
|
|
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:
|
|
357
|
+
stepNumber: isSubStep ? undefined : onlyParentSteps.indexOf(key) + 1,
|
|
317
358
|
title,
|
|
318
|
-
|
|
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
|
-
|
|
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, {
|
|
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(
|
|
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,12 +1,12 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import { UseFormReturn } from "react-hook-form";
|
|
3
|
-
import {
|
|
3
|
+
import { AnyZodObject, z } from "zod";
|
|
4
4
|
import { ComponentStepDefinitions, FromWizardComponentProps } from "./useFormWizard";
|
|
5
|
-
export type FormWizardStepFormReturnMap<TFullSchema extends
|
|
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
|
|
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,
|
|
4
|
+
import { AnyZodObject, SomeZodObject, z } from "zod";
|
|
5
5
|
export type StepState = "active" | "done" | "invalid" | "disabled" | "ready";
|
|
6
|
-
export type StepDefinitions<TFullSchema extends
|
|
7
|
-
[KStepKey in keyof z.
|
|
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
|
|
9
|
+
export type StepComponentPropsInner<TStepSchema extends TFullSchemaValue[keyof TFullSchemaValue], TFullSchemaValue extends z.TypeOf<AnyZodObject>> = {
|
|
10
10
|
form: UseFormReturn<TStepSchema>;
|
|
11
|
-
fullForm: UseFormReturn<
|
|
12
|
-
setSubmitHandler: ((onFinalSubmit: (fullFormState:
|
|
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
|
|
21
|
-
component: (props: StepComponentPropsInner<TStepSchema,
|
|
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 () =>
|
|
24
|
-
export type ComponentStepDefinitions<TFullSchema extends
|
|
25
|
-
[KStepKey in keyof z.
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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,
|
|
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 {
|
|
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.
|
|
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<
|
|
15
|
+
fullFormState: DeepPartial<z.TypeOf<TFullSchema>>;
|
|
16
|
+
setFullFormState: Dispatch<SetStateAction<DeepPartial<z.TypeOf<TFullSchema>>>>;
|
|
17
17
|
stepKey: TStepKey;
|
|
18
|
-
formSchema: z.
|
|
18
|
+
formSchema: z.TypeOf<TFullSchema>[TStepKey];
|
|
19
19
|
setStepStates: Dispatch<SetStateAction<Partial<Record<keyof ComponentStepDefinitions<TFullSchema>, StepFormValidity>>>>;
|
|
20
|
-
component: (props: StepComponentPropsInner<z.
|
|
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
|
|
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;
|