Skip to content

Import#

import { Form } from '@dnb/eufemia/extensions/forms'
render(<Form.Section.EditContainer />)

Description#

Form.Section.EditContainer enables users to toggle (with animation) the content of each item between the Form.Section.ViewContainer and this edit container.

By default, it features a toolbar containing a "Done" button and a "Cancel" button. The "Cancel" button resets any changes made to the item content, restoring it to its original state.

Use preventUncommittedChanges when users must confirm the section before submitting the form or continuing to the next Wizard step. Navigation is blocked while the section is in edit mode, until the user selects "Done" or "Cancel". The parent Form.Section must have a path.

import { Form, Field, Value } from '@dnb/eufemia/extensions/forms'
render(
<Form.Section>
<Form.Section.EditContainer title="Edit account holder">
<Field.Name.First path="/firstName" />
<Field.Name.Last path="/lastName" />
</Form.Section.EditContainer>
<Form.Section.ViewContainer title="Account holder">
<Value.SummaryList>
<Value.Name.First path="/firstName" />
<Value.Name.Last path="/lastName" />
</Value.SummaryList>
</Form.Section.ViewContainer>
</Form.Section>
)

Customize the Toolbar#

import { Form, Field } from '@dnb/eufemia/extensions/forms'
render(
<Form.Section>
<Form.Section.EditContainer>
<Field.Name.Last itemPath="/name" />
<Form.Section.Toolbar>
<Form.Section.EditContainer.DoneButton />
<Form.Section.EditContainer.CancelButton />
</Form.Section.Toolbar>
</Form.Section.EditContainer>
</Form.Section>
)

Accessibility#

The EditContainer component has an aria-label attribute, which is set to the title property value. It uses a section element to wrap the content, which helps users with screen readers to get the needed announcement.

When the edit container becomes active, it will automatically receive the active element focus. And when the edit container switches to the view container, the focus will be set to the view container.

Demos#

View and edit container#

This demo shows the edit container opened by default by using the containerMode="edit" property.

Your account


const MyEditContainer = () => {
  return (
    <Form.Section.EditContainer>
      <Field.Name.First path="/firstName" />
      <Field.Name.Last path="/lastName" />
    </Form.Section.EditContainer>
  )
}
const MyViewContainer = () => {
  return (
    <Form.Section.ViewContainer>
      <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',
      },
    }}
  >
    <Form.Card>
      <Form.SubHeading>Your account</Form.SubHeading>
      <Form.Section path="/nestedPath" required containerMode="edit">
        <MyEditContainer />
        <MyViewContainer />
      </Form.Section>
    </Form.Card>
    <Form.SubmitButton />
  </Form.Handler>
)

Prevent uncommitted changes#

With preventUncommittedChanges, the user must select "Done" or "Cancel" before continuing to the next Wizard step, even when no values have changed.


<Form.Handler>
  <Wizard.Container>
    <Wizard.Step title="Profile">
      <Form.Section path="/profile" containerMode="edit">
        <Form.Section.EditContainer preventUncommittedChanges>
          <Field.Name.First path="/firstName" />
        </Form.Section.EditContainer>

        <Form.Section.ViewContainer>
          <Value.Name.First path="/firstName" />
        </Form.Section.ViewContainer>
      </Form.Section>

      <Wizard.Buttons />
    </Wizard.Step>

    <Wizard.Step title="Summary">
      <Value.Name.First path="/profile/firstName" />
      <Wizard.Buttons />
    </Wizard.Step>
  </Wizard.Container>
</Form.Handler>

Async onDone#

When onDone returns a Promise, the section stays in edit mode while it is pending. It switches to view mode when the Promise resolves and stays in edit mode when it rejects.

While the Promise is pending, the fields inside the section and its "Done" and "Cancel" buttons are disabled. This keeps the submitted values from changing while the save is in flight. If the Promise does not settle within asyncSubmitTimeout (30 seconds by default), the section re-enables itself and stays in edit mode, mirroring Form.Handler's submit behavior, so a save that never settles cannot leave the section stuck.

The demo starts with a simulated save error. Change the first name and select Done to verify that the input remains. Turn off Simulate save error and try again to complete the save.


const formId = 'async-on-done'
const Example = () => {
  const [error, setError] = useState<string>()
  const saveSection = async () => {
    setError(undefined)
    await new Promise((resolve) => setTimeout(resolve, 1500))
    const { getValue } = Form.getData(formId)
    if (getValue('/simulateError')) {
      setError('The section could not be saved. Try again.')
      throw new Error('The section could not be saved')
    }
  }
  return (
    <Form.Handler
      id={formId}
      defaultData={{
        profile: {
          firstName: 'Nora',
        },
        simulateError: true,
      }}
    >
      <Form.Card>
        <Form.Section path="/profile" containerMode="edit">
          <Form.Section.EditContainer onDone={saveSection}>
            <Field.Name.First path="/firstName" />
            <Field.Boolean
              path="//simulateError"
              label="Simulate save error"
              variant="button"
            />
            {error && <FormStatus>{error}</FormStatus>}
          </Form.Section.EditContainer>

          <Form.Section.ViewContainer>
            <Value.Name.First path="/firstName" />
          </Form.Section.ViewContainer>
        </Form.Section>
      </Form.Card>
    </Form.Handler>
  )
}
render(<Example />)
Suggest an edit