-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Forms): add
Form.useSnapshot
hook to handle snapshots of data
- Loading branch information
1 parent
e36c224
commit 3847a31
Showing
8 changed files
with
350 additions
and
7 deletions.
There are no files selected for viewing
23 changes: 23 additions & 0 deletions
23
...s/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useSnapshot.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
--- | ||
title: 'useSnapshot' | ||
description: '`Form.useSnapshot` lets you store data snapshots of your form data, either inside or outside of the form context.' | ||
showTabs: true | ||
tabs: | ||
- title: Info | ||
key: '/info' | ||
- title: Demos | ||
key: '/demos' | ||
breadcrumb: | ||
- text: Forms | ||
href: /uilib/extensions/forms/ | ||
- text: Form | ||
href: /uilib/extensions/forms/Form/ | ||
- text: Form.useSnapshot | ||
href: /uilib/extensions/forms/Form/useSnapshot/ | ||
--- | ||
|
||
import Info from 'Docs/uilib/extensions/forms/Form/useSnapshot/info' | ||
import Demos from 'Docs/uilib/extensions/forms/Form/useSnapshot/demos' | ||
|
||
<Info /> | ||
<Demos /> |
100 changes: 100 additions & 0 deletions
100
...es/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useSnapshot/Examples.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import ComponentBox from '../../../../../../shared/tags/ComponentBox' | ||
import { Field, Form, Wizard } from '@dnb/eufemia/src/extensions/forms' | ||
|
||
export const InWizard = () => { | ||
return ( | ||
<ComponentBox> | ||
{() => { | ||
const MyForm = () => { | ||
const { createSnapshot, revertSnapshot } = | ||
Form.useSnapshot('my-form') | ||
|
||
return ( | ||
<Form.Handler | ||
id="my-form" | ||
defaultData={{ activeSteps: 'group-1' }} | ||
> | ||
<Wizard.Container | ||
onStepChange={(index, mode, args) => { | ||
if (mode !== 'previous') { | ||
createSnapshot(args.id) | ||
} | ||
}} | ||
onBeforeStepChange={(index, mode, args) => { | ||
if (mode === 'previous') { | ||
revertSnapshot(args.id) | ||
} | ||
}} | ||
> | ||
<Wizard.Step | ||
title="Step A" | ||
id="step-a" | ||
activeWhen={{ | ||
path: '/activeSteps', | ||
hasValue: 'group-1', | ||
}} | ||
> | ||
<Form.MainHeading>Step A</Form.MainHeading> | ||
<Field.String path="/foo" label="Content" /> | ||
<Wizard.Buttons /> | ||
</Wizard.Step> | ||
|
||
<Wizard.Step | ||
title="Step B" | ||
id="step-b" | ||
activeWhen={{ | ||
path: '/activeSteps', | ||
hasValue: 'group-1', | ||
}} | ||
> | ||
<Form.MainHeading>Step B</Form.MainHeading> | ||
<Field.String path="/foo" label="Content" /> | ||
<Wizard.Buttons /> | ||
</Wizard.Step> | ||
|
||
<Wizard.Step | ||
title="Step C" | ||
id="step-c" | ||
activeWhen={{ | ||
path: '/activeSteps', | ||
hasValue: (value: string) => | ||
['group-1', 'group-2'].includes(value), | ||
}} | ||
> | ||
<Form.MainHeading>Step C</Form.MainHeading> | ||
<Field.String path="/foo" label="Content" /> | ||
<Wizard.Buttons /> | ||
</Wizard.Step> | ||
|
||
<Wizard.Step | ||
title="Step D" | ||
id="step-d" | ||
activeWhen={{ | ||
path: '/activeSteps', | ||
hasValue: 'group-2', | ||
}} | ||
> | ||
<Form.MainHeading>Step D</Form.MainHeading> | ||
<Field.String path="/foo" label="Content" /> | ||
<Wizard.Buttons /> | ||
</Wizard.Step> | ||
</Wizard.Container> | ||
|
||
<Field.Selection | ||
path="/activeSteps" | ||
variant="button" | ||
optionsLayout="horizontal" | ||
top | ||
> | ||
<Field.Option value="group-1" title="Group 1" /> | ||
<Field.Option value="group-2" title="Group 2" /> | ||
</Field.Selection> | ||
</Form.Handler> | ||
) | ||
} | ||
|
||
return <MyForm></MyForm> | ||
}} | ||
</ComponentBox> | ||
) | ||
} |
13 changes: 13 additions & 0 deletions
13
...design-system-portal/src/docs/uilib/extensions/forms/Form/useSnapshot/demos.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
--- | ||
showTabs: true | ||
--- | ||
|
||
import * as Examples from './Examples' | ||
|
||
## Demos | ||
|
||
### Used in a Wizard | ||
|
||
This example revers the form data to its previous state when the user navigates back to a previous step. | ||
|
||
<Examples.InWizard /> |
75 changes: 75 additions & 0 deletions
75
...-design-system-portal/src/docs/uilib/extensions/forms/Form/useSnapshot/info.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
--- | ||
showTabs: true | ||
--- | ||
|
||
## Description | ||
|
||
The `Form.useSnapshot` hook lets you store data snapshots of your form data, either inside or outside of the form context. | ||
|
||
The hook returns an object with the following properties: | ||
|
||
```tsx | ||
import { Form } from '@dnb/eufemia/extensions/forms' | ||
|
||
function MyComponent() { | ||
const { createSnapshot, revertSnapshot } = Form.useSnapshot() | ||
|
||
return <>MyComponent</> | ||
} | ||
|
||
render( | ||
<Form.Handler> | ||
<MyComponent /> | ||
</Form.Handler>, | ||
) | ||
``` | ||
|
||
- `createSnapshot` will store the current data as a new snapshot with the given id. | ||
- `revertSnapshot` will revert the data to the snapshot with the given id (required). A reverted snapshot gets deleted from the memory. | ||
|
||
## Usage | ||
|
||
You can use the `Form.useSnapshot` hook with or without an `id` (string) property, which is optional and can be used to link the data to a specific [Form.Handler](/uilib/extensions/forms/Form/Handler/) component. | ||
|
||
### Without an `id` property | ||
|
||
Here "Component" is rendered inside the `Form.Handler` component and does not need an `id` property to access the form data: | ||
|
||
```jsx | ||
import { Form } from '@dnb/eufemia/extensions/forms' | ||
|
||
function MyForm() { | ||
return ( | ||
<Form.Handler> | ||
<Component /> | ||
</Form.Handler> | ||
) | ||
} | ||
|
||
function Component() { | ||
const { createSnapshot, revertSnapshot } = Form.useSnapshot() | ||
} | ||
``` | ||
|
||
### With an `id` property | ||
|
||
While in this example, "Component" is outside the `Form.Handler` context, but linked together via the `id` (string) property: | ||
|
||
```jsx | ||
import { Form } from '@dnb/eufemia/extensions/forms' | ||
|
||
function MyForm() { | ||
return ( | ||
<> | ||
<Form.Handler id="unique">...</Form.Handler> | ||
<Component /> | ||
</> | ||
) | ||
} | ||
|
||
function Component() { | ||
const { createSnapshot, revertSnapshot } = Form.useSnapshot('unique') | ||
} | ||
``` | ||
|
||
This is beneficial when you need to utilize the form data in other places within your application. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
68 changes: 68 additions & 0 deletions
68
packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useSnapshot.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { renderHook, act } from '@testing-library/react' | ||
import useSnapshot from '../useSnapshot' | ||
|
||
let mockData: any | ||
const mockSet = jest.fn() | ||
|
||
beforeEach(() => { | ||
mockData = { value: 'initial' } | ||
mockSet.mockClear() | ||
}) | ||
|
||
afterEach(() => { | ||
jest.restoreAllMocks() | ||
}) | ||
|
||
jest.mock('../../Form/data-context/useData', () => { | ||
const useDataMock = jest.fn(() => ({ | ||
data: mockData, | ||
set: mockSet, | ||
})) | ||
return useDataMock | ||
}) | ||
|
||
describe('Form.useSnapshot', () => { | ||
it('creates a snapshot and retrieves it correctly', () => { | ||
const { result } = renderHook(useSnapshot) | ||
let snapshotId: string | ||
|
||
act(() => { | ||
snapshotId = result.current.createSnapshot() | ||
}) | ||
|
||
expect(result.current.createSnapshot).toBeDefined() | ||
expect(result.current.revertSnapshot).toBeDefined() | ||
expect(snapshotId).toBeTruthy() | ||
}) | ||
|
||
it('reverts to a snapshot', () => { | ||
const { result } = renderHook(useSnapshot) | ||
act(() => { | ||
const snapshotId = result.current.createSnapshot(undefined, { | ||
value: 'modified', | ||
}) | ||
mockSet.mockClear() | ||
result.current.revertSnapshot(snapshotId) | ||
}) | ||
|
||
expect(mockSet).toHaveBeenCalledWith({ value: 'modified' }) | ||
}) | ||
|
||
it('reverts and deletes the snapshot', () => { | ||
const { result } = renderHook(useSnapshot) | ||
let snapshotId: string | ||
|
||
act(() => { | ||
snapshotId = result.current.createSnapshot() | ||
result.current.revertSnapshot(snapshotId) | ||
}) | ||
|
||
expect(mockSet).toHaveBeenCalledWith(mockData) | ||
|
||
mockSet.mockClear() | ||
act(() => { | ||
result.current.revertSnapshot(snapshotId) | ||
}) | ||
expect(mockSet).not.toHaveBeenCalled() | ||
}) | ||
}) |
Oops, something went wrong.