Demos
Here are some examples and case demos (fullscreen) of how you can use the form components.
Base field components
Base field components are targeting the data type they produce. They can receive values and change handlers directly by properties.
<Form.Card> <Field.String label="Text field" value="Lorem Ipsum" onChange={(value) => console.log('onChange', value)} /> <Field.Number label="Number Field" value={789} onChange={(value) => console.log('onChange', value)} /> <Field.Boolean label="Boolean Field" value={true} onChange={(value) => console.log('onChange', value)} /> </Form.Card>
Feature fields
Feature fields build on top of base field components and provide standard properties for simplified form implementations.
<Form.Card> <Field.String label="Fornavn" value="John" /> <Field.String label="Etternavn" value="Smith" /> <Field.NationalIdentityNumber value="20058512345" /> <Field.Email value="john@smith.email" /> <Field.PhoneNumber value="+47 98765432" /> </Form.Card>
Layout components
Wrapping inputs in Flex.Stack and Form.Card with the stack property, provides the standard design without
the need for local styles.
Profile
Name
More information
Visibility based on data
Certain fields are displayed based on specific data requirements.
<Form.Handler defaultData={{ firstName: undefined, lastName: 'Smith', advanced: false, ssn: '123', email: '@smith.email', phone: '+47 98765432', }} onChange={(data) => console.log('onChange', data)} onPathChange={(path, value) => console.log('onPathChange', path, value)} onSubmit={(data) => console.log('onSubmit', data)} > <Flex.Stack> <Form.MainHeading>Profile</Form.MainHeading> <Form.Card> <Form.SubHeading>Name</Form.SubHeading> <Field.String path="/firstName" label="Fornavn" /> <Field.String path="/lastName" label="Etternavn" /> </Form.Card> <Field.Boolean path="/advanced" variant="checkbox-button" label="More fields" /> <Form.Visibility pathTrue="/advanced"> <Flex.Stack> <Form.Card> <Form.SubHeading>More information</Form.SubHeading> <Field.NationalIdentityNumber value="20058512345" /> <Field.Email value="john@smith.email" /> <Field.PhoneNumber value="+47 98765432" /> </Form.Card> </Flex.Stack> </Form.Visibility> </Flex.Stack> </Form.Handler>
Validation
Here are some examples of validation properties of field components.
<Form.Handler defaultData={{ firstName: undefined, lastName: 'Smith', ssn: '123', email: '@smith.email', phone: '+47 98765432', }} onChange={(data) => console.log('onChange', data)} onPathChange={(path, value) => console.log('onPathChange', path, value)} onSubmit={(data) => console.log('onSubmit', data)} > <Form.MainHeading>Profile</Form.MainHeading> <Form.Card> <Field.String path="/firstName" label="Fornavn" required /> <Field.String path="/lastName" label="Etternavn" required /> <Field.NationalIdentityNumber path="/ssn" validateInitially /> <Field.Email path="/email" validateInitially /> <Field.PhoneNumber path="/phone" validateInitially /> </Form.Card> </Form.Handler>
Using Form.Handler
Wrapping fields with a Form.Handler component lets them read and write data to one common data set, and have input and output of data in one place instead of connecting to every single field component.
<Form.Handler defaultData={{ firstName: 'John', lastName: 'Smith', ssn: '20058512345', email: 'john@smith.email', phone: '+47 98765432', }} onChange={(data) => console.log('onChange', data)} onPathChange={(path, value) => console.log('onPathChange', path, value)} onSubmit={(data) => console.log('onSubmit', data)} > <Form.MainHeading>Profile</Form.MainHeading> <Form.Card> <Field.String path="/firstName" label="Fornavn" /> <Field.String path="/lastName" label="Etternavn" /> <Field.NationalIdentityNumber path="/ssn" /> <Field.Email path="/email" /> <Field.PhoneNumber path="/phone" /> <Form.ButtonRow> <Form.SubmitButton /> </Form.ButtonRow> </Form.Card> </Form.Handler>
Using Wizard
With a Wizard component, you can create a wizard-like flow of steps.
function MyForm() { // Routers like "react-router" are supported as well Wizard.useQueryLocator('my-wizard') const { summaryTitle } = Form.useLocale().Step return ( <Form.Handler defaultData={{ firstName: undefined, lastName: 'Smith', advanced: false, ssn: '123', email: '@smith.email', phone: '+47 98765432', }} onChange={(data) => console.log('onChange', data)} onPathChange={(path, value) => console.log('onPathChange', path, value) } onSubmit={(data) => console.log('onSubmit', data)} > <Wizard.Container id="my-wizard" mode="loose"> <Wizard.Step title="Name"> <Form.MainHeading>Profile</Form.MainHeading> <Form.Card> <Form.SubHeading>Name</Form.SubHeading> <Field.String path="/firstName" label="Fornavn" required /> <Field.String path="/lastName" label="Etternavn" required /> </Form.Card> <Wizard.Buttons /> </Wizard.Step> <Wizard.Step title="More information"> <Form.MainHeading>Profile</Form.MainHeading> <Form.Card> <Form.SubHeading>More information</Form.SubHeading> <Field.NationalIdentityNumber path="/ssn" /> <Field.Email path="/email" /> <Field.PhoneNumber path="/phone" /> </Form.Card> <Wizard.Buttons /> </Wizard.Step> <Wizard.Step title={summaryTitle}> <Form.MainHeading>Profile</Form.MainHeading> <Form.Card> <Value.SummaryList layout="grid"> <Value.String path="/firstName" label="Fornavn" /> <Value.String path="/lastName" label="Etternavn" /> <Value.NationalIdentityNumber path="/ssn" /> <Value.Email path="/email" /> <Value.PhoneNumber path="/phone" /> </Value.SummaryList> </Form.Card> <Form.ButtonRow> <Wizard.Buttons /> <Form.SubmitButton /> </Form.ButtonRow> </Wizard.Step> </Wizard.Container> </Form.Handler> ) } render(<MyForm />)
Using a Form.Section
With a Form.Section component, you can create a section of fields and values that can be reused in different contexts. It also lets you define a container for the section, so you can easily switch between an edit and a view container.
const MyEditContainer = () => { return ( <Form.Section.EditContainer variant="basic"> <Field.Name.First path="/firstName" /> <Field.Name.Last path="/lastName" /> </Form.Section.EditContainer> ) } const MyViewContainer = () => { return ( <Form.Section.ViewContainer variant="basic"> <Value.SummaryList> <Value.Name.First path="/firstName" /> <Value.Name.Last path="/lastName" /> </Value.SummaryList> </Form.Section.ViewContainer> ) } render( <Form.Handler onSubmit={async (data) => console.log('onSubmit', data)} defaultData={{ nestedPath: { firstName: 'Nora', lastName: undefined, // initiate error }, }} > <Form.Card> <Form.SubHeading>Your account</Form.SubHeading> <Form.Section path="/nestedPath" required> <MyEditContainer /> <MyViewContainer /> </Form.Section> </Form.Card> <Form.SubmitButton /> </Form.Handler>, )
Iterate over repeated data
You can use the Iterate component to iterate over repeated data. It also lets you define animated containers, so you can easily switch between an edit and a view container.
const MyEditItemForm = () => { return ( <Field.Composition> <Field.Name.First itemPath="/firstName" width="medium" /> <Field.Name.Last itemPath="/lastName" width="medium" required /> </Field.Composition> ) } const MyEditItem = () => { return ( <Iterate.EditContainer title="Edit account holder {itemNo}" titleWhenNew="New account holder {itemNo}" > <MyEditItemForm /> </Iterate.EditContainer> ) } const MyViewItem = () => { const item = Iterate.useItem() console.log('index:', item.index) return ( <Iterate.ViewContainer title="Account holder {itemNo}"> <Value.SummaryList> <Value.Name.First itemPath="/firstName" showEmpty /> <Value.Name.Last itemPath="/lastName" placeholder="-" /> </Value.SummaryList> </Iterate.ViewContainer> ) } const CreateNewEntry = () => { return ( <Iterate.PushContainer path="/accounts" title="New account holder" openButton={ <Iterate.PushContainer.OpenButton text="Add another account" /> } showOpenButtonWhen={(list) => list.length > 0} > <MyEditItemForm /> </Iterate.PushContainer> ) } function MyForm() { return ( <Form.Handler data={{ accounts: [ { firstName: 'Tony', lastName: undefined, // initiate error }, ], }} onChange={(data) => console.log('DataContext/onChange', data)} onSubmit={async (data) => console.log('onSubmit', data)} > <Flex.Vertical> <Form.MainHeading>Accounts</Form.MainHeading> <Form.Card gap={false}> <Iterate.Array path="/accounts"> <MyViewItem /> <MyEditItem /> </Iterate.Array> <CreateNewEntry /> </Form.Card> <Form.SubmitButton variant="send" /> </Flex.Vertical> </Form.Handler> ) } render(<MyForm />)