Skip to content

Commit

Permalink
fix(Forms): ensure transformData contains displayValue from field…
Browse files Browse the repository at this point in the history
…s inside Iterate (#4526)

- This is kind of a following up on #4510
- More info
[here](https://dnb-it.slack.com/archives/CMXABCHEY/p1738314903619959?thread_ts=1736345187.215229&cid=CMXABCHEY).

**Note:**

When using `transformData` with Iterate.Array, then it is important to
exclude the entry from being transformed, because that is probably not
what you want. You can do it with a type check or with a `path` !==
'myArray':

<img width="441" alt="Screenshot 2025-02-03 at 13 46 08"
src="https://github.com/user-attachments/assets/83e969c8-73f0-4214-8c3c-97ceccdef9be"
/>
  • Loading branch information
tujoworker authored Feb 3, 2025
1 parent 76172c2 commit cc67a19
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,9 @@ export const TransformData = () => {
const transformedData = transformData(
data,
({ value, displayValue, label }) => {
return { value, displayValue, label }
if (!Array.isArray(value)) {
return { value, displayValue, label }
}
},
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -339,3 +339,35 @@ The callback function receives the following arguments as an object:
Most of the fields will return the `displayValue` as a string. But there are some exceptions:

- [ArraySelection](/uilib/extensions/forms/base-fields/ArraySelection/) will return the displayed/active options content as an array that contains a string (or React.ReactNode).

##### `displayValue` from fields inside Iterate.Array

When using the `Iterate.Array` component, you may check if the current entry is an array. This way you ensure you never transform the array itself, but only the values from the fields inside the array.

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

const MyForm = () => {
return (
<Form.Handler
onSubmit={(data, { transformData }) => {
const transformedData = transformData(
data,
({ value, displayValue, label }) => {
if (!Array.isArray(value)) {
return { value, displayValue, label }
}
},
)
}}
>
<Form.Card>
<Iterate.Array path="/myArray">
<Field.String itemPath="/" label="My label" />
</Iterate.Array>
</Form.Card>
<Form.SubmitButton />
</Form.Handler>
)
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -424,17 +424,26 @@ export default function Provider<Data extends JsonObject>(
* Mutate the data set based on the filterData function
*/
const mutateDataHandler = useCallback(
(data: Data, handler: TransformData | FilterData, remove = false) => {
const mutate = (path: Path, result: boolean | unknown) => {
(
data: Data,
handler: TransformData | FilterData,
{ remove = false, mutate = true } = {}
) => {
const freshData = {} as Data
const mutateEntry = (path: Path, result: boolean | unknown) => {
if (remove) {
if (result === false) {
data = structuredClone(data)
pointer.remove(data, path)
}
} else {
if (typeof result !== 'undefined') {
data = structuredClone(data)
pointer.set(data, path, result)
if (mutate) {
data = structuredClone(data)
pointer.set(data, path, result)
} else {
pointer.set(freshData, path, result)
}
}
}
}
Expand All @@ -458,11 +467,15 @@ export default function Provider<Data extends JsonObject>(
props,
internal,
})
mutate(path, result)
mutateEntry(path, result)
}
}
)

if (!mutate) {
return freshData
}

return data
} else if (handler) {
const runFilter = ({ path, condition }) => {
Expand All @@ -480,7 +493,7 @@ export default function Provider<Data extends JsonObject>(
internal,
})
: condition
mutate(path, result)
mutateEntry(path, result)
}
}

Expand Down Expand Up @@ -567,7 +580,7 @@ export default function Provider<Data extends JsonObject>(
const filterDataHandler = useCallback(
(data: Data, filter: FilterData) => {
if (filter) {
return mutateDataHandler(data, filter, true)
return mutateDataHandler(data, filter, { remove: true })
}

return data
Expand Down Expand Up @@ -1176,7 +1189,7 @@ export default function Provider<Data extends JsonObject>(
}

const transformData = (data: Data, handler: TransformData) => {
return mutateDataHandler(data, handler)
return mutateDataHandler(data, handler, { mutate: false })
}

const formElement = formElementRef.current
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
OnChange,
DataValueWriteProps,
OnSubmit,
Iterate,
} from '../../../'
import { isCI } from 'repo-utils'
import { Props as StringFieldProps } from '../../../Field/String'
Expand Down Expand Up @@ -4883,6 +4884,70 @@ describe('DataContext.Provider', () => {
})
})

it('should transform data with "transformData" from fields inside Iterate', async () => {
let transformedData = undefined
const onSubmit = jest.fn((data, { transformData }) => {
transformedData = transformData(
data,
({ value, displayValue, label }) => {
if (!Array.isArray(value)) {
return { value, displayValue, label }
}
}
)
})

render(
<Form.Handler
onSubmit={onSubmit}
defaultData={{
accounts: [null],
}}
>
<Iterate.Array path="/accounts">
<Field.String
label="Bar label"
itemPath="/fooPath"
defaultValue="foo value"
/>
</Iterate.Array>
</Form.Handler>
)

fireEvent.submit(document.querySelector('form'))

expect(onSubmit).toHaveBeenCalledTimes(1)
expect(transformedData).toEqual({
accounts: [
{
fooPath: {
displayValue: 'foo value',
label: 'Bar label',
value: 'foo value',
},
},
],
})

const stringField = document.querySelector('input')
fireEvent.change(stringField, { target: { value: 'bar value' } })

fireEvent.submit(document.querySelector('form'))

expect(onSubmit).toHaveBeenCalledTimes(2)
expect(transformedData).toEqual({
accounts: [
{
fooPath: {
displayValue: 'bar value',
label: 'Bar label',
value: 'bar value',
},
},
],
})
})

describe('reduceToVisibleFields', () => {
it('should remove data entries of hidden fields using Visibility', async () => {
let submitData = null
Expand Down

0 comments on commit cc67a19

Please sign in to comment.