Skip to main content

Wizard

warning

Metadata usage is deprecated. While you can still define the Wizard-related components using metadata, we recommend that you use JSX instead.

Examples

Wizard example

This example shows a simple Wizard component with the following features:

  • Three different tabs
  • Navigation using Next and Previous buttons
  • Validation of data before navigating away from the first page
  • A cancel option that redirects the user to the second page
  • A simple progress bar

The following sections explain the different steps to define a basic Wizard using the components from the @jutro/wizard-next package.

The Wizard implementation requires:

  1. Definition of the different Wizard pages that can be displayed
  2. Definition of the Wizard layout: how the wizard is displayed, including breakpoint alternatives
  3. Definition of the custom elements like the action bar and progress indicator
  4. Definition of the Wizard steps: routes and alternatives, for example where the user is directed after completing a step
  5. The composition of the Wizard by using all the previously defined elements

1. Define the wizard pages

Defining a wrapper component is the recommended approach for the Wizard pages definition. Each Wizard step must reference a component that either wraps or extends WizardPage.

The following example shows how to create the page components for the above Wizard.


import { useState } from 'react';
import { WizardPage } from '@jutro/wizard-next';
import { Checkbox } from '@jutro/components';
import { useLocation } from 'react-router-dom';


const TheFirstPage = ({id, wizardPageProps}) => {
const location = useLocation();
const [valid, setValid] = useState(false);
const [stateMessages, setStateMessages] = useState(null);
const firstStepCheckboxChanged = (e, value) => {
setValid(value);
}
const wizardPageOnNext = () => {
if (!valid) {
setStateMessages({ error: ["Select this checkbox to go to the next page"] });
}
// if false is returned, the default next event does not continue
return valid;
}

return (
<WizardPage
id={id}
{...wizardPageProps}
onNext={wizardPageOnNext}
location={location}
title="First page">
<span>Here you can add your content</span>
<Checkbox
label="This is mandatory to continue"
required={true}
stateMessages={stateMessages}
onChange={firstStepCheckboxChanged}
/>
</WizardPage>
);
};

const AnotherWizardPage = ({ id, wizardPageProps }) => {
const location = useLocation();
return (
<WizardPage
id={id}
{...wizardPageProps}
location={location}
title="Second page">
<span>Another wizard page</span>
</WizardPage>
);
};

const LastWizardPage = ({ id, wizardPageProps }) => {
const location = useLocation();
return (
<WizardPage
id={id}
{...wizardPageProps}
location={location}
title="The last page">
<span>The last wizard page</span>
</WizardPage>
);
};

Apart from the page definition, the example has a validation mechanism implemented for the first page which, in case of invalid data, prevents the transition to the next page.

2. Define the layout

Define the Wizard page structure for the different breakpoints.

const layout = {
desktop: { colStart: 1, colSpan: 12 },
tablet: { colStart: 1, colSpan: 10 },
phone: { colStart: 1, colSpan: 6 },
}

3. Define any custom elements

In this example, we are using components from the base configuration. See the Customizing the progress bar and Customizing the action bar sections for custom implementation references.

4. Define the wizard steps

You must specify the corresponding route and the assigned WizardPage component for each step.


const steps = [
{
id: 'wizard.firstpage',
route: 'firstpage',
title: 'First page',
component: TheFirstPage,
},
{
id: 'wizard.secondpage',
route: 'secondpage',
title: 'Second page',
component: AnotherWizardPage,
},
{
id: 'wizard.lastpage',
route: 'lastpage',
title: 'Last page',
component: LastWizardPage,
}
]

5. Compose the wizard

Bring all the elements defined so far together to compose the wizard.

The Cancel option is added by defining a cancelPath property, while the Next and Previous buttons use the steps definition order by default.

import { Wizard } from '@jutro/wizard-next';
import { useLocation } from 'react-router-dom';


<Wizard
layout={layout}
baseRoute={'/'}
basePath={'/'}
location={useLocation}
cancelPath="secondpage"
steps={steps}
/>

Customizing the progress bar

Use renderProgressBar to turn off the progress bar, or render a custom progress bar.

  • To turn off the progress bar, set renderProgressBar to null or false.
  • To customize the progress bar, set renderProgressBarto a render prop.

This render prop expects a function that receives one parameter, an object with the basePath prop and an array of the steps which are both passed to the Wizard.

The following is an example of a custom progress bar made using the layout and steps defined in the previous examples:

import { useLocation } from 'react-router-dom';
import { Wizard } from '@jutro/wizard-next'
import { StepProgressBar } from '@jutro/components';


const customProgressBar = ({ basePath, steps }) => {
const { pathname } = useLocation(); // calc if the subroute a match
const stepRoutes = steps.map((step) => step?.route);
const currentStep = stepRoutes.indexOf(pathname.replace(`${basePath}/`, ''));

const barSteps = steps.map((step) => {
step.active = false;
return step;
});
if (currentStep >= 0) barSteps[currentStep].active = true;
return <StepProgressBar steps={barSteps} />;
};

<Wizard
layout={layout}
baseRoute={'/'}
basePath={'/'}
location={useLocation}
cancelPath="firstpage"
steps={steps}
renderProgressBar={customProgressBar}
/>

For more information about render props, see the React docs.

Customizing the action bar

Use renderActionBar to disable the action bar, or render a custom action bar.

  • To disable the action bar, set renderActionBar to null or false.
  • To customize the action bar, set renderActionBar to a render prop.

The render prop is a function that receives two parameters:

  1. resolvedPropsForButtons - this includes route paths which are calculated by the Wizard based on the flow and the current step.
  2. actionBarLayout - the same prop passed into Wizard to set the layout of the action bar. If you are using a custom action bar, you can use this prop in any way you want, as it is passed directly through the Wizard to the render prop function.

An example of a custom action bar, using the steps and layout from previous examples would be:


export const WizardWithCustomActionBar = () => {
const location = useLocation();
const history = useHistory();

const customActionBar = (resolvedPropsForButtons, actionBarLayout) => {
const selectedFlexDirection = actionBarLayout === 'column' ? 'column' : 'row';
return (
<div
style={{
border: 'solid 1px yellow',
display: 'flex',
flexDirection: selectedFlexDirection,
}}
>
{resolvedPropsForButtons.map(propForButton => (
(("next" == propForButton.name || "previous" == propForButton.name) && propForButton.to != undefined ?
<Button
label={propForButton.name}
fullWidth={true}
onClick = {(e, to:propForButton.to) => handleNavigation(e , to)}
/>
:"")))}
</div>)
};

const handleNavigation = (event, to) => {
history.replace(`//${to}`);
}


return (
<Wizard
layout={layout}
baseRoute={'/'}
basePath={'/'}
location={location}
steps={steps}
renderActionBar={customActionBar}
/>
);
};

We have removed the validation from this example, but you can add it in the handleNavigation function.