Demos
Basic usage
Code Editor
const initialData = { firstName: 'John', lastName: 'Doe', streetName: 'Osloveien', streetNr: 12, postalCode: '1234', city: 'Oslo', } const Step1 = () => ( <Wizard.Step title="Step 1"> <Form.MainHeading>Heading</Form.MainHeading> <Form.Card> <P>Contents</P> </Form.Card> <Form.Card> <P>Contents</P> </Form.Card> <Wizard.Buttons /> </Wizard.Step> ) const Step2 = () => ( <Wizard.Step title="Step 2"> <Form.MainHeading>Heading</Form.MainHeading> <Form.Card> <P>Contents</P> </Form.Card> <Form.Card> <P>Contents</P> </Form.Card> <Wizard.Buttons /> </Wizard.Step> ) const Summary = () => { const { summaryTitle } = Form.useLocale().Step return ( <Wizard.Step title={summaryTitle}> <Form.MainHeading>Summary</Form.MainHeading> <Form.Card> <Form.SubHeading>Deliver address</Form.SubHeading> <Value.SummaryList layout="grid"> <Value.Name.First path="/firstName" /> <Value.Name.Last path="/lastName" /> <Value.Composition label="Street"> <Value.String path="/streetName" /> <Value.Number path="/streetNr" /> </Value.Composition> <Value.Composition label="City"> <Value.String path="/postalCode" /> <Value.String path="/city" /> </Value.Composition> </Value.SummaryList> <Wizard.EditButton toStep={1} /> </Form.Card> <Form.ButtonRow> <Wizard.Buttons /> <Form.SubmitButton variant="send" /> </Form.ButtonRow> </Wizard.Step> ) } // Can be an async function, in case you need to make some async stuff const onStepChange = async (step, mode) => { if (mode === 'next') { await new Promise((resolve) => setTimeout(resolve, 1000)) } console.log('onStepChange', step, mode) } // Can be an async function, in case you need to make some async stuff const onSubmit = async (data) => { await new Promise((resolve) => setTimeout(resolve, 2000)) console.log('onSubmit', data) } const MyForm = () => { // Routers like "react-router" are supported as well Wizard.useQueryLocator('my-wizard') return ( <Form.Handler data={initialData} onSubmit={onSubmit}> <Wizard.Container id="my-wizard" onStepChange={onStepChange}> <Step1 /> <Step2 /> <Summary /> </Wizard.Container> </Form.Handler> ) } render(<MyForm />)
Async wizard
Code Editor
const MyForm = () => { const onStepChange = React.useCallback(async (index, mode) => { console.log('onStepChange', index) if (mode === 'next') { try { const request = createRequest() await request(1000) // Simulate a request } catch (error) { return error } } // Optional, you can show a FormStatus at the bottom of the form return { info: `Info message: ${index}`, } }, []) const onSubmit = React.useCallback(async (data) => { console.log('onSubmit', data) try { const request = createRequest() await request(1000) // Simulate a request } catch (error) { return error } // Optional, you can show a FormStatus at the bottom of the form return { warning: 'Warning message', } }, []) const validator = React.useCallback(async (value) => { try { const request = createRequest() await request(1000) // Simulate a request } catch (error) { return error } if (value === 'invalid') { return Error('Error message') } }, []) const validator1 = debounceAsync(validator) const validator2 = debounceAsync(validator) const Step1 = () => { return ( <Wizard.Step title="Step 1"> <Form.Card> <Field.String label="Required field with async validator" onChangeValidator={validator1} path="/field1" required /> <Field.String label="Field with async validator" onChangeValidator={validator2} path="/field2" /> </Form.Card> <Wizard.Buttons /> </Wizard.Step> ) } const Step2 = () => { return ( <Wizard.Step title="Step 2"> <Form.MainHeading>Heading</Form.MainHeading> <Form.Card> <P>Contents of step 2</P> </Form.Card> <Form.ButtonRow> <Wizard.Buttons /> <Form.SubmitButton variant="send" /> </Form.ButtonRow> </Wizard.Step> ) } return ( <Form.Handler onSubmit={onSubmit}> <Wizard.Container onStepChange={onStepChange}> <Step1 /> <Step2 /> </Wizard.Container> </Form.Handler> ) } render(<MyForm />)
With StatusMessage in Menu
This example uses the loose mode to demonstrate status messages. Press the Send button to see the status message. You may also navigate to the previous steps and press the Send button again.
Code Editor
<Form.Handler onSubmit={(data) => { console.log('onSubmit', data) }} > <Wizard.Container onStepChange={async (index, mode) => { console.log('onStepChange', index, mode) }} mode="loose" initialActiveIndex={2} > <Wizard.Step title="Step 1"> <Field.String label="Step 1" path="/step1" required /> <Wizard.Buttons /> </Wizard.Step> <Wizard.Step title="Step 2"> <Field.String label="Step 2" path="/step2" required /> <Wizard.Buttons /> </Wizard.Step> <Wizard.Step title="Step 3"> <Field.String label="Step 3" path="/step3" /> <Wizard.Buttons /> </Wizard.Step> </Wizard.Container> <Form.SubmitButton /> </Form.Handler>
With StatusMessage
Code Editor
<Form.Handler> <Wizard.Container onStepChange={async (index, mode, { preventNavigation }) => { preventNavigation() return { info: 'Info message.', warning: 'Warning message.', } }} > <Wizard.Step title="Step 1"> <Form.MainHeading>Step 1</Form.MainHeading> <P>Content</P> <Wizard.NextButton text="Press me to see the status message" /> </Wizard.Step> </Wizard.Container> </Form.Handler>
Get errors before submit or step change
You can use the onSubmitRequest property on the Form.Handler to get visible errors before the form is submitted.
Each item in the error array contains the following properties in an object:
pathThe path of the field.valueThe value of the field.displayValueThe displayed value of the field.labelThe label of the field.propsThe given field properties.errorThe error of the field.
const onSubmitRequest: OnSubmitRequest = ({ getErrors }) => {getErrors().forEach(({ path, value, displayValue, label, props, error }) => {// Do something with the errorconsole.log(label, error.message)},)}
Code Editor
<Form.Handler onSubmitRequest={({ getErrors }) => { getErrors().forEach(({ label, error }) => { console.log(label, error.message) }) }} > <Wizard.Container mode="loose" variant="drawer"> <Wizard.Step title="Step 1"> <Form.Card> <Field.String path="/foo" label="Foo" defaultValue="With default value" required /> <Field.String path="/bar" label="Bar" required /> </Form.Card> <Wizard.Buttons /> </Wizard.Step> <Wizard.Step title="Step 2"> <Form.Card> <Field.String path="/baz" label="Baz" required /> </Form.Card> <Wizard.Buttons /> <Form.SubmitButton /> </Wizard.Step> </Wizard.Container> </Form.Handler>
Outset and layout (Card.Provider)
The wizard navigation area (StepIndicator) will "outset" (break out) from the layout using negative margins.
This outset is turned off if the Wizard.Container is placed inside a <Card>, but if placed in a different wrapper that messes with the layout, you can manually turn it off in two ways:
- Wrap the Wizard.Container in
<Form.Card.Provider disableCardBreakout>to make all nested cards act like they’re inside a<Card>. - Or set
outset={false}on each card.
See the Form.Card "Outset" example for more details.
Code Editor
<CustomContainerWithPadding> <Card.Provider disableCardBreakout> <Form.Handler> <Wizard.Container mode="loose"> <Wizard.Step title="Step 1"> <Form.Card> <Field.String label="Step 1" path="/step1" required /> <Wizard.Buttons /> </Form.Card> </Wizard.Step> <Wizard.Step title="Step 2"> <Form.Card> <Field.String label="Step 2" path="/step2" required /> <Wizard.Buttons /> </Form.Card> </Wizard.Step> <Wizard.Step title="Step 3"> <Form.Card> <Field.String label="Step 3" path="/step3" /> <Wizard.Buttons /> <Form.SubmitButton /> </Form.Card> </Wizard.Step> </Wizard.Container> </Form.Handler> </Card.Provider> </CustomContainerWithPadding>