diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Array/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Array/Examples.tsx index 0154e03209c..4068ebed55b 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Array/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Array/Examples.tsx @@ -378,41 +378,19 @@ export const InitialOpen = () => { - + - - {({ items }) => - items.length === 1 ? ( - - ) : ( - <> - - - - ) - } - - + - - {({ items }) => - items.length === 1 ? null : ( - <> - - - - ) - } - diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Array/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Array/demos.mdx index f56b88b5021..b378f85f54c 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Array/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Array/demos.mdx @@ -51,9 +51,9 @@ 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 `toolbarVariant` prop with `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. +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. diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/EditContainer/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/EditContainer/info.mdx index 362ac4bdcc0..bc187efbd48 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/EditContainer/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/EditContainer/info.mdx @@ -47,6 +47,27 @@ render( ) ``` +## Get the internal item object + +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 +} + +render( + + + , +) +``` + ## Customize the Toolbar ```tsx @@ -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 -} +```tsx +import { Iterate } from '@dnb/eufemia/extensions/forms' render( - - + + + Item Content + , ) ``` diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Toolbar/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Toolbar/info.mdx index 528c891b984..065e0a078af 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Toolbar/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Toolbar/info.mdx @@ -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' diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/ViewContainer/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/ViewContainer/info.mdx index 3002f328cd2..5205c5e49d6 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/ViewContainer/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/ViewContainer/info.mdx @@ -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( + + + Item Content + + , +) +``` + ## 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. diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/changelog.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/changelog.mdx index 255bcbe57a6..ea188547b87 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/changelog.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/changelog.mdx @@ -15,7 +15,8 @@ Change log for the Eufemia Forms extension. ## v10.48 -- Make [Iterate.Toolbar](/uilib/extensions/forms/Iterate/Toolbar/) customizable. +- Make [Iterate.Toolbar](/uilib/extensions/forms/Iterate/Toolbar/) customizable when used inside [Iterate.Array](/uilib/extensions/forms/Iterate/Array/). +- Add new `toolbarVariant` to [Iterate.ViewContainer](/uilib/extensions/forms/Iterate/ViewContainer/) and [Iterate.EditContainer](/uilib/extensions/forms/Iterate/EditContainer/) for hiding the buttons when there is only one item in the array. ## v10.46 diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/EditContainer.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/EditContainer.tsx index 5f92fae827f..c9bff7b0cca 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/EditContainer.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/EditContainer.tsx @@ -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 }) @@ -48,7 +58,7 @@ export default function EditContainer(props: AllProps) { toolbar={ hasToolbar ? null - : toolbar ?? ( + : toolbarElement ?? ( diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/EditContainerDocs.ts b/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/EditContainerDocs.ts index 4d2295cb9f7..167d73bb836 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/EditContainerDocs.ts +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/EditContainerDocs.ts @@ -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', diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/__tests__/EditAndViewContainer.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/__tests__/EditAndViewContainer.test.tsx index bcc78da8312..c3fb3b9475d 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/__tests__/EditAndViewContainer.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/__tests__/EditAndViewContainer.test.tsx @@ -589,11 +589,13 @@ describe('EditContainer and ViewContainer', () => { const { rerender } = render( - + View Content + {viewToolbar} - + Edit Content + {editToolbar} ) @@ -617,10 +619,67 @@ describe('EditContainer and ViewContainer', () => { rerender( - + View Content + {viewToolbar} - + + Edit Content + {editToolbar} + + + ) + + { + 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( + + + View Content + + + Edit Content + + + ) + + { + 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( + + + View Content + + Edit Content diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/ViewContainer/ViewContainer.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/ViewContainer/ViewContainer.tsx index 2c29b06c896..0ab74348dd9 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/ViewContainer/ViewContainer.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/ViewContainer/ViewContainer.tsx @@ -19,13 +19,24 @@ 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]) @@ -33,8 +44,17 @@ function ViewContainer(props: AllProps) { itemTitle = ariaLabel = ariaLabel.replace('{itemNr}', index + 1) } + let toolbarElement = toolbar + if (toolbarVariant === 'minimumOneItem' && arrayValue.length <= 1) { + toolbarElement = ( + + + + ) + } + const hasToolbar = - !toolbar && + !toolbarElement && React.Children.toArray(children).some((child) => { return child?.['type'] === Toolbar }) @@ -51,7 +71,7 @@ function ViewContainer(props: AllProps) { {children} {hasToolbar ? null - : toolbar ?? ( + : toolbarElement ?? ( diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/ViewContainer/ViewContainerDocs.ts b/packages/dnb-eufemia/src/extensions/forms/Iterate/ViewContainer/ViewContainerDocs.ts index 2cfdc9fd0ce..5c2a7fb2dd0 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/ViewContainer/ViewContainerDocs.ts +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/ViewContainer/ViewContainerDocs.ts @@ -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', diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/Iterate.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/Iterate.stories.tsx index fb62fd9046d..9c66ade692f 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/Iterate.stories.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/Iterate.stories.tsx @@ -148,10 +148,10 @@ export const InitialOpen = () => { const MyEditItem = useCallback(() => { return ( - + - + {/* {({ items }) => { if (items.length === 1) { return null @@ -164,20 +164,20 @@ export const InitialOpen = () => { ) }} - + */} ) }, [MyEditItemForm]) const MyViewItem = useCallback(() => { return ( - + - + {/* {({ items }) => { if (items.length === 1) { return @@ -190,7 +190,7 @@ export const InitialOpen = () => { ) }} - + */} ) }, [])