The Wizard package helps capture data in steps and provide ability to navigate between steps.
WizardProgressRope
The WizardProgressRope
is used to help the user add an entity through a guided wizard. This component only provides the item and rope UI, consumer is expected to provide the container UI with things like height, background color etc.
NOTE: Do not use this component for updating entities.
Usage
Usage
The snippet below demonstrates:
- Create a wizard schema with 2 steps, each having their own state
- Use useCreateWizard hook to create a wizard
- Render a wizard nav using the WizardProgress component
- Use the Wizard component and useWizard hook to render components for each step
// Shape of the data required by each step is defined by the consumertype StepMetaData = { title: string, complete: boolean };type Step1Type = StepMetaData & { step1State?: string };type Step2Type = StepMetaData & { step2State?: string };// Wizard schema is an object defined by the consumer with numbers as keys i.e Record<number, T>// Valid { 1: { a : string } }. Invalid { step1: { a : string } }type MyWizardSchema = { 1: Step1Type, 2: Step2Type,};const initialWizardState: MyWizardSchema = { 1: { title: 'Step 1', step1State: 'step 1 state', complete: false }, 2: { title: 'Step 2', step2State: 'step 2 state', complete: false },};// This is used to retrieve the correct wizard from context, also useful for serialization.const DEMO_WIZARD_ID = 'wizard-demo'// Create a wizard object with steps, which holds the state for each step. This object returns functions that allows us to manipulate the wizard state.// Initial state is recorded only once, use the functions return by this hook to manipulate state after that.const wizard = useCreateWizard({ id: DEMO_WIZARD_ID, initialState: initialWizardState, initialStep: 1, });const { stepsState, setWizardState, currentStep, setCurrentStep } = wizard<Stack gap="small"> <WizardProgress steps={{ 1: { title: stepsState[1].title, subtitle: 'Subtitle', action: { label: 'Reset', action: () => { setWizardState(() => initialWizardState); setCurrentStep(1); }, }, complete: stepsState[1].complete }, 2: { title: stepsState[2].title, complete: stepsState[2].complete }, }} currentStep={currentStep} /> {/* WizardProvider is a convenience component that provides wizard state to its children. */} <WizardProvider wizard={wizard}> <Step1 /> <Step2 /> </WizardProvider></Stack>;const Step1 = () => { {/* Returns an instance of the wizard from context based on the id provided. Must be used in children of WizardProvider component. */} const wizard = useWizard<MyWizardSchema>(DEMO_WIZARD_ID); if (!wizard || wizard.currentStep !== 1) return null; const { stepsState, nextStep } = wizard; const state = stepsState[1]; return ( <div> <div>{state.step1State}</div> <button onClick={nextStep}>Next</button> </div> );};const Step2 = () => { const wizard = useWizard<MyWizardSchema>(DEMO_WIZARD_ID); if (!wizard || wizard.currentStep !== 2) return null; const { stepsState, previousStep } = wizard; const state = stepsState[2]; return ( <div> <div>{state.step2State}</div> <button onClick={previousStep}>Previous</button> </div> );};
Resume from previous state
Below example demonstrates a flow where user has to leave the page to retrieve data from a 3rd party and then resume from where they left.
- User clicks on something to leave the page.
- You serialize the wizard state into local storage.
- User does stuff 3rd party stuff and returns to the page.
- You retrieve the wizard state from local storage using the wizard id and pass it as the initialState to the useCreateWizard hook.
Above example will be modified as:
// Grab wizard from local storage if possibleconst wizardFromLocalStorage = localStorage.getItem(`wizard:${DEMO_WIZARD_ID}`);// When resuming a wizard you want to make sure the data is in sync with the step you're on// This function should evaluate the wizard state and determine which step to resume fromconst initalStep = calculateCurrentStepFromWizardState(wizardFromLocalStorage);const wizard = useCreateWizard({ id: DEMO_WIZARD_ID, // If there's anything in your local storage it will use that as a starting point, otherwise it'll start from the beginning initialState: wizardFromLocalStorage || initialWizardState, initialStep: initalStep,});const Step2 = () => { const wizard = useWizard < MyWizardSchema > DEMO_WIZARD_ID; if (!wizard || wizard.currentStep !== 2) return null; const { stepsState } = wizard; const state = stepsState[2]; const goToThirdParty = () => { // We serialize the wizard state before leaving the page // Note that we only have to serialize the stepsState localStorage.setItem(`wizard:${DEMO_WIZARD_ID}`, stepsState); window.location.href = 'http://some3rdparty.com'; }; return ( <div> <div>{state.step2State}</div> <button onClick={goToThirdParty}>Leave page</button> </div> );};