Skip to content

Commit

Permalink
Empty data with correct type
Browse files Browse the repository at this point in the history
  • Loading branch information
tujoworker committed Oct 7, 2024
1 parent 4d83c77 commit 5620eea
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@ hideInMenu: true

import TranslationsTable from 'dnb-design-system-portal/src/shared/parts/TranslationsTable'
import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable'
import { PushContainerProperties } from '@dnb/eufemia/src/extensions/forms/Iterate/PushContainer/PushContainerDocs'
import {
PushContainerProperties,
PushContainerEvents,
} from '@dnb/eufemia/src/extensions/forms/Iterate/PushContainer/PushContainerDocs'

## Properties

<PropertiesTable props={PushContainerProperties} />

## Events

<PropertiesTable props={PushContainerEvents} />

## Translations

<TranslationsTable localeKey={['IteratePushContainer']} />
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ export default function Provider<Data extends JsonObject>(
// eslint-disable-next-line react-hooks/exhaustive-deps -- Avoid triggering code that should only run initially
}, [])
const internalDataRef = useRef<Data>(initialData)
const isEmptyDataRef = useRef(false)

// - Validator
const ajvValidatorRef = useRef<ValidateFunction>()
Expand Down Expand Up @@ -658,8 +659,10 @@ export default function Provider<Data extends JsonObject>(
) {
cacheRef.current.shared = sharedData.data

if (internalDataRef.current === clearedData) {
return clearedData as Data
if (isEmptyDataRef.current) {
return (
Array.isArray(internalDataRef.current) ? [] : clearedData
) as Data
}

return {
Expand All @@ -682,10 +685,12 @@ export default function Provider<Data extends JsonObject>(
? pointer.get(internalData, props.path)
: internalData

const isEmptyDataRef = useRef(false)
const clearData = useCallback(() => {
isEmptyDataRef.current = true
internalDataRef.current = (emptyData ?? clearedData) as Data
internalDataRef.current = ((typeof emptyData === 'function'
? emptyData(internalDataRef.current)
: emptyData) ??
(Array.isArray(internalDataRef.current) ? [] : clearedData)) as Data

if (id) {
setSharedData?.(internalDataRef.current)
Expand Down Expand Up @@ -1002,7 +1007,6 @@ export default function Provider<Data extends JsonObject>(
if (isolate) {
submitResult = await onCommit?.(internalDataRef.current, {
clearData,
setData,
})
} else {
submitResult = await onSubmit()
Expand Down Expand Up @@ -1075,7 +1079,6 @@ export default function Provider<Data extends JsonObject>(
setFormState,
setShowAllErrors,
setSubmitState,
setData,
]
)

Expand Down Expand Up @@ -1490,9 +1493,3 @@ function useFormStatusBuffer(props: FormStatusBufferProps) {
}

export const clearedData = Object.freeze({})
export const getClearedData = (mergeData?: Record<string, unknown>) => {
if (mergeData) {
return Object.assign(clearedData, mergeData)
}
return clearedData
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import EditContainer, { CancelButton, DoneButton } from '../EditContainer'
import IterateArray, { ContainerMode } from '../Array'
import OpenButton from './OpenButton'
import { Flex, HeightAnimation } from '../../../../components'
import { Path } from '../../types'
import { OnCommit, Path } from '../../types'
import { SpacingProps } from '../../../../shared/types'
import { useArrayLimit, useSwitchContainerMode } from '../hooks'
import Toolbar from '../Toolbar'
Expand Down Expand Up @@ -58,6 +58,11 @@ export type Props = {
*/
toolbar?: React.ReactNode

/**
* Will be called when the user clicks on the "Done" button.
*/
onCommit?: OnCommit

/**
* The container contents.
*/
Expand All @@ -76,6 +81,7 @@ function PushContainer(props: AllProps) {
children,
openButton,
showOpenButtonWhen,
onCommit,
...rest
} = props

Expand Down Expand Up @@ -120,30 +126,40 @@ function PushContainer(props: AllProps) {
}
}, [dataProp, defaultDataProp, isolatedData])

const emptyData = useCallback(
(data: { pushContainerItems: unknown[] }) => {
const firstItem = data.pushContainerItems?.[0]
if (firstItem === null || typeof firstItem !== 'object') {
return {
...isolatedData,
pushContainerItems: [null],
}
}
return defaultData
},
[defaultData, isolatedData]
)

return (
<Isolation
data={data}
defaultData={defaultData}
emptyData={defaultData}
emptyData={emptyData}
commitHandleRef={commitHandleRef}
transformOnCommit={({ pushContainerItems }) => {
return moveValueToPath(path, [...entries, ...pushContainerItems])
}}
onCommit={(data, { clearData, setData, preventCommit }) => {
onCommit={(data, options) => {
const { clearData, preventCommit } = options
if (hasReachedLimit) {
preventCommit()
setShowStatus(true)
} else {
setNextContainerMode('view')
switchContainerModeRef.current?.('view')
clearData()
if (isolatedData) {
setData({
...isolatedData,
pushContainerItems: [clearedData],
})
}
}
onCommit?.(data, options)
}}
>
<PushContainerContext.Provider value={newItemContextProps}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { PropertiesTableProps } from '../../../../shared/types'
import { IsolationEvents } from '../../Form/Isolation/IsolationDocs'

export const PushContainerProperties: PropertiesTableProps = {
path: {
Expand Down Expand Up @@ -58,4 +59,6 @@ export const PushContainerProperties: PropertiesTableProps = {
},
}

export const PushContainerEvents: PropertiesTableProps = {}
export const PushContainerEvents: PropertiesTableProps = {
onCommit: IsolationEvents.onCommit,
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React from 'react'
import React, { useContext } from 'react'
import { render, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { Field, Form, Iterate } from '../../..'
import nbNO from '../../../constants/locales/nb-NO'
import { Div } from '../../../../../elements'
import DataContext from '../../../DataContext/Context'

import nbNO from '../../../constants/locales/nb-NO'
const nb = nbNO['nb-NO']

describe('PushContainer', () => {
Expand Down Expand Up @@ -548,7 +549,7 @@ describe('PushContainer', () => {
)
})

it('should keep the defaultValue after clearing', async () => {
it('should not show error message after clearing', async () => {
const onChange = jest.fn()

render(
Expand Down Expand Up @@ -643,6 +644,68 @@ describe('PushContainer', () => {
expect(document.querySelector('.dnb-form-status')).toBeNull()
})
})

it('should keep the defaultValue after clearing', async () => {
const onChange = jest.fn()
const onCommit = jest.fn()

let internalContext = null
const CollectInternalData = () => {
internalContext = useContext(DataContext)
return null
}

render(
<Form.Handler onChange={onChange}>
<Iterate.PushContainer path="/" onCommit={onCommit}>
<Field.String itemPath="/" defaultValue="default value" />
<CollectInternalData />
</Iterate.PushContainer>
</Form.Handler>
)

expect(internalContext).toMatchObject({
data: {
pushContainerItems: ['default value'],
},
})

const input = document.querySelector('input')

await userEvent.type(input, ' changed')

const button = document.querySelector('button')

await userEvent.click(button)
expect(internalContext.internalDataRef.current).toEqual({
pushContainerItems: ['default value'],
})
expect(onChange).toHaveBeenCalledTimes(1)
expect(onChange).toHaveBeenLastCalledWith(
['default value changed'],
expect.anything()
)
expect(onCommit).toHaveBeenCalledTimes(1)
expect(onCommit).toHaveBeenLastCalledWith(
['default value changed'],
expect.anything()
)

await userEvent.click(button)
expect(internalContext.internalDataRef.current).toEqual({
pushContainerItems: ['default value'],
})
expect(onChange).toHaveBeenCalledTimes(2)
expect(onChange).toHaveBeenLastCalledWith(
['default value changed', 'default value'],
expect.anything()
)
expect(onCommit).toHaveBeenCalledTimes(2)
expect(onCommit).toHaveBeenLastCalledWith(
['default value changed', 'default value'],
expect.anything()
)
})
})

it('should support initial data as a string', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1823,11 +1823,8 @@ export default function useFieldProps<Value, EmptyValue, Props>(

useEffect(() => {
if (isEmptyData()) {
// Fill the data context with the default value after it has been cleared
requestAnimationFrame(() => {
setContextData()
validateValue()
})
setContextData()
validateValue()
}
}, [isEmptyData, setContextData, validateValue])

Expand Down
1 change: 0 additions & 1 deletion packages/dnb-eufemia/src/extensions/forms/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,6 @@ export type OnCommit<Data = JsonObject> = (
preventCommit,
}: {
clearData: () => void
setData?: (data: Data) => void
preventCommit?: () => void
}
) =>
Expand Down

0 comments on commit 5620eea

Please sign in to comment.