Skip to content

Commit

Permalink
feat(Forms): auto-open iterate container items when validation errors…
Browse files Browse the repository at this point in the history
… and make `Iterate.Toolbar` fully customizable
  • Loading branch information
tujoworker committed Sep 10, 2024
1 parent a045acd commit 8fdeecf
Show file tree
Hide file tree
Showing 58 changed files with 1,873 additions and 500 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Field,
Value,
Form,
Tools,
} from '@dnb/eufemia/src/extensions/forms'
export { Default as AnimatedContainer } from '../AnimatedContainer/Examples'

Expand Down Expand Up @@ -172,7 +173,7 @@ export const ArrayFromFormHandler = () => {
}}
onChange={(data) => console.log('DataContext/onChange', data)}
>
<Flex.Vertical>
<Flex.Stack>
<Form.MainHeading>Avengers</Form.MainHeading>

<Card stack>
Expand Down Expand Up @@ -212,7 +213,7 @@ export const ArrayFromFormHandler = () => {
pushValue={{}}
/>
</Card>
</Flex.Vertical>
</Flex.Stack>
</Form.Handler>
</ComponentBox>
)
Expand Down Expand Up @@ -282,7 +283,7 @@ export const ViewAndEditContainer = () => {
accounts: [
{
firstName: 'Tony',
lastName: undefined, // initiate error
lastName: 'Rogers',
},
],
}}
Expand All @@ -291,7 +292,7 @@ export const ViewAndEditContainer = () => {
}
onSubmit={async (data) => console.log('onSubmit', data)}
>
<Flex.Vertical>
<Flex.Stack>
<Form.MainHeading>Accounts</Form.MainHeading>

<Card stack>
Expand All @@ -304,7 +305,7 @@ export const ViewAndEditContainer = () => {
</Card>

<Form.SubmitButton variant="send" />
</Flex.Vertical>
</Flex.Stack>
</Form.Handler>
)
}
Expand Down Expand Up @@ -364,3 +365,80 @@ export const WithVisibility = () => {
</ComponentBox>
)
}

