@trackunit/react-form-wizard 0.1.10 → 0.1.12
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
|
@@ -200,9 +200,9 @@ const cvaNavigationContainer = cssClassVarianceUtilities.cvaMerge(["flex", "gap-
|
|
|
200
200
|
* The footer component for the FormWizard.
|
|
201
201
|
* In most cases you should not be using this component directly, but rather use the FormWizard component.
|
|
202
202
|
*/
|
|
203
|
-
const FormWizardFooter = ({ onBack, onCancel, primaryActionLabel = "Next" }) => {
|
|
203
|
+
const FormWizardFooter = ({ disablePrimaryAction = false, onBack, onCancel, primaryActionLabel = "Next", }) => {
|
|
204
204
|
const [t] = useTranslation();
|
|
205
|
-
return (jsxRuntime.jsxs("div", { className: cvaFooterContainer(), children: [jsxRuntime.jsx(reactComponents.Button, { dataTestId: "wizard-cancel", onClick: onCancel, variant: "ghost-neutral", children: t("wizard.defaults.cancel") }), jsxRuntime.jsxs("div", { className: cvaNavigationContainer(), children: [onBack ? (jsxRuntime.jsx(reactComponents.Button, { dataTestId: "wizard-back", onClick: onBack, variant: "secondary", children: t("wizard.defaults.back") })) : null, jsxRuntime.jsx(reactComponents.Button, { dataTestId: "wizard-next", type: "submit", children: primaryActionLabel ? primaryActionLabel : t("wizard.defaults.next") })] })] }));
|
|
205
|
+
return (jsxRuntime.jsxs("div", { className: cvaFooterContainer(), children: [jsxRuntime.jsx(reactComponents.Button, { dataTestId: "wizard-cancel", onClick: onCancel, variant: "ghost-neutral", children: t("wizard.defaults.cancel") }), jsxRuntime.jsxs("div", { className: cvaNavigationContainer(), children: [onBack ? (jsxRuntime.jsx(reactComponents.Button, { dataTestId: "wizard-back", onClick: onBack, variant: "secondary", children: t("wizard.defaults.back") })) : null, jsxRuntime.jsx(reactComponents.Button, { dataTestId: "wizard-next", disabled: disablePrimaryAction, type: "submit", children: primaryActionLabel ? primaryActionLabel : t("wizard.defaults.next") })] })] }));
|
|
206
206
|
};
|
|
207
207
|
|
|
208
208
|
const cvaFormWizardHeader = cssClassVarianceUtilities.cvaMerge([
|
|
@@ -230,6 +230,7 @@ const cvaFormWizardComponentStepForm = cssClassVarianceUtilities.cvaMerge(["grid
|
|
|
230
230
|
*/
|
|
231
231
|
const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, component, formSchema, nextStepPath, previousStepPath, lastStepPrimaryActionLabel, fullFormSchema, setFullFormState, fullFormState, stepForms, setStepForms, setStepStates, basePath, }) => {
|
|
232
232
|
const navigate = reactRouter.useNavigate();
|
|
233
|
+
const [isSubmitting, setIsSubmitting] = react.useState(false);
|
|
233
234
|
const formRef = react.useRef(null);
|
|
234
235
|
const [submitHandlerInternal, setSubmitHandlerInternal] = react.useState(null);
|
|
235
236
|
const form = reactHookForm.useForm({
|
|
@@ -262,7 +263,12 @@ const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, componen
|
|
|
262
263
|
return onCancel ? onCancel() : navigate({ to: basePath, params: { stepPath: "" } });
|
|
263
264
|
}, [onCancel, navigate, basePath]);
|
|
264
265
|
const setSubmitHandler = react.useCallback(callback => {
|
|
265
|
-
|
|
266
|
+
const wrappedCallbackFunction = (fullFormStateInner) => __awaiter(void 0, void 0, void 0, function* () {
|
|
267
|
+
setIsSubmitting(true);
|
|
268
|
+
yield callback(fullFormStateInner);
|
|
269
|
+
setIsSubmitting(false);
|
|
270
|
+
});
|
|
271
|
+
setSubmitHandlerInternal(() => wrappedCallbackFunction);
|
|
266
272
|
}, [setSubmitHandlerInternal]);
|
|
267
273
|
const isEmptyStep = sharedUtils.objectKeys(form.getValues()).length === 0;
|
|
268
274
|
const emptyStepSubmitHandler = react.useCallback((e) => {
|
|
@@ -274,7 +280,7 @@ const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, componen
|
|
|
274
280
|
setFullFormState(prev => (Object.assign(Object.assign({}, prev), { [stepKey]: data })));
|
|
275
281
|
if (isLastStep) {
|
|
276
282
|
//? This state is not updated for the submit handler below; therefore, there is duplication.
|
|
277
|
-
submitHandlerInternal === null || submitHandlerInternal === void 0 ? void 0 : submitHandlerInternal(Object.assign(Object.assign({}, fullFormState), { [stepKey]: data }));
|
|
283
|
+
yield (submitHandlerInternal === null || submitHandlerInternal === void 0 ? void 0 : submitHandlerInternal(Object.assign(Object.assign({}, fullFormState), { [stepKey]: data })));
|
|
278
284
|
}
|
|
279
285
|
else if (nextStepPath) {
|
|
280
286
|
navigate({ to: basePath, params: { stepPath: nextStepPath } });
|
|
@@ -298,7 +304,8 @@ const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, componen
|
|
|
298
304
|
}) }), jsxRuntime.jsx(FormWizardFooter, { stepForms,
|
|
299
305
|
onBack: previousStepPath ? () => navigate({ to: basePath, params: { stepPath: previousStepPath } }) : null,
|
|
300
306
|
primaryActionLabel: isLastStep ? lastStepPrimaryActionLabel : undefined,
|
|
301
|
-
onCancel: handleCancel
|
|
307
|
+
onCancel: handleCancel,
|
|
308
|
+
disablePrimaryAction: isSubmitting })] }, String(stepKey)));
|
|
302
309
|
};
|
|
303
310
|
|
|
304
311
|
const cvaFormWizardContainer = cssClassVarianceUtilities.cvaMerge(["h-full"]);
|
|
@@ -325,7 +332,9 @@ const FormWizard = ({ lastStepPrimaryActionLabel, steps, fullFormSchema, onCance
|
|
|
325
332
|
// A possible workaround could be to type it Partial<<DeepPartial<...>>> everywhere
|
|
326
333
|
// Any weakening of the type in a union would be worse and likely just push the issue forward until it hits react-hook-form
|
|
327
334
|
// eslint-disable-next-line local-rules/no-typescript-assertion
|
|
328
|
-
|
|
335
|
+
sharedUtils.objectFromEntries(sharedUtils.objectEntries(steps)
|
|
336
|
+
.filter(([_, { defaultValues }]) => defaultValues)
|
|
337
|
+
.map(([key, { defaultValues }]) => [key, defaultValues])));
|
|
329
338
|
const currentVisibleStepKey = (_a = sharedUtils.objectEntries(steps).find(([, { path }]) => subStepPath ? path === `${stepPath}/${subStepPath}` : path === stepPath)) === null || _a === void 0 ? void 0 : _a[0];
|
|
330
339
|
const currentVisibleStepIndex = sharedUtils.objectKeys(steps).findIndex(key => key === currentVisibleStepKey);
|
|
331
340
|
return (jsxRuntime.jsx("div", { className: cvaFormWizardContainer({ className }), "data-testid": dataTestId, children: jsxRuntime.jsxs("div", { className: cvaFormWizardLayout(), children: [jsxRuntime.jsx(FormWizardSidebar, { children: sharedUtils.objectEntries(steps)
|
package/index.esm.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { registerTranslations, useNamespaceTranslation } from '@trackunit/i18n-library-translation';
|
|
3
3
|
import { Link, useNavigate, useParams } from '@tanstack/react-router';
|
|
4
|
-
import { objectKeys, objectEntries, objectValues
|
|
5
|
-
import {
|
|
4
|
+
import { objectKeys, objectFromEntries, objectEntries, objectValues } from '@trackunit/shared-utils';
|
|
5
|
+
import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
|
|
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';
|
|
@@ -196,9 +196,9 @@ const cvaNavigationContainer = cvaMerge(["flex", "gap-responsive-space"]);
|
|
|
196
196
|
* The footer component for the FormWizard.
|
|
197
197
|
* In most cases you should not be using this component directly, but rather use the FormWizard component.
|
|
198
198
|
*/
|
|
199
|
-
const FormWizardFooter = ({ onBack, onCancel, primaryActionLabel = "Next" }) => {
|
|
199
|
+
const FormWizardFooter = ({ disablePrimaryAction = false, onBack, onCancel, primaryActionLabel = "Next", }) => {
|
|
200
200
|
const [t] = useTranslation();
|
|
201
|
-
return (jsxs("div", { className: cvaFooterContainer(), children: [jsx(Button, { dataTestId: "wizard-cancel", onClick: onCancel, variant: "ghost-neutral", children: t("wizard.defaults.cancel") }), jsxs("div", { className: cvaNavigationContainer(), children: [onBack ? (jsx(Button, { dataTestId: "wizard-back", onClick: onBack, variant: "secondary", children: t("wizard.defaults.back") })) : null, jsx(Button, { dataTestId: "wizard-next", type: "submit", children: primaryActionLabel ? primaryActionLabel : t("wizard.defaults.next") })] })] }));
|
|
201
|
+
return (jsxs("div", { className: cvaFooterContainer(), children: [jsx(Button, { dataTestId: "wizard-cancel", onClick: onCancel, variant: "ghost-neutral", children: t("wizard.defaults.cancel") }), jsxs("div", { className: cvaNavigationContainer(), children: [onBack ? (jsx(Button, { dataTestId: "wizard-back", onClick: onBack, variant: "secondary", children: t("wizard.defaults.back") })) : null, jsx(Button, { dataTestId: "wizard-next", disabled: disablePrimaryAction, type: "submit", children: primaryActionLabel ? primaryActionLabel : t("wizard.defaults.next") })] })] }));
|
|
202
202
|
};
|
|
203
203
|
|
|
204
204
|
const cvaFormWizardHeader = cvaMerge([
|
|
@@ -226,6 +226,7 @@ const cvaFormWizardComponentStepForm = cvaMerge(["grid-rows-min-fr-min", "grid",
|
|
|
226
226
|
*/
|
|
227
227
|
const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, component, formSchema, nextStepPath, previousStepPath, lastStepPrimaryActionLabel, fullFormSchema, setFullFormState, fullFormState, stepForms, setStepForms, setStepStates, basePath, }) => {
|
|
228
228
|
const navigate = useNavigate();
|
|
229
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
229
230
|
const formRef = useRef(null);
|
|
230
231
|
const [submitHandlerInternal, setSubmitHandlerInternal] = useState(null);
|
|
231
232
|
const form = useForm({
|
|
@@ -258,7 +259,12 @@ const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, componen
|
|
|
258
259
|
return onCancel ? onCancel() : navigate({ to: basePath, params: { stepPath: "" } });
|
|
259
260
|
}, [onCancel, navigate, basePath]);
|
|
260
261
|
const setSubmitHandler = useCallback(callback => {
|
|
261
|
-
|
|
262
|
+
const wrappedCallbackFunction = (fullFormStateInner) => __awaiter(void 0, void 0, void 0, function* () {
|
|
263
|
+
setIsSubmitting(true);
|
|
264
|
+
yield callback(fullFormStateInner);
|
|
265
|
+
setIsSubmitting(false);
|
|
266
|
+
});
|
|
267
|
+
setSubmitHandlerInternal(() => wrappedCallbackFunction);
|
|
262
268
|
}, [setSubmitHandlerInternal]);
|
|
263
269
|
const isEmptyStep = objectKeys(form.getValues()).length === 0;
|
|
264
270
|
const emptyStepSubmitHandler = useCallback((e) => {
|
|
@@ -270,7 +276,7 @@ const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, componen
|
|
|
270
276
|
setFullFormState(prev => (Object.assign(Object.assign({}, prev), { [stepKey]: data })));
|
|
271
277
|
if (isLastStep) {
|
|
272
278
|
//? This state is not updated for the submit handler below; therefore, there is duplication.
|
|
273
|
-
submitHandlerInternal === null || submitHandlerInternal === void 0 ? void 0 : submitHandlerInternal(Object.assign(Object.assign({}, fullFormState), { [stepKey]: data }));
|
|
279
|
+
yield (submitHandlerInternal === null || submitHandlerInternal === void 0 ? void 0 : submitHandlerInternal(Object.assign(Object.assign({}, fullFormState), { [stepKey]: data })));
|
|
274
280
|
}
|
|
275
281
|
else if (nextStepPath) {
|
|
276
282
|
navigate({ to: basePath, params: { stepPath: nextStepPath } });
|
|
@@ -294,7 +300,8 @@ const FormWizardStepWrapper = ({ stepKey, onCancel, description, title, componen
|
|
|
294
300
|
}) }), jsx(FormWizardFooter, { stepForms,
|
|
295
301
|
onBack: previousStepPath ? () => navigate({ to: basePath, params: { stepPath: previousStepPath } }) : null,
|
|
296
302
|
primaryActionLabel: isLastStep ? lastStepPrimaryActionLabel : undefined,
|
|
297
|
-
onCancel: handleCancel
|
|
303
|
+
onCancel: handleCancel,
|
|
304
|
+
disablePrimaryAction: isSubmitting })] }, String(stepKey)));
|
|
298
305
|
};
|
|
299
306
|
|
|
300
307
|
const cvaFormWizardContainer = cvaMerge(["h-full"]);
|
|
@@ -321,7 +328,9 @@ const FormWizard = ({ lastStepPrimaryActionLabel, steps, fullFormSchema, onCance
|
|
|
321
328
|
// A possible workaround could be to type it Partial<<DeepPartial<...>>> everywhere
|
|
322
329
|
// Any weakening of the type in a union would be worse and likely just push the issue forward until it hits react-hook-form
|
|
323
330
|
// eslint-disable-next-line local-rules/no-typescript-assertion
|
|
324
|
-
|
|
331
|
+
objectFromEntries(objectEntries(steps)
|
|
332
|
+
.filter(([_, { defaultValues }]) => defaultValues)
|
|
333
|
+
.map(([key, { defaultValues }]) => [key, defaultValues])));
|
|
325
334
|
const currentVisibleStepKey = (_a = objectEntries(steps).find(([, { path }]) => subStepPath ? path === `${stepPath}/${subStepPath}` : path === stepPath)) === null || _a === void 0 ? void 0 : _a[0];
|
|
326
335
|
const currentVisibleStepIndex = objectKeys(steps).findIndex(key => key === currentVisibleStepKey);
|
|
327
336
|
return (jsx("div", { className: cvaFormWizardContainer({ className }), "data-testid": dataTestId, children: jsxs("div", { className: cvaFormWizardLayout(), children: [jsx(FormWizardSidebar, { children: objectEntries(steps)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { RegisteredRouter, RouteIds } from "@tanstack/react-router";
|
|
2
2
|
import { CommonProps } from "@trackunit/react-components";
|
|
3
3
|
import { ReactNode } from "react";
|
|
4
|
-
import { UseFormReturn } from "react-hook-form";
|
|
4
|
+
import { DeepPartial, UseFormReturn } from "react-hook-form";
|
|
5
5
|
import { AnyZodObject, SomeZodObject, z } from "zod";
|
|
6
6
|
export type StepState = "active" | "done" | "invalid" | "disabled" | "ready";
|
|
7
7
|
export type StepDefinitions<TFullSchema extends AnyZodObject> = {
|
|
@@ -22,6 +22,7 @@ interface DefinedStep<TStepSchema extends TFullSchemaValue[keyof TFullSchemaValu
|
|
|
22
22
|
component: (props: StepComponentPropsInner<TStepSchema, TFullSchemaValue>) => React.ReactNode;
|
|
23
23
|
parent?: keyof TFullSchemaValue;
|
|
24
24
|
shouldRender?: (fullFormState: Partial<TFullSchemaValue>) => boolean;
|
|
25
|
+
defaultValues?: DeepPartial<TStepSchema>;
|
|
25
26
|
}
|
|
26
27
|
export type StepComponentProps<TFullSchemaHook extends () => AnyZodObject, TStepKey extends keyof z.TypeOf<ReturnType<TFullSchemaHook>>> = StepComponentPropsInner<z.TypeOf<ReturnType<TFullSchemaHook>>[TStepKey], z.TypeOf<ReturnType<TFullSchemaHook>>>;
|
|
27
28
|
export type ComponentStepDefinitions<TFullSchema extends AnyZodObject> = {
|
|
@@ -4,10 +4,11 @@ interface FormWizardFooterProps extends CommonProps {
|
|
|
4
4
|
primaryActionLabel?: string;
|
|
5
5
|
onCancel: () => void;
|
|
6
6
|
onBack: (() => void) | null;
|
|
7
|
+
disablePrimaryAction?: boolean;
|
|
7
8
|
}
|
|
8
9
|
/**
|
|
9
10
|
* The footer component for the FormWizard.
|
|
10
11
|
* In most cases you should not be using this component directly, but rather use the FormWizard component.
|
|
11
12
|
*/
|
|
12
|
-
export declare const FormWizardFooter: ({ onBack, onCancel, primaryActionLabel }: FormWizardFooterProps) => JSX.Element;
|
|
13
|
+
export declare const FormWizardFooter: ({ disablePrimaryAction, onBack, onCancel, primaryActionLabel, }: FormWizardFooterProps) => JSX.Element;
|
|
13
14
|
export {};
|