Skip to content

Commit

Permalink
Refactor of ArrayElement and its context
Browse files Browse the repository at this point in the history
  • Loading branch information
tujoworker committed Sep 11, 2024
1 parent 590eb8e commit 06506cd
Show file tree
Hide file tree
Showing 10 changed files with 184 additions and 203 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import { Flex, HeightAnimation } from '../../../../components'
import IterateItemContext, {
IterateItemContextState,
} from '../IterateItemContext'
import ElementBlockContext from './ElementBlockContext'
import ArrayItemAreaContext from './ArrayItemAreaContext'
import FieldBoundaryContext from '../../DataContext/FieldBoundary/FieldBoundaryContext'
import { Props as FlexContainerProps } from '../../../../components/flex/Container'
import { ContainerMode } from '../Array/types'
import { ContainerMode } from './types'

export type ElementSectionProps = {
export type ArrayItemAreaProps = {
/**
* Defines the variant of the ViewContainer or EditContainer. Can be `outline`.
* Defaults to `outline`.
Expand All @@ -28,9 +28,9 @@ export type Props = {
open?: boolean | undefined
ariaLabel?: string
openDelay?: number
} & ElementSectionProps
} & ArrayItemAreaProps

function ElementBlock(props: Props & FlexContainerProps) {
function ArrayItemArea(props: Props & FlexContainerProps) {
const [, forceUpdate] = useReducer(() => ({}), {})

const {
Expand All @@ -46,11 +46,11 @@ function ElementBlock(props: Props & FlexContainerProps) {
} = props

const localContextRef = useRef<IterateItemContextState>()

const { hasError, hasSubmitError } =
useContext(FieldBoundaryContext) || {}
localContextRef.current = useContext(IterateItemContext) || {}
const omitFocusManagementRef = useRef(false)
const nextFocusElementRef = useRef<HTMLElement>()
const { isNew, value } = localContextRef.current
if (hasSubmitError || !value) {
localContextRef.current.containerMode = 'edit'
Expand Down Expand Up @@ -79,7 +79,7 @@ function ElementBlock(props: Props & FlexContainerProps) {
determineMode()
}, [determineMode])

const { handleRemove, previousContainerMode, containerMode } =
const { handleRemove, index, previousContainerMode, containerMode } =
localContextRef.current

const openRef = useRef(open ?? (containerMode === mode && !isNew))
Expand Down Expand Up @@ -121,26 +121,7 @@ function ElementBlock(props: Props & FlexContainerProps) {
if (state === 'opened') {
localContextRef.current?.elementRef?.current?.focus?.()
} else if (state === 'closed') {
// Wait until the element is removed, then check if we can set focus
window.requestAnimationFrame(() => {
// try to focus on the second last element
try {
if (
// But not when we focus is already inside our element
!document.activeElement?.closest(
'.dnb-forms-iterate__element'
)
) {
const elements =
localContextRef.current?.containerRef.current.querySelectorAll<HTMLDivElement>(
'.dnb-forms-iterate__element'
)
elements[elements.length - 1].focus()
}
} catch (e) {
/* do nothing */
}
})
nextFocusElementRef.current?.focus?.()
}
}

Expand All @@ -162,14 +143,23 @@ function ElementBlock(props: Props & FlexContainerProps) {
},
[onAnimationEnd, setFocus]
)
const handleRemoveBlock = useCallback(() => {

const handleRemoveItem = useCallback(() => {
try {
// Because "previousElementSibling" did not work in Jest/JSDOM
nextFocusElementRef.current = Array.from(
localContextRef.current.elementRef.current.parentElement.childNodes
).at(index - 1) as HTMLElement
} catch (e) {
//
}
isRemoving.current = true
handleRemove?.({ keepItems: true })
setOpenState(false)
}, [handleRemove, setOpenState])
}, [handleRemove, index, setOpenState])

return (
<ElementBlockContext.Provider value={{ handleRemoveBlock }}>
<ArrayItemAreaContext.Provider value={{ handleRemoveItem }}>
<HeightAnimation
className={classnames(
'dnb-forms-section-block',
Expand All @@ -192,9 +182,9 @@ function ElementBlock(props: Props & FlexContainerProps) {
{children}
</Flex.Stack>
</HeightAnimation>
</ElementBlockContext.Provider>
</ArrayItemAreaContext.Provider>
)
}

ElementBlock._supportsSpacingProps = true
export default ElementBlock
ArrayItemArea._supportsSpacingProps = true
export default ArrayItemArea
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createContext } from 'react'

type ArrayItemAreaContext = {
handleRemoveItem?: () => void
}

const ArrayItemAreaContext = createContext<ArrayItemAreaContext>(null)

export default ArrayItemAreaContext
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react'
import React, { useEffect } from 'react'
import { fireEvent, render } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import * as Iterate from '../..'
import * as DataContext from '../../../DataContext'
import { IterateItemContext } from '../..'
import { Field, FieldBlock, Form, Value, ValueBlock } from '../../..'
import { FilterData } from '../../../DataContext'

Expand Down Expand Up @@ -1171,4 +1172,97 @@ describe('Iterate.Array', () => {
log.mockRestore()
})
})

it('should contain tabindex of -1', () => {
render(<Iterate.Array value={['one']}>content</Iterate.Array>)

expect(
document.querySelector('.dnb-forms-iterate__element')
).toHaveAttribute('tabindex', '-1')
})

it('should set elementRef', () => {
let elementRef = null

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

useEffect(() => {
elementRef = context.elementRef.current
})

return null
}

render(
<Iterate.Array value={['one']}>
<ContextConsumer />
</Iterate.Array>
)

expect(elementRef).toBeDefined()
expect(elementRef instanceof HTMLElement).toBeTruthy()
})

it('should set index and value', () => {
let contextToTest = null

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

useEffect(() => {
contextToTest = context
})

return null
}

render(
<Iterate.Array value={['one']}>
<ContextConsumer />
</Iterate.Array>
)

expect(contextToTest).toMatchObject({
index: 0,
value: 'one',
})
})

it('focuses on the block when focusOnOpen prop is true', async () => {
const { rerender } = render(
<Iterate.Array value={['foo']}>
{(itemValue, index) => {
return (
<output>
Content {JSON.stringify(itemValue)} {index}
</output>
)
}}
</Iterate.Array>
)

expect(
document.querySelectorAll('.dnb-forms-iterate__element')
).toHaveLength(1)
expect(document.querySelector('output')).toHaveTextContent(
'Content "foo" 0'
)

rerender(
<Iterate.Array value={['foo', 'bar']}>
{(itemValue, index) => {
return (
<output>
Content {JSON.stringify(itemValue)} {index}
</output>
)
}}
</Iterate.Array>
)

const outputs = document.querySelectorAll('output')
expect(outputs[0]).toHaveTextContent('Content "foo" 0')
expect(outputs[1]).toHaveTextContent('Content "bar" 1')
})
})

This file was deleted.

Loading

0 comments on commit 06506cd

Please sign in to comment.