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 Down
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` prop with `minimumOneItem`.
langz marked this conversation as resolved.
Show resolved Hide resolved

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
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
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,42 @@ export type Props = {
* An alternative toolbar to be shown in the ViewContainer.
*/
toolbar?: React.ReactNode
/**
* The variant of the toolbar.
*/
toolbarVariant?: 'minimumOneItem'
}

export type AllProps = Props & FlexContainerProps & ArrayItemAreaProps

function ViewContainer(props: AllProps) {
const { children, className, title, toolbar, ...restProps } = props || {}
const { index } = useContext(IterateItemContext)
const {
children,
className,
title,
toolbar,
toolbarVariant,
...restProps
} = props || {}
const { index, arrayValue } = useContext(IterateItemContext)

let itemTitle = title
let ariaLabel = useMemo(() => convertJsxToString(itemTitle), [itemTitle])
if (ariaLabel.includes('{itemNr}')) {
itemTitle = ariaLabel = ariaLabel.replace('{itemNr}', index + 1)
}

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

const hasToolbar =
!toolbar &&
!toolbarElement &&
React.Children.toArray(children).some((child) => {
return child?.['type'] === Toolbar
})
Expand All @@ -51,7 +71,7 @@ function ViewContainer(props: AllProps) {
{children}
{hasToolbar
? null
: toolbar ?? (
: toolbarElement ?? (
<Toolbar>
<EditButton />
<RemoveButton />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ export const ViewContainerProperties: 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',
},
'[FlexVertical](/uilib/layout/flex/container/)': {
doc: 'All Flex.Vertical properties.',
type: 'Various',
Expand Down
Loading
Loading