Skip to content

Commit

Permalink
Ensure correct error message and avoid cancel click when item has error
Browse files Browse the repository at this point in the history
  • Loading branch information
tujoworker committed Aug 30, 2024
1 parent 497e2fc commit 0c6e41c
Show file tree
Hide file tree
Showing 23 changed files with 678 additions and 203 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -389,20 +389,6 @@ export const WithInitialValue = () => {
)
}

const CreateNewEntry = () => {
return (
<Iterate.PushContainer
path="/countries"
openButton={
<Iterate.PushContainer.OpenButton text="Legg til flere statsborgerskap" />
}
showOpenButtonWhen={(list) => list.length > 0}
>
<MyEditItemForm />
</Iterate.PushContainer>
)
}

const MyViewItem = () => {
return (
<Iterate.ViewContainer>
Expand All @@ -417,7 +403,6 @@ export const WithInitialValue = () => {
const MyForm = () => {
return (
<Form.Handler
data={{ countries: [null] }}
onSubmit={(data) => console.log('onSubmit', data)}
onSubmitRequest={() => console.log('onSubmitRequest')}
>
Expand All @@ -427,15 +412,22 @@ export const WithInitialValue = () => {
<Card align="stretch">
<Iterate.Array
path="/countries"
defaultValue={[null]}
containerMode={(items) => {
return items.length === 1 ? 'new' : 'view'
if (items?.length === 1) {
return 'initial-open'
}
}}
>
<MyViewItem />
<MyEditItem />
</Iterate.Array>

<CreateNewEntry />
<Iterate.PushButton
path="/countries"
pushValue={null}
text="Legg til flere statsborgerskap"
/>
</Card>

<Form.SubmitButton variant="send" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ With an optional `title` and [Iterate.Toolbar](/uilib/extensions/forms/Iterate/T

<Examples.ValueComposition />

### With initial value and `containerMode="new"`
### With initial value and `containerMode="initial-open"`

By using `containerMode="new"` and providing an initial value of `null` to the array, the user needs to fill out the first item.
By using `containerMode="initial-open"` and providing an initial value of `null` to the array, the user needs to fill out the first item.

<Examples.WithInitialValue />
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,18 @@ export interface Props<Data extends JsonObject>
* Unique ID to connect with a GlobalStatus
*/
globalStatusId?: string
/**
* Source data, will be used instead of defaultData, and leading to updates if changed after mount
*/
data?: Data
/**
* Default source data, only used if no other source is available, and not leading to updates if changed after mount
*/
defaultData?: Data
/**
* Source data, will be used instead of defaultData, and leading to updates if changed after mount
* Empty data, used to clear the data set.
*/
data?: Data
emptyData?: unknown
/**
* JSON Schema to validate the data against.
*/
Expand Down Expand Up @@ -178,6 +182,7 @@ export default function Provider<Data extends JsonObject>(
id,
globalStatusId = 'main',
defaultData,
emptyData,
data,
schema,
onChange,
Expand Down Expand Up @@ -829,14 +834,15 @@ export default function Provider<Data extends JsonObject>(
}, [])

const clearData = useCallback(() => {
internalDataRef.current = clearedData as Data
internalDataRef.current = (emptyData ?? clearedData) as Data

if (id) {
setSharedData?.(internalDataRef.current)
} else {
forceUpdate()
}
onClear?.()
}, [id, onClear, setSharedData])
}, [emptyData, id, onClear, setSharedData])

/**
* Shared logic dedicated to submit the whole form
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,9 @@ function IsolationProvider<Data extends JsonObject>(
// Commit the internal data to the nested context data
handlePathChangeOuter?.(
path,
extendDeep({}, outerData, isolatedData)
Array.isArray(isolatedData)
? isolatedData
: extendDeep({}, outerData, isolatedData)
)

return await onCommitProp?.(
Expand Down Expand Up @@ -202,8 +204,7 @@ function IsolationProvider<Data extends JsonObject>(

const providerProps: IsolationProps<Data> = {
...props,
data: internalDataRef.current,
defaultData: undefined,
[defaultData ? 'defaultData' : 'data']: internalDataRef.current,
onPathChange: onPathChangeHandler,
onCommit,
onClear,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,20 @@ function ElementBlock(props: Props & FlexContainerProps) {
contextRef.current.hasSubmitError = hasSubmitError

// - Set the container mode to "edit" if we have an error
if (hasSubmitError || contextRef.current.containerMode === 'new') {
if (
hasSubmitError ||
contextRef.current.containerMode === 'initial-open'
) {
contextRef.current.containerMode = 'edit'
}

const { handleRemove, switchContainerMode, containerMode, isNew } =
contextRef.current
const {
handleRemove,
switchContainerMode,
containerMode,
initialContainerMode,
isNew,
} = contextRef.current

const {
mode,
Expand Down Expand Up @@ -98,7 +106,10 @@ function ElementBlock(props: Props & FlexContainerProps) {
const handleAnimationEnd = useCallback(
(state) => {
// - Keep the block open if we have an error
if (contextRef.current.hasSubmitError) {
if (
contextRef.current.hasSubmitError &&
initialContainerMode !== 'initial-open'
) {
switchContainerMode?.('edit')
}

Expand Down Expand Up @@ -137,7 +148,7 @@ function ElementBlock(props: Props & FlexContainerProps) {

onAnimationEnd?.(state)
},
[onAnimationEnd, switchContainerMode]
[initialContainerMode, onAnimationEnd, switchContainerMode]
)
const handleRemoveBlock = useCallback(() => {
isRemoving.current = true
Expand Down
47 changes: 18 additions & 29 deletions packages/dnb-eufemia/src/extensions/forms/Iterate/Array/Array.tsx
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, usePath } from '../../hooks'
import { useFieldProps } from '../../hooks'
import { makeUniqueId } from '../../../../shared/component-helper'
import { Flex } from '../../../../components'
import { pickSpacingProps } from '../../../../components/flex/utils'
Expand All @@ -23,17 +23,11 @@ import IterateItemContext, {
} from '../IterateItemContext'
import SummaryListContext from '../../Value/SummaryList/SummaryListContext'
import ValueBlockContext from '../../ValueBlock/ValueBlockContext'
import DataContext from '../../DataContext/Context'
import FieldBoundaryProvider from '../../DataContext/FieldBoundary/FieldBoundaryProvider'
import useDataValue from '../../hooks/useDataValue'
import { useSwitchContainerMode } from '../hooks'

import type {
ContainerMode,
ProxyContainerModeWhen,
ElementChild,
Props,
Value,
} from './types'
import type { ContainerMode, ElementChild, Props, Value } from './types'
import type { Identifier, Path } from '../../types'

/**
Expand All @@ -50,7 +44,6 @@ 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 @@ -116,25 +109,15 @@ function ArrayComponent(props: Props) {
valueCountRef.current = arrayValue || []
}, [arrayValue])

const { fieldPropsRef } = useContext(DataContext) || {}
const pathProxy = fieldPropsRef?.current?.[
joinPath([path, '/iterateProxy'])
] as {
containerModeWhen?: ProxyContainerModeWhen
}

const containerModeForAllItems =
typeof containerMode === 'function'
? containerMode(arrayValue)
: containerMode
const { getNextContainerMode } = useSwitchContainerMode()

const elementData = useMemo(() => {
return ((valueWhileClosingRef.current || arrayValue) ?? []).map(
(value, index) => {
const id = idsRef.current[index] || makeUniqueId()

const hasNewItems =
arrayValue.length > valueCountRef.current?.length
arrayValue?.length > valueCountRef.current?.length

if (!idsRef.current[index]) {
isNewRef.current[id] = hasNewItems
Expand All @@ -143,10 +126,16 @@ function ArrayComponent(props: Props) {

const isNew = isNewRef.current[id] || false
if (!modesRef.current[id]) {
modesRef.current[id] =
containerModeForAllItems ??
pathProxy?.containerModeWhen?.(isNew) ??
(isNew ? 'edit' : 'view')
modesRef.current[id] = isNew
? getNextContainerMode() ?? 'edit'
: 'view'
}
const containerModeForAllItems =
typeof containerMode === 'function'
? containerMode(arrayValue)
: containerMode
if (containerModeForAllItems) {
modesRef.current[id] = containerModeForAllItems
}

return {
Expand Down Expand Up @@ -176,7 +165,7 @@ function ArrayComponent(props: Props) {
},
handlePush: (element: unknown) => {
hadPushRef.current = true
handleChange([...(arrayValue ?? []), element])
handleChange([...(arrayValue || []), element])
},
handleRemove: ({ keepItems = false } = {}) => {
if (keepItems) {
Expand Down Expand Up @@ -265,7 +254,7 @@ function ArrayComponent(props: Props) {
if (omitFlex) {
return (
<IterateItemContext.Provider
key={`element-${id}`}
key={`element-${id}-${index}`}
value={contextValue}
>
<FieldBoundaryProvider>{content}</FieldBoundaryProvider>
Expand All @@ -278,7 +267,7 @@ function ArrayComponent(props: Props) {
className="dnb-forms-iterate__element"
tabIndex={-1}
innerRef={elementRef}
key={`element-${id}`}
key={`element-${id}-${index}`}
>
<IterateItemContext.Provider value={contextValue}>
<FieldBoundaryProvider>{content}</FieldBoundaryProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const ArrayProperties: PropertiesTableProps = {
status: 'optional',
},
containerMode: {
doc: 'Defines the container mode for all items. Can be `view`, `edit` or `new`. Use `new` to hide the toolbar and show items in `edit` mode. You can provide a function that should return the mode based on your logic. The first parameter is an array with all items. Defaults to `view`.',
doc: 'Defines the container mode for all items. Can be `view`, `edit` or `initial-open`. Use `initial-open` to hide the toolbar and show items in `edit` mode. You can provide a function that should return the mode based on your logic. The first parameter is an array with all items. Defaults to `view`.',
type: ['string', 'function'],
status: 'optional',
},
Expand Down
14 changes: 9 additions & 5 deletions packages/dnb-eufemia/src/extensions/forms/Iterate/Array/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Path, UseFieldProps } from '../../types'
import { Props as FlexContainerProps } from '../../../../components/flex/Container'

export type ContainerMode = 'view' | 'edit' | 'new'
export type ContainerMode = 'view' | 'edit' | 'initial-open'
export type Value = Array<unknown | Record<string, unknown>>
export type ElementChild =
| React.ReactNode
Expand All @@ -12,7 +12,7 @@ export type Props = Omit<
> &
Pick<
UseFieldProps<Value, undefined | Value>,
'value' | 'emptyValue' | 'onChange'
'value' | 'defaultValue' | 'emptyValue' | 'onChange'
> & {
children: ElementChild | Array<ElementChild>
path?: Path
Expand All @@ -26,6 +26,10 @@ export type Props = Omit<
export type ContainerModeWhen = (
items: Array<ElementChild>
) => ContainerMode | undefined
export type ProxyContainerModeWhen = (
isNew: boolean
) => ContainerMode | undefined
export type ProxyContainerModeWhen = ({
currentMode,
hasError,
}: {
currentMode: ContainerMode
hasError: boolean
}) => ContainerMode | undefined
Loading

0 comments on commit 0c6e41c

Please sign in to comment.