Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Forms): add toolbarVariant="minimumOneItem" to Iterate.Toolbar for hiding buttons when there is only one item in the array #3919

Merged
merged 7 commits into from
Sep 12, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -378,41 +378,19 @@ export const InitialOpen = () => {

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

<Iterate.EditContainer>
<Iterate.EditContainer toolbarVariant="minimumOneItem">
<Field.SelectCountry
label="Land du er statsborger i"
itemPath="/"
required
/>
<Iterate.Toolbar>
{({ items }) =>
items.length === 1 ? null : (
<>
<Iterate.EditContainer.DoneButton />
<Iterate.EditContainer.CancelButton />
</>
)
}
</Iterate.Toolbar>
</Iterate.EditContainer>
</Iterate.Array>

Expand All @@ -431,3 +409,33 @@ export const InitialOpen = () => {
</ComponentBox>
)
}

export const ToolbarVariantMiniumOneItemOneItem = () => {
return (
<ComponentBox hideCode>
<Iterate.Array value={['foo']}>
<Iterate.ViewContainer toolbarVariant="minimumOneItem">
View Content
</Iterate.ViewContainer>
<Iterate.EditContainer toolbarVariant="minimumOneItem">
Edit Content
</Iterate.EditContainer>
</Iterate.Array>
</ComponentBox>
)
}