export const InitialOpen = () => {
return (
<ComponentBox scope={{ Iterate, Tools }}>
<Form.Handler
onSubmit={async (data) => console.log('onSubmit', data)}
onSubmitRequest={() => console.log('onSubmitRequest')}
>
<Flex.Stack>
<Form.MainHeading>Statsborgerskap</Form.MainHeading>

<Card align="stretch">
<Iterate.Array path="/countries" defaultValue={[null]}>
<Iterate.ViewContainer
toolbar={
<Iterate.Toolbar>
{({ EditButton, RemoveButton, items }) => {
if (items.length === 1) {
return <EditButton />
}
return (
<>
<EditButton />
<RemoveButton />
</>
)
}}
</Iterate.Toolbar>
}
>
<Value.SelectCountry
label="Land du er statsborger i"
itemPath="/"
/>
</Iterate.ViewContainer>

<Iterate.EditContainer
toolbar={
<Iterate.Toolbar>
{({ DoneButton, CancelButton, items }) => {
if (items.length === 1) {
return null
}

return (
<>
<DoneButton />
<CancelButton />
</>
)
}}
</Iterate.Toolbar>
}
>
<Field.SelectCountry
label="Land du er statsborger i"
itemPath="/"
required
/>
</Iterate.EditContainer>
</Iterate.Array>

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

<Form.SubmitButton variant="send" />

<Tools.Log />
</Flex.Stack>
</Form.Handler>
</ComponentBox>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ With an optional `title` and [Iterate.Toolbar](/uilib/extensions/forms/Iterate/T

<Examples.ViewAndEditContainer />

### Initially open

This example uses a customized [Iterate.Toolbar](/uilib/extensions/forms/Iterate/Toolbar/).

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

<Examples.InitialOpen />

### With DataContext and add/remove buttons

<Examples.ArrayFromFormHandler />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ render(
)
```

### Initial container mode

This section describes the behavior of the `EditContainer` and the `ViewContainer` components.

By default, the container mode is set to `auto`. This means that the container will open (switch to `edit` mode) when there is an error in the container or the value is falsy (empty string, null, undefined, etc.).

When a new item is added via the [Iterate.PushButton](/uilib/extensions/forms/Iterate/PushButton/) component, the item before it will change to `view` mode, if it had no validation errors.

## Filter data

You can filter data by paths specific or all paths.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export const InitiallyOpen = () => {
}
onSubmit={async (data) => console.log('onSubmit', data)}
>
<Flex.Vertical>
<Flex.Stack>
<Form.MainHeading>Accounts</Form.MainHeading>

<Card stack>
Expand All @@ -87,7 +87,7 @@ export const InitiallyOpen = () => {
</Card>

<Form.SubmitButton variant="send" />
</Flex.Vertical>
</Flex.Stack>
</Form.Handler>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ hideInMenu: true

Use `Iterate.Toolbar` to enhance each item in the array with additional functionality. It's particularly useful within components like [Iterate.AnimatedContainer](/uilib/extensions/forms/Iterate/AnimatedContainer) to incorporate a toolbar with extra tools.

The Toolbar is integrated into the containers [Iterate.ViewContainer](/uilib/extensions/forms/Iterate/ViewContainer/) and [Iterate.EditContainer](/uilib/extensions/forms/Iterate/EditContainer/).

```tsx
import { Iterate } from '@dnb/eufemia/extensions/forms'

Expand All @@ -22,4 +24,46 @@ render(
)
```

The Toolbar is integrated into the containers [Iterate.ViewContainer](/uilib/extensions/forms/Iterate/ViewContainer/) and [Iterate.EditContainer](/uilib/extensions/forms/Iterate/EditContainer/).
## Cusomize the Toolbar

You can customize the toolbar by passing a function as a child to `Iterate.Toolbar`. The function receives the following parameters as an object:

- `index` the index of the current item in the array.
- `items` the array of items.
- `EditButton` used in the `Iterate.ViewContainer`.
- `RemoveButton` used in the `Iterate.ViewContainer`.
- `CancelButton` used in the `Iterate.EditContainer`.
- `DoneButton` used in the `Iterate.EditContainer`.

```tsx
import { Iterate } from '@dnb/eufemia/extensions/forms'

render(
<Iterate.Array>
<Iterate.ViewContainer>
Item Content
<Iterate.Toolbar>
{({
index,
items,
EditButton,
RemoveButton,
CancelButton,
DoneButton,
}) => {
if (items.length === 1) {
return null
}

return (
<>
<EditButton />
<RemoveButton />
</>
)
}}
</Iterate.Toolbar>
</Iterate.ViewContainer>
</Iterate.Array>,
)
```
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ breadcrumb:

Change log for the Eufemia Forms extension.

## v10.48

- Make [Iterate.Toolbar](/uilib/extensions/forms/Iterate/Toolbar/) customizable when used inside [Iterate.Array](/uilib/extensions/forms/Iterate/Array/).

## v10.46

- Added [Value.SelectCountry](/uilib/extensions/forms/Value/SelectCountry/) component to render a country value.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export interface ContextState {
handlePathChangeUnvalidated: (path: Path, value: any) => void
updateDataValue: (path: Path, value: any) => void
setData: (data: any) => void
clearData?: () => void
mutateDataHandler?: (data: any, mutate: TransformData) => any
filterDataHandler?: (data: any, filter: FilterData) => any
validateData: () => void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function FieldBoundaryProvider({ children }) {

const errorsRef = useRef<Record<Path, boolean>>({})
const showBoundaryErrorsRef = useRef<boolean>(false)
const hasError = Object.keys(errorsRef.current || {}).length > 0
const hasError = Object.keys(errorsRef.current).length > 0
const hasSubmitError = showAllErrors && hasError

const setFieldError = useCallback((path: Path, error: Error) => {
Expand All @@ -20,6 +20,7 @@ export default function FieldBoundaryProvider({ children }) {
} else {
delete errorsRef.current?.[path]
}
forceUpdate()
}, [])

const setShowBoundaryErrors = useCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,7 @@ export default function Provider<Data extends JsonObject>(
validateData,
updateDataValue,
setData,
clearData,
filterDataHandler,
addOnChangeHandler,
setHandleSubmit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ function SelectCountry(props: Props) {
label={label}
input_icon={false}
data={dataRef.current}
value={value}
value={typeof value === 'string' ? value : null}
disabled={disabled}
on_show={fillData}
on_focus={onFocusHandler}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ describe('Field.String', () => {
await userEvent.type(input, '{Backspace>3}aBc')

expect(input).toHaveValue('ABC')
expect(transformIn).toHaveBeenCalledTimes(13)
expect(transformIn).toHaveBeenCalledTimes(7)
expect(transformIn).toHaveBeenLastCalledWith('abc')
expect(transformOut).toHaveBeenCalledTimes(6)
expect(transformOut).toHaveBeenLastCalledWith('ABc')
Expand All @@ -289,7 +289,7 @@ describe('Field.String', () => {
await userEvent.type(input, '{Backspace>3}EfG')

expect(input).toHaveValue('EFG')
expect(transformIn).toHaveBeenCalledTimes(25)
expect(transformIn).toHaveBeenCalledTimes(13)
expect(transformIn).toHaveBeenLastCalledWith('efg')
expect(transformOut).toHaveBeenCalledTimes(12)
expect(transformOut).toHaveBeenLastCalledWith('EFG')
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
Loading

0 comments on commit 8fdeecf

Please sign in to comment.