Skip to content

Commit

Permalink
feat(Forms): add update method to Form.setData (#4416)
Browse files Browse the repository at this point in the history
Like the `update` method from useData, I think it would be befeifial to
have an update method to use "outside" of the React tree. Right now I
think it is logical to place in the setData handler. But any better
ideas are welcome.

```tsx
import { Form } from '@dnb/eufemia/extensions/forms'

const myFormId = 'unique-id' // or a function, object or React Context reference
const { update } = Form.setData(myFormId)

// Call "update" with the path and the new value.
update('/foo', 1)
```
  • Loading branch information
tujoworker authored Dec 18, 2024
1 parent 885ae0d commit d2f5c23
Show file tree
Hide file tree
Showing 9 changed files with 342 additions and 90 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react'
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
import { Button } from '@dnb/eufemia/src'
import { Form, Field } from '@dnb/eufemia/src/extensions/forms'

export function Default() {
Expand Down Expand Up @@ -41,3 +42,33 @@ export function AfterFirstRender() {
</ComponentBox>
)
}

export function UpdateValue() {
return (
<ComponentBox>
{() => {
const myFormId = {}
const { update } = Form.setData(myFormId)

const Component = () => {
return (
<Form.Card>
<Form.Handler id={myFormId}>
<Field.Number path="/foo" defaultValue={1} />
</Form.Handler>
<Button
onClick={() => {
update('/foo', (count) => count + 1)
}}
>
Update
</Button>
</Form.Card>
)
}

return <Component />
}}
</ComponentBox>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ import * as Examples from './Examples'
### Set data after first render

<Examples.AfterFirstRender />

### Using the update function

<Examples.UpdateValue />
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,57 @@ showTabs: true

With the `Form.setData` method, you can manage your form data outside of the form itself. This is beneficial when you need to utilize the form data in other places within your application:

Related helpers:

- [getData](/uilib/extensions/forms/Form/getData/)
- [useData](/uilib/extensions/forms/Form/useData/)

## Replace the whole data set

When a value is given to the `setData` function, the whole data set will be replaced.

```jsx
import { Form } from '@dnb/eufemia/extensions/forms'

const myFormId = 'unique-id' // or a function, object or React Context reference

Form.setData('unique', { foo: 'bar' })

function Component() {
return <Form.Handler id={myFormId}>...</Form.Handler>
function MyForm() {
return (
<Form.Handler id={myFormId}>
<Field.String path="/foo" />
</Form.Handler>
)
}
```

You link them together via the `id` (string, function, object or React Context as the reference) property.
## Update a single value

Related helpers:
You can use the `update` function to update the data.

- [getData](/uilib/extensions/forms/Form/getData/)
- [useData](/uilib/extensions/forms/Form/useData/)
```tsx
import { Form } from '@dnb/eufemia/extensions/forms'

const myFormId = 'unique-id' // or a function, object or React Context reference
const { update } = Form.setData(myFormId)

function MyForm() {
return (
<Form.Handler id={myFormId}>
<Field.Number path="/foo" defaultValue={0} />
</Form.Handler>
)
}

// Call "update" with the path and the new value.
update('/foo', 1)

// Or with a function that gives you the current value, if any.
update('/foo', (value) => {
if (typeof value === 'number') {
return value + 1
}
return 1
})
```
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ describe('getData', () => {

const { data } = getData<Data>(identifier)
expect(data).toEqual({ foo: 'bar' })
expect(data.foo satisfies string).toBe('bar')
})

it('should be able to get data from form after render', () => {
Expand Down Expand Up @@ -131,81 +132,85 @@ describe('getData', () => {
expect(getData(identifier).getValue('/does-not-exist')).toBeUndefined()
})

it('should provide filterData handler', () => {
type Data = { foo: string }

const filterDataHandler = jest.fn(({ props }) => {
if (props.disabled === true) {
return false
}
})

render(
<Form.Handler id={identifier}>
<Field.String path="/foo" value="foo" disabled />
<Field.String path="/bar" value="baz" />
</Form.Handler>
)

const { data, filterData } = getData<Data>(identifier)

expect(data).toEqual({
foo: 'foo',
bar: 'baz',
})

expect(filterData(filterDataHandler)).toEqual({
bar: 'baz',
describe('filterData', () => {
it('should provide filterData handler', () => {
type Data = { foo: string }

const filterDataHandler = jest.fn(({ props }) => {
if (props.disabled === true) {
return false
}
})

render(
<Form.Handler id={identifier}>
<Field.String path="/foo" defaultValue="foo" disabled />
<Field.String path="/bar" defaultValue="baz" />
</Form.Handler>
)

const { data, filterData } = getData<Data>(identifier)

expect(data).toEqual({
foo: 'foo',
bar: 'baz',
})

expect(filterData(filterDataHandler)).toEqual({
bar: 'baz',
})
})
})

it('should provide reduceToVisibleFields handler', () => {
type Data = { foo: string }

const { rerender } = render(
<Form.Handler id={identifier}>
<Form.Visibility visible={true}>
<Field.String path="/foo" value="foo" />
</Form.Visibility>

<Field.String path="/bar" value="baz" />
</Form.Handler>
)

const { data, reduceToVisibleFields } = getData<Data>(identifier)

expect(data).toEqual({
foo: 'foo',
bar: 'baz',
})

rerender(
<Form.Handler id={identifier}>
<Form.Visibility visible={false}>
<Field.String path="/foo" value="foo" />
</Form.Visibility>

<Field.String path="/bar" value="baz" />
</Form.Handler>
)

expect(reduceToVisibleFields(data)).toEqual({
bar: 'baz',
})

rerender(
<Form.Handler id={identifier}>
<Form.Visibility visible={true}>
<Field.String path="/foo" value="foo" />
</Form.Visibility>

<Field.String path="/bar" value="baz" />
</Form.Handler>
)

expect(reduceToVisibleFields(data)).toEqual({
foo: 'foo',
bar: 'baz',
describe('reduceToVisibleFields', () => {
it('should provide reduceToVisibleFields handler', () => {
type Data = { foo: string }

const { rerender } = render(
<Form.Handler id={identifier}>
<Form.Visibility visible={true}>
<Field.String path="/foo" defaultValue="foo" />
</Form.Visibility>

<Field.String path="/bar" defaultValue="baz" />
</Form.Handler>
)

const { data, reduceToVisibleFields } = getData<Data>(identifier)

expect(data).toEqual({
foo: 'foo',
bar: 'baz',
})

rerender(
<Form.Handler id={identifier}>
<Form.Visibility visible={false}>
<Field.String path="/foo" defaultValue="foo" />
</Form.Visibility>

<Field.String path="/bar" defaultValue="baz" />
</Form.Handler>
)

expect(reduceToVisibleFields(data)).toEqual({
bar: 'baz',
})

rerender(
<Form.Handler id={identifier}>
<Form.Visibility visible={true}>
<Field.String path="/foo" defaultValue="foo" />
</Form.Visibility>

<Field.String path="/bar" defaultValue="baz" />
</Form.Handler>
)

expect(reduceToVisibleFields(data)).toEqual({
foo: 'foo',
bar: 'baz',
})
})
})
})
Loading

0 comments on commit d2f5c23

Please sign in to comment.