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 bb0dda9 commit c66e459
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 267 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 c66e459

Please sign in to comment.