Skip to content

Commit

Permalink
fix(Forms): keep Iterate.EditContainer open when falsy value or emp…
Browse files Browse the repository at this point in the history
…ty object was given as the iterate value (#4087)

Fixes #4046
  • Loading branch information
tujoworker authored Oct 9, 2024
1 parent 9baaa37 commit e932059
Show file tree
Hide file tree
Showing 12 changed files with 466 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Value,
Form,
Tools,
Wizard,
} from '@dnb/eufemia/src/extensions/forms'
export { Default as AnimatedContainer } from '../AnimatedContainer/Examples'

Expand Down Expand Up @@ -368,7 +369,62 @@ export const WithVisibility = () => {
)
}

export const InitialOpen = () => {
export const InitiallyOpen = () => {
return (
<ComponentBox scope={{ Iterate, Tools }}>
<Form.Handler required>
<Wizard.Container>
<Wizard.Step>
<Card stack>
<Iterate.Array path="/myList" defaultValue={[{}]}>
<Iterate.ViewContainer>
<Value.String label="Item {itemNo}" itemPath="/foo" />
</Iterate.ViewContainer>
<Iterate.EditContainer>
<Field.String
label="Item {itemNo}"
itemPath="/foo"
defaultValue="foo"
/>
</Iterate.EditContainer>
</Iterate.Array>

<Iterate.PushButton
text="Add"
path="/myList"
variant="tertiary"
pushValue={{}}
/>
</Card>

<Wizard.Buttons />
</Wizard.Step>

<Wizard.Step>
<Iterate.Array path="/myList" defaultValue={[{}]}>
<Iterate.EditContainer>
<Field.String
label="Item {itemNo}"
itemPath="/foo"
defaultValue="foo"
/>
</Iterate.EditContainer>
<Iterate.ViewContainer>
<Value.String label="Item {itemNo}" itemPath="/foo" />
</Iterate.ViewContainer>
</Iterate.Array>

<Wizard.Buttons />
</Wizard.Step>
</Wizard.Container>

<Tools.Log top />
</Form.Handler>
</ComponentBox>
)
}

export const InitialOpenWithToolbarVariant = () => {
return (
<ComponentBox scope={{ Iterate, Tools }}>
{() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,15 @@ With an optional `title` and [Iterate.Toolbar](/uilib/extensions/forms/Iterate/T

### Initially open

<Examples.InitiallyOpen />

### Minium one item

This example uses the container's `toolbarVariant` property with the value `minimumOneItem`.

It hides the toolbar in the `EditContainer` when there is only one item in the array. And it hides the remove button in the `ViewContainer` when there is only one item in the array.

<Examples.InitialOpen />
<Examples.InitialOpenWithToolbarVariant />

### With DataContext and add/remove buttons

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import {
Field,
Form,
Iterate,
Tools,
Value,
} from '@dnb/eufemia/src/extensions/forms'
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
import { Card, Flex } from '@dnb/eufemia/src'
import React from 'react'

export { ViewAndEditContainer } from '../Array/Examples'

Expand Down Expand Up @@ -97,3 +99,167 @@ export const InitiallyOpen = () => {
</ComponentBox>
)
}

export const IsolatedData = () => {
return (
<ComponentBox scope={{ Tools }}>
{() => {
const formData = {
persons: [
{
firstName: 'Ola',
lastName: 'Nordmann',
},
{
firstName: 'Kari',
lastName: 'Nordmann',
},
{
firstName: 'Per',
lastName: 'Hansen',
},
],
}

function RepresentativesView() {
return (
<Iterate.ViewContainer>
<Value.Composition>
<Value.String itemPath="/firstName" />
<Value.String itemPath="/lastName" />
</Value.Composition>
</Iterate.ViewContainer>
)
}

function RepresentativesEdit() {
return (
<Iterate.EditContainer>
<Field.Name.First itemPath="/firstName" />
<Field.Name.Last itemPath="/lastName" />
</Iterate.EditContainer>
)
}

function ExistingPersonDetails() {
const { data, getValue } = Form.useData()
const person = getValue(data.selectedPerson)?.data || {}

return (
<Flex.Stack>
<Field.Name.First
readOnly
itemPath="/firstName"
value={person.firstName}
/>
<Field.Name.Last
readOnly
itemPath="/lastName"
value={person.lastName}
/>
</Flex.Stack>
)
}

function NewPersonDetails() {
return (
<Flex.Stack>
<Field.Name.First required itemPath="/firstName" />
<Field.Name.Last required itemPath="/lastName" />
</Flex.Stack>
)
}

function PushContainerContent() {
const { data, update } = Form.useData()

// Clear the PushContainer data when the selected person is "other",
// so the fields do not inherit existing data.
React.useLayoutEffect(() => {
if (data.selectedPerson === 'other') {
update('/pushContainerItems/0', {})
}
}, [data.selectedPerson, update])

return (
<Flex.Stack>
<Field.Selection
variant="radio"
required
path="/selectedPerson"
dataPath="/persons"
>
<Field.Option value="other" label="Other person" />
</Field.Selection>
<Form.Visibility
visibleWhen={{
path: '/selectedPerson',
hasValue: (value) =>
typeof value === 'string' && value !== 'other',
}}
>
<ExistingPersonDetails />
</Form.Visibility>

<Form.Visibility
visibleWhen={{
path: '/selectedPerson',
hasValue: (value) => value === 'other',
}}
>
<NewPersonDetails />
</Form.Visibility>
</Flex.Stack>
)
}

function RepresentativesCreateNew() {
return (
<Iterate.PushContainer
path="/representatives"
title="Add new representative"
isolatedData={{
persons: formData.persons.map((data, i) => {
return {
title: [data.firstName, data.lastName].join(' '),
value: '/persons/' + i,
data,
}
}),
}}
openButton={
<Iterate.PushContainer.OpenButton
variant="tertiary"
text="Add new representative"
/>
}
showOpenButtonWhen={(list) => list.length > 0}
>
<PushContainerContent />
</Iterate.PushContainer>
)
}

return (
<Form.Handler>
<Form.MainHeading>Representatives</Form.MainHeading>
<Flex.Stack>
<Card stack>
<Iterate.Array path="/representatives">
<RepresentativesView />
<RepresentativesEdit />
</Iterate.Array>
<RepresentativesCreateNew />
</Card>

<Card stack>
<Form.SubHeading>Data Context</Form.SubHeading>
<Tools.Log placeholder="-" />
</Card>
</Flex.Stack>
</Form.Handler>
)
}}
</ComponentBox>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,9 @@ import * as Examples from './Examples'
### With existing data

<Examples.ViewAndEditContainer />

### Isolated data

This demo shows how to use the `isolatedData` property to provide data to the PushContainer.

<Examples.IsolatedData />
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from '../../../../components/flex/Container'
import IterateItemContext, {
IterateItemContextState,
ModeOptions,
} from '../IterateItemContext'
import SummaryListContext from '../../Value/SummaryList/SummaryListContext'
import ValueBlockContext from '../../ValueBlock/ValueBlockContext'
Expand Down Expand Up @@ -117,7 +118,7 @@ function ArrayComponent(props: Props) {
{
current: ContainerMode
previous?: ContainerMode
options?: { omitFocusManagement?: boolean }
options?: ModeOptions
}
>
>({})
Expand Down Expand Up @@ -180,7 +181,9 @@ function ArrayComponent(props: Props) {
modesRef.current[id].current = mode
modesRef.current[id].options = options
delete isNewRef.current?.[id]
forceUpdate()
if (options?.preventUpdate !== true) {
forceUpdate()
}
},
handleChange: (path, value) => {
const newArrayValue = structuredClone(arrayValue)
Expand Down
Loading

0 comments on commit e932059

Please sign in to comment.