Skip to content

Commit

Permalink
feat(Forms): add Iterate.ItemNo component (#4095)
Browse files Browse the repository at this point in the history
Quick example:

```tsx
<Iterate.Array value={['foo', 'bar']}>
  <Form.SubHeading>
    <Iterate.ItemNo>{'Item no. {itemNo} string'}</Iterate.ItemNo>
  </Form.SubHeading>
</Iterate.Array>
```

Here is a
[demo](https://eufemia-git-feat-iterate-item-no-component-eufemia.vercel.app/uilib/extensions/forms/Iterate/ItemNo/)
of it.
  • Loading branch information
tujoworker authored Oct 9, 2024
1 parent 145399e commit c736c9e
Show file tree
Hide file tree
Showing 13 changed files with 157 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
title: 'Count'
description: '`Iterate.Count` is a helper component / function that returns the count of a data array or object.'
order: 11
showTabs: true
tabs:
- title: Info
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
title: 'ItemNo'
description: '`Iterate.ItemNo` is a helper component that can be used to render the current item number (index) in a given string.'
showTabs: true
tabs:
- title: Info
key: '/info'
- title: Demos
key: '/demos'
breadcrumb:
- text: Forms
href: /uilib/extensions/forms/
- text: Extended features
href: /uilib/extensions/forms/
- text: Iterate
href: /uilib/extensions/forms/Iterate/
- text: ItemNo
href: /uilib/extensions/forms/Iterate/ItemNo/
---

import Info from 'Docs/uilib/extensions/forms/Iterate/ItemNo/info'
import Demos from 'Docs/uilib/extensions/forms/Iterate/ItemNo/demos'

<Info />
<Demos />
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
import { Form, Iterate } from '@dnb/eufemia/src/extensions/forms'

export const Default = () => {
return (
<ComponentBox>
<Iterate.Array value={['foo', 'bar']}>
<Form.SubHeading>
<Iterate.ItemNo>{'Item no. {itemNo}'}</Iterate.ItemNo>
</Form.SubHeading>
</Iterate.Array>
</ComponentBox>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
showTabs: true
---

import * as Examples from './Examples'

## Demos

### Default

<Examples.Default />
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
showTabs: true
---

## Description

`Iterate.ItemNo` is a helper component that can be used to render the current item number (index) in a given string. It will replace `{itemNo}` with the current item number.

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

const myString = 'Item no. {itemNo}'

render(
<Iterate.Array value={['foo', 'bar']}>
<Form.SubHeading>
<Iterate.ItemNo>{myString}</Iterate.ItemNo>
</Form.SubHeading>
</Iterate.Array>,
)
```
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: 'PushContainer'
description: '`Iterate.PushContainer` enables users to create a new item in the array.'
order: 9
order: 7
showTabs: true
tabs:
- title: Info
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
title: 'Toolbar'
description: '`Iterate.Toolbar` is a helper component to be used within an `Iterate.AnimatedContainer` to add a toolbar to each item in the array.'
order: 10
showTabs: true
tabs:
- title: Info
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Toolbar from '../Toolbar'
import { useSwitchContainerMode } from '../hooks'
import DoneButton from './DoneButton'
import CancelButton, { useWasNew } from './CancelButton'
import { replaceItemNo } from '../ItemNo'

export type Props = {
/**
Expand Down Expand Up @@ -88,26 +89,20 @@ export function EditContainerWithoutToolbar(
} = props || {}

const wasNew = useWasNew({ isNew, containerMode })
let itemTitle = wasNew && titleWhenNew ? titleWhenNew : title
let ariaLabel = useMemo(() => convertJsxToString(itemTitle), [itemTitle])
if (ariaLabel.includes('{itemN')) {
/**
* {itemNr} is deprecated, and can be removed in v11 in favor of {itemNo}
* So in v11 we can use '{itemNo}' instead of a regex
*/
itemTitle = ariaLabel = ariaLabel.replace(
/\{itemN(r|o)\}/g,
String(index + 1)
const itemTitle = useMemo(() => {
return replaceItemNo(
wasNew && titleWhenNew ? titleWhenNew : title,
index
)
}
}, [index, title, titleWhenNew, wasNew])

useSwitchContainerMode({ path })

return (
<ArrayItemArea
mode="edit"
className={classnames('dnb-forms-section-edit-block', className)}
ariaLabel={ariaLabel}
ariaLabel={convertJsxToString(itemTitle)}
{...restProps}
>
{itemTitle && <Lead size="basis">{itemTitle}</Lead>}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, { useMemo } from 'react'
import { useItem } from '../hooks'
import { convertJsxToString } from '../../../../shared/component-helper'

function ItemNo({ children }) {
const { index } = useItem()

children = useMemo(
() => replaceItemNo(children, index),
[children, index]
)

return <>{replaceItemNo(children, index)}</>
}

export function replaceItemNo(
children: React.ReactNode,
index: number
): string | React.ReactNode {
const text =
typeof children !== 'string' ? convertJsxToString(children) : children

if (text.includes('{itemN')) {
/**
* {itemNr} is deprecated, and can be removed in v11 in favor of {itemNo}
* So in v11 we can use '{itemNo}' instead of a regex
*/
return text.replace(/\{itemN(r|o)\}/g, String(index + 1))
}

return children
}

ItemNo._supportsSpacingProps = false
export default ItemNo
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from 'react'
import { render } from '@testing-library/react'
import { Iterate } from '../../..'

describe('Iterate.ItemNo', () => {
it('should replace {itemNo} in children given as a string', () => {
render(
<Iterate.Array value={['foo']}>
<Iterate.ItemNo>{'Item no. {itemNo} string'}</Iterate.ItemNo>
</Iterate.Array>
)
expect(document.body).toHaveTextContent('Item no. 1 string')
})

it('should replace several array items', () => {
render(
<Iterate.Array value={['foo', 'bar']}>
<Iterate.ItemNo>{'Item no. {itemNo} string'}</Iterate.ItemNo>
</Iterate.Array>
)
expect(document.body).toHaveTextContent('Item no. 1 string')
expect(document.body).toHaveTextContent('Item no. 2 string')
})

it('should remove jsx and return only a string', () => {
render(
<Iterate.Array value={['foo']}>
<Iterate.ItemNo>
<b>{'Item no. {itemNo} string'}</b>
</Iterate.ItemNo>
</Iterate.Array>
)
expect(document.body).toHaveTextContent('Item no. 1 string')
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default } from './ItemNo'
export * from './ItemNo'
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import IterateItemContext from '../IterateItemContext'
import Toolbar from '../Toolbar'
import EditButton from './EditButton'
import RemoveButton from './RemoveButton'
import { replaceItemNo } from '../ItemNo'

export type Props = {
/**
Expand Down Expand Up @@ -37,19 +38,9 @@ function ViewContainer(props: AllProps) {
...restProps
} = props || {}
const { index, arrayValue } = useContext(IterateItemContext)

let itemTitle = title
let ariaLabel = useMemo(() => convertJsxToString(itemTitle), [itemTitle])
if (ariaLabel.includes('{itemN')) {
/**
* {itemNr} is deprecated, and can be removed in v11 in favor of {itemNo}
* So in v11 we can use '{itemNo}' instead of a regex
*/
itemTitle = ariaLabel = ariaLabel.replace(
/\{itemN(r|o)\}/g,
String(index + 1)
)
}
const itemTitle = useMemo(() => {
return replaceItemNo(title, index)
}, [index, title])

let toolbarElement = toolbar
if (toolbarVariant === 'minimumOneItem' && arrayValue.length <= 1) {
Expand All @@ -69,7 +60,7 @@ function ViewContainer(props: AllProps) {
return (
<ArrayItemArea
mode="view"
ariaLabel={ariaLabel}
ariaLabel={convertJsxToString(itemTitle)}
className={classnames('dnb-forms-section-view-block', className)}
{...restProps}
>
Expand Down
1 change: 1 addition & 0 deletions packages/dnb-eufemia/src/extensions/forms/Iterate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export { default as EditContainer } from './EditContainer'
export { default as ViewContainer } from './ViewContainer'
export { default as AnimatedContainer } from './AnimatedContainer'
export { default as Toolbar } from './Toolbar'
export { default as ItemNo } from './ItemNo'
export { useCount, count, Count } from './Count'
export { default as useItem } from './hooks/useItem'
export { default as IterateItemContext } from './IterateItemContext'
Expand Down

0 comments on commit c736c9e

Please sign in to comment.