Demos#
Basic usage#
const initialData = { firstName: 'John', lastName: 'Doe', streetName: 'Osloveien', streetNr: 12, postalCode: '1234', city: 'Oslo', } const Step1 = () => ( <Wizard.Step aria-label="Basic wizard: Step 1" 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 aria-label="Basic wizard: Step 2" 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.useTranslation().Step return ( <Wizard.Step aria-label={`Basic wizard: ${summaryTitle}`} 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}> <Provider StepIndicator={{ overviewTitle: 'Basic wizard steps', }} > <Wizard.Container id="my-wizard" onStepChange={onStepChange}> <Step1 /> <Step2 /> <Summary /> </Wizard.Container> </Provider> </Form.Handler> ) } render(<MyForm />)
Async wizard#
const MyForm = () => { const onStepChange = 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 instanceof Error ? error : new Error(String(error)) } } // Optional, you can show a FormStatus at the bottom of the form return { info: `Info message: ${index}`, } }, []) const onSubmit = useCallback(async (data) => { console.log('onSubmit', data) try { const request = createRequest() await request(1000) // Simulate a request } catch (error) { return error instanceof Error ? error : new Error(String(error)) } // Optional, you can show a FormStatus at the bottom of the form return { warning: 'Warning message', } }, []) const validator = useCallback(async (value) => { try { const request = createRequest() await request(1000) // Simulate a request } catch (error) { return error instanceof Error ? error : new Error(String(error)) } if (value === 'invalid') { return Error('Error message') } }, []) const validator1 = debounceAsync(validator) const validator2 = debounceAsync(validator) const Step1 = () => { return ( <Wizard.Step aria-label="Asynchronous wizard: Step 1" 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 aria-label="Asynchronous wizard: Step 2" 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}> <Provider StepIndicator={{ overviewTitle: 'Asynchronous wizard steps', }} > <Wizard.Container onStepChange={onStepChange}> <Step1 /> <Step2 /> </Wizard.Container> </Provider> </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.
<Form.Handler onSubmit={(data) => { console.log('onSubmit', data) }} > <Provider StepIndicator={{ overviewTitle: 'Wizard menu status steps', }} > <Wizard.Container onStepChange={async (index, mode) => { console.log('onStepChange', index, mode) }} mode="loose" initialActiveIndex={2} > <Wizard.Step aria-label="Wizard menu status: Step 1" title="Step 1"> <Field.String label="Step 1" path="/step1" required /> <Wizard.Buttons /> </Wizard.Step> <Wizard.Step aria-label="Wizard menu status: Step 2" title="Step 2"> <Field.String label="Step 2" path="/step2" required /> <Wizard.Buttons /> </Wizard.Step> <Wizard.Step aria-label="Wizard menu status: Step 3" title="Step 3"> <Field.String label="Step 3" path="/step3" /> <Wizard.Buttons /> </Wizard.Step> </Wizard.Container> </Provider> <Form.SubmitButton /> </Form.Handler>
With StatusMessage#
<Form.Handler> <Provider StepIndicator={{ overviewTitle: 'Wizard status message steps', }} > <Wizard.Container onStepChange={async (index, mode, { preventNavigation }) => { preventNavigation() return { info: 'Info message.', warning: 'Warning message.', } }} > <Wizard.Step aria-label="Wizard status message: Step 1" 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> </Provider> </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)})}
<Form.Handler onSubmitRequest={({ getErrors }) => { getErrors().forEach(({ label, error }) => { console.log(label, error.message) }) }} > <Provider StepIndicator={{ overviewTitle: 'Wizard submit request steps', }} > <Wizard.Container mode="loose"> <Wizard.Step aria-label="Wizard submit request: Step 1" 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 aria-label="Wizard submit request: Step 2" title="Step 2" > <Form.Card> <Field.String path="/baz" label="Baz" required /> </Form.Card> <Wizard.Buttons /> <Form.SubmitButton /> </Wizard.Step> </Wizard.Container> </Provider> </Form.Handler>