Skip to content

Commit

Permalink
Close items when PushContainer opens
Browse files Browse the repository at this point in the history
  • Loading branch information
tujoworker committed Aug 28, 2024
1 parent b5fd428 commit 4e6f93a
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import React, {
} from 'react'
import classnames from 'classnames'
import pointer from 'json-pointer'
import { useFieldProps } from '../../hooks'
import { useFieldProps, usePath } from '../../hooks'
import { makeUniqueId } from '../../../../shared/component-helper'
import { Flex } from '../../../../components'
import { pickSpacingProps } from '../../../../components/flex/utils'
Expand Down Expand Up @@ -50,6 +50,7 @@ function ArrayComponent(props: Props) {
const summaryListContext = useContext(SummaryListContext)
const valueBlockContext = useContext(ValueBlockContext)

const { joinPath } = usePath()
const { getValue } = useDataValue()
const preparedProps = useMemo(() => {
const {
Expand Down Expand Up @@ -117,7 +118,7 @@ function ArrayComponent(props: Props) {

const { fieldPropsRef } = useContext(DataContext) || {}
const pathProxy = fieldPropsRef?.current?.[
String(path) + '/iterateProxy'
joinPath([path, '/iterateProxy'])
] as {
containerModeWhen?: ProxyContainerModeWhen
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import { convertJsxToString } from '../../../../shared/component-helper'
import { Lead } from '../../../../elements'
import { Props as FlexContainerProps } from '../../../../components/flex/Container'
import IterateItemContext from '../IterateItemContext'
import DataContext from '../../DataContext/Context'
import EditToolbarTools, { useWasNew } from './EditToolbarTools'
import ElementBlock, {
ElementSectionProps,
} from '../AnimatedContainer/ElementBlock'
import Toolbar from '../Toolbar'
import usePath from '../../hooks/usePath'

export type Props = {
/**
Expand All @@ -35,10 +37,30 @@ export type Props = {
export type AllProps = Props & FlexContainerProps & ElementSectionProps

export default function EditContainer(props: AllProps) {
const { setFieldProps } = useContext(DataContext) || {}
const iterateItemContext = useContext(IterateItemContext)
const { initialContainerMode } = iterateItemContext ?? {}
const { switchContainerMode, initialContainerMode, path } =
iterateItemContext ?? {}
const { toolbar, ...rest } = props

const { joinPath } = usePath()

useMemo(() => {
if (initialContainerMode === 'new') {
setFieldProps?.(joinPath([path, 'iterateProxy']), {
onPushContainerOpen: () => {
switchContainerMode?.('view')
},
})
}
}, [
initialContainerMode,
joinPath,
path,
setFieldProps,
switchContainerMode,
])

return (
<EditContainerWithoutToolbar
toolbar={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,13 +288,25 @@ describe('EditContainer and ViewContainer', () => {

describe('containerMode', () => {
it('should set all items to edit mode', async () => {
let containerMode = null

const ContextConsumer = () => {
const context = React.useContext(IterateItemContext)
containerMode = context.containerMode

return null
}

render(
<Iterate.Array value={['foo', 'bar']} containerMode="edit">
<Iterate.ViewContainer>View Content</Iterate.ViewContainer>
<Iterate.EditContainer>Edit Content</Iterate.EditContainer>
<ContextConsumer />
</Iterate.Array>
)

expect(containerMode).toBe('edit')

const elements = document.querySelectorAll(
'.dnb-forms-iterate__element'
)
Expand All @@ -321,6 +333,8 @@ describe('EditContainer and ViewContainer', () => {
expect(editBlock).toHaveClass('dnb-forms-section-edit-block')
expect(editBlock).not.toHaveClass('dnb-height-animation--hidden')
}

expect(containerMode).toBe('edit')
})

it('should not contain toolbar when mode is "new"', async () => {
Expand All @@ -346,7 +360,58 @@ describe('EditContainer and ViewContainer', () => {
expect(viewBlock.querySelectorAll('button')).toHaveLength(2)
})

it('should close the item when a new appears in the array', async () => {
let containerMode = null

const ContextConsumer = () => {
const context = React.useContext(IterateItemContext)
containerMode = context.containerMode

return null
}

render(
<Form.Handler data={['foo']}>
<Iterate.Array
path="/"
containerMode={(items) => {
return items.length === 1 ? 'new' : 'edit'
}}
>
<Iterate.ViewContainer>View Content</Iterate.ViewContainer>
<Iterate.EditContainer>Edit Content</Iterate.EditContainer>
<ContextConsumer />
</Iterate.Array>

<Iterate.PushContainer
path="/"
openButton={<Iterate.PushContainer.OpenButton />}
showOpenButtonWhen={(list) => list.length > 0}
>
New Content
</Iterate.PushContainer>
</Form.Handler>
)

expect(containerMode).toBe('edit')

await userEvent.click(
document.querySelector('button.dnb-forms-iterate-open-button')
)

expect(containerMode).toBe('view')
})

it('should set existing item to new/edit mode when there is only one item', async () => {
let containerMode = null

const ContextConsumer = () => {
const context = React.useContext(IterateItemContext)
containerMode = context.containerMode

return null
}

render(
<Form.Handler data={['foo']}>
<Iterate.Array
Expand All @@ -357,12 +422,15 @@ describe('EditContainer and ViewContainer', () => {
>
<Iterate.ViewContainer>View Content</Iterate.ViewContainer>
<Iterate.EditContainer>Edit Content</Iterate.EditContainer>
<ContextConsumer />
</Iterate.Array>

<Iterate.PushButton path="/" pushValue="bar" />
</Form.Handler>
)

expect(containerMode).toBe('edit')

{
const elements = document.querySelectorAll(
'.dnb-forms-iterate__element'
Expand All @@ -378,6 +446,8 @@ describe('EditContainer and ViewContainer', () => {
expect(editBlock).not.toHaveClass('dnb-height-animation--hidden')
}

expect(containerMode).toBe('edit')

await userEvent.click(
document.querySelector('button.dnb-forms-iterate-push-button')
)
Expand Down Expand Up @@ -409,6 +479,8 @@ describe('EditContainer and ViewContainer', () => {
expect(editBlock.querySelectorAll('button')).toHaveLength(2)
}
}

expect(containerMode).toBe('edit')
})
})
})
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React, { useCallback, useContext, useRef } from 'react'
import React, { useCallback, useContext, useEffect, useRef } from 'react'
import Isolation from '../../Form/Isolation'
import PushContainerContext from './PushContainerContext'
import IterateItemContext from '../IterateItemContext'
import DataContext from '../../DataContext/Context'
import useDataValue from '../../hooks/useDataValue'
import usePath from '../../hooks/usePath'
import EditContainer from '../EditContainer'
import IterateArray, { ContainerMode } from '../Array'
import OpenButton from './OpenButton'
Expand Down Expand Up @@ -62,7 +63,7 @@ function PushContainer(props: AllProps) {
...rest
} = props

const { setFieldProps } = useContext(DataContext) || {}
const { setFieldProps, fieldPropsRef } = useContext(DataContext) || {}
const commitHandleRef = useRef<() => void>()
const switchContainerModeRef = useRef<(mode: ContainerMode) => void>()
const { value: entries = [], moveValueToPath } = useDataValue<
Expand All @@ -78,16 +79,17 @@ function PushContainer(props: AllProps) {
switchContainerMode: switchContainerModeRef.current,
}

const { joinPath } = usePath()

const switchArrayContainerToViewMode = useCallback(() => {
if (path) {
const opts = {
setFieldProps?.(joinPath([path, '/iterateProxy']), {
containerModeWhen: (isNew: boolean) => {
return isNew ? 'view' : undefined
},
}
setFieldProps?.(path + '/iterateProxy', opts)
})
}
}, [path, setFieldProps])
}, [joinPath, path, setFieldProps])

return (
<Isolation
Expand All @@ -103,37 +105,70 @@ function PushContainer(props: AllProps) {
>
<PushContainerContext.Provider value={newItemContextProps}>
<IterateArray value={[data]} path="/newItems">
<IterateItemContext.Consumer>
{({ containerMode, switchContainerMode }) => {
switchContainerModeRef.current = switchContainerMode

return (
<>
<EditContainer
open={!showOpenButton || containerMode === 'edit'}
title={title}
{...rest}
>
{children}
</EditContainer>

{openButton && typeof showOpenButton === 'boolean' && (
<HeightAnimation
open={showOpenButton && containerMode === 'view'}
>
{openButton}
</HeightAnimation>
)}
</>
)
}}
</IterateItemContext.Consumer>
<ItemWrapper
path={path}
showOpenButton={showOpenButton}
openButton={openButton}
title={title}
fieldPropsRef={fieldPropsRef}
switchContainerModeRef={switchContainerModeRef}
{...rest}
>
{children}
</ItemWrapper>
</IterateArray>
</PushContainerContext.Provider>
</Isolation>
)
}

function ItemWrapper({
path,
showOpenButton,
openButton,
title,
switchContainerModeRef,
fieldPropsRef,
children,
...rest
}) {
const { containerMode, switchContainerMode } =
useContext(IterateItemContext) || {}
const { joinPath } = usePath()

switchContainerModeRef.current = switchContainerMode

useEffect(() => {
if (containerMode === 'edit') {
const pathProxy = fieldPropsRef?.current?.[
joinPath([path, '/iterateProxy'])
] as {
onPushContainerOpen?: () => void
}

pathProxy?.onPushContainerOpen?.()
}
}, [containerMode, fieldPropsRef, joinPath, path])

return (
<>
<EditContainer
open={!showOpenButton || containerMode === 'edit'}
title={title}
{...rest}
>
{children}
</EditContainer>

{openButton && typeof showOpenButton === 'boolean' && (
<HeightAnimation open={showOpenButton && containerMode === 'view'}>
{openButton}
</HeightAnimation>
)}
</>
)
}

PushContainer.OpenButton = OpenButton
PushContainer._supportsSpacingProps = true

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ describe('usePath', () => {
expect(result.current.path).toBe(`${sectionPath}${path}`)
})

it('joinPath', () => {
const { result } = renderHook(() => usePath(), {})
expect(
result.current.joinPath([undefined, null, '', 'foo/', '/bar//'])
).toBe('/foo/bar')
})

it('should return the correct identifier when itemPath is defined', () => {
const path = '/path'
const iteratePath = '/iteratePath'
Expand Down
8 changes: 8 additions & 0 deletions packages/dnb-eufemia/src/extensions/forms/hooks/usePath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ export default function usePath(props: Props = {}) {
throw new Error(`itemPath="${itemPathProp}" must start with a slash`)
}

const joinPath = useCallback((paths: Array<Path>) => {
return paths
.reduce((acc, cur) => (cur ? `${acc}/${cur}` : acc), '/')
.replace(/\/{2,}/g, '/')
.replace(/\/+$/, '')
}, [])

const makeSectionPath = useCallback(
(path: Path) => {
return `${
Expand Down Expand Up @@ -88,6 +95,7 @@ export default function usePath(props: Props = {}) {
identifier,
path,
itemPath,
joinPath,
makePath,
makeIteratePath,
makeSectionPath,
Expand Down

0 comments on commit 4e6f93a

Please sign in to comment.