export const ToolbarVariantMiniumOneItemTwoItems = () => {
return (
<ComponentBox hideCode>
<Iterate.Array value={['foo', 'bar']}>
<Iterate.ViewContainer toolbarVariant="minimumOneItem">
View Content
</Iterate.ViewContainer>
<Iterate.EditContainer toolbarVariant="minimumOneItem">
Edit Content
</Iterate.EditContainer>
</Iterate.Array>
</ComponentBox>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ With an optional `title` and [Iterate.Toolbar](/uilib/extensions/forms/Iterate/T

### Initially open

This example uses a customized [Iterate.Toolbar](/uilib/extensions/forms/Iterate/Toolbar/).
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.

Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export { ViewAndEditContainer } from '../Array/Examples'
export {
ViewAndEditContainer,
ToolbarVariantMiniumOneItemOneItem,
ToolbarVariantMiniumOneItemTwoItems,
} from '../Array/Examples'
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,15 @@ import * as Examples from './Examples'
## Demos

<Examples.ViewAndEditContainer />

### Toolbar variant

#### Minimum one item

When having one item in the Iterate.Array:

<Examples.ToolbarVariantMiniumOneItemOneItem />

When having two items in the Iterate.Array:

<Examples.ToolbarVariantMiniumOneItemTwoItems />
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,27 @@ render(
)
```

## Get the internal item object
langz marked this conversation as resolved.
Show resolved Hide resolved

You can get the internal item object by using the `Iterate.useItem` hook.

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

const MyItemForm = () => {
const item = Iterate.useItem()
console.log('index:', item.index)

return <Field.String itemPath="/" />
}

render(
<Iterate.Array value={['foo', 'bar']}>
<MyItemForm />
</Iterate.Array>,
)
```

## Customize the Toolbar

```tsx
Expand All @@ -65,23 +86,22 @@ render(
)
```

## Get the internal item object
### Variants

You can get the internal item object by using the `Iterate.useItem` hook.
#### `minimumOneItem`

```tsx
import { Iterate, Field, Value } from '@dnb/eufemia/extensions/forms'
This variant has the following behavior:

const MyItemForm = () => {
const item = Iterate.useItem()
console.log('index:', item.index)
- When `EditContainer` is visible, and the number of items in the array is one, the entire toolbar will be hidden.

return <Field.String itemPath="/" />
}
```tsx
import { Iterate } from '@dnb/eufemia/extensions/forms'

render(
<Iterate.Array value={['foo', 'bar']}>
<MyItemForm />
<Iterate.Array>
<Iterate.EditContainer toolbarVariant="minimumOneItem">
Item Content
</Iterate.EditContainer>
</Iterate.Array>,
)
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ 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/).
The Toolbar is integrated into the [Iterate.ViewContainer](/uilib/extensions/forms/Iterate/ViewContainer/) and the [Iterate.EditContainer](/uilib/extensions/forms/Iterate/EditContainer/).

```tsx
import { Iterate } from '@dnb/eufemia/extensions/forms'
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export { ViewAndEditContainer } from '../Array/Examples'
export {
ViewAndEditContainer,
ToolbarVariantMiniumOneItemOneItem,
ToolbarVariantMiniumOneItemTwoItems,
} from '../Array/Examples'
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,15 @@ import * as Examples from './Examples'
## Demos

<Examples.ViewAndEditContainer />

### Toolbar variant

#### Minimum one item

When having one item in the Iterate.Array:

<Examples.ToolbarVariantMiniumOneItemOneItem />

When having two items in the Iterate.Array:

<Examples.ToolbarVariantMiniumOneItemTwoItems />
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,26 @@ render(
)
```

### Variants

#### `minimumOneItem`

This variant has the following behavior:

- When `ViewContainer` is visible, and the number of items in the array is one, the remove button will be hidden.

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

render(
<Iterate.Array>
<Iterate.ViewContainer toolbarVariant="minimumOneItem">
Item Content
</Iterate.ViewContainer>
</Iterate.Array>,
)
```

## Accessibility

The `Iterate.ViewContainer` component has an `aria-label` attribute, which is set to the `title` prop value. It uses a section element to wrap the content, which helps users with screen readers to get the needed announcement.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Change log for the Eufemia Forms extension.
## v10.48

- Make [Iterate.Toolbar](/uilib/extensions/forms/Iterate/Toolbar/) customizable.
- Add new property `toolbarVariant` to [Iterate.ViewContainer](/uilib/extensions/forms/Iterate/ViewContainer/) and [Iterate.EditContainer](/uilib/extensions/forms/Iterate/EditContainer/) for hiding toolbar buttons when there is only one item in the array.

## v10.46

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,25 @@ export type Props = {
* An alternative toolbar to be shown in the EditContainer.
*/
toolbar?: React.ReactNode
/**
* The variant of the toolbar.
*/
toolbarVariant?: 'minimumOneItem'
}

export type AllProps = Props & FlexContainerProps & ArrayItemAreaProps

export default function EditContainer(props: AllProps) {
const { toolbar, children, ...rest } = props
const { toolbar, toolbarVariant, children, ...rest } = props
const { arrayValue } = useContext(IterateItemContext)

let toolbarElement = toolbar
if (toolbarVariant === 'minimumOneItem' && arrayValue.length <= 1) {
toolbarElement = <></>
}

const hasToolbar =
!toolbar &&
!toolbarElement &&
React.Children.toArray(children).some((child) => {
return child?.['type'] === Toolbar
})
Expand All @@ -48,7 +58,7 @@ export default function EditContainer(props: AllProps) {
toolbar={
hasToolbar
? null
: toolbar ?? (
: toolbarElement ?? (
<Toolbar>
<DoneButton />
<CancelButton />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ export const EditContainerProperties: PropertiesTableProps = {
type: 'React.Node',
status: 'optional',
},
toolbarVariant: {
doc: 'Use variants to render the toolbar differently. Currently there is only the `minimumOneItem` variant. See the info section for more info.',
type: 'string',
status: 'optional',
},
open: {
doc: 'If the container should be open or not. This is taken care of internally by default.',
type: 'boolean',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -589,11 +589,13 @@ describe('EditContainer and ViewContainer', () => {

const { rerender } = render(
<Iterate.Array value={['foo']}>
<Iterate.ViewContainer toolbar={viewToolbar}>
<Iterate.ViewContainer>
View Content
{viewToolbar}
</Iterate.ViewContainer>
<Iterate.EditContainer toolbar={editToolbar}>
<Iterate.EditContainer>
Edit Content
{editToolbar}
</Iterate.EditContainer>
</Iterate.Array>
)
Expand All @@ -617,10 +619,67 @@ describe('EditContainer and ViewContainer', () => {

rerender(
<Iterate.Array value={['foo', 'bar']}>
<Iterate.ViewContainer toolbar={viewToolbar}>
<Iterate.ViewContainer>
View Content
{viewToolbar}
</Iterate.ViewContainer>
<Iterate.EditContainer toolbar={editToolbar}>
<Iterate.EditContainer>
Edit Content
{editToolbar}
</Iterate.EditContainer>
</Iterate.Array>
)

{
const elements = document.querySelectorAll(
'.dnb-forms-iterate__element'
)
expect(elements).toHaveLength(2)

const [firstElement] = Array.from(elements)
const [viewBlock, editBlock] = Array.from(
firstElement.querySelectorAll('.dnb-forms-section-block')
)
expect(editBlock.querySelectorAll('button')).toHaveLength(2)
expect(viewBlock.querySelectorAll('button')).toHaveLength(2)
}
})

it('should render toolbarVariant="minimumOneItem" with correct buttons', () => {
const { rerender } = render(
<Iterate.Array value={['foo']}>
<Iterate.ViewContainer toolbarVariant="minimumOneItem">
View Content
</Iterate.ViewContainer>
<Iterate.EditContainer toolbarVariant="minimumOneItem">
Edit Content
</Iterate.EditContainer>
</Iterate.Array>
)

{
const elements = document.querySelectorAll(
'.dnb-forms-iterate__element'
)
expect(elements).toHaveLength(1)

const [firstElement] = Array.from(elements)
const [viewBlock, editBlock] = Array.from(
firstElement.querySelectorAll('.dnb-forms-section-block')
)
expect(editBlock.querySelectorAll('button')).toHaveLength(0)
expect(viewBlock.querySelectorAll('button')).toHaveLength(1)
expect(viewBlock.querySelectorAll('button')[0]).toHaveTextContent(
tr.viewContainer.editButton
)
}

rerender(
<Iterate.Array value={['foo', 'bar']}>
<Iterate.ViewContainer toolbarVariant="minimumOneItem">
View Content
</Iterate.ViewContainer>
<Iterate.EditContainer toolbarVariant="minimumOneItem">
Edit Content
</Iterate.EditContainer>
</Iterate.Array>
Expand Down
Loading
Loading