Skip to content

Commit

Permalink
feat(forms): add Value.Selection component (#3857)
Browse files Browse the repository at this point in the history
- [x] Fix and update tests
- [x] Improve and fix Value rendering

---------

Co-authored-by: Tobias Høegh <tobias@tujo.no>
  • Loading branch information
joakbjerk and tujoworker authored Aug 26, 2024
1 parent 13fc1a8 commit 7432986
Show file tree
Hide file tree
Showing 18 changed files with 571 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const FieldArraySelectionAndOption = () => {
<Field.Option value="baz" title="Baz" />
</Field.ArraySelection>

<Value.ArraySelection label="My selections" path="/myPath" />
<Value.ArraySelection inheritLabel path="/myPath" />
</Flex.Stack>
</Form.Handler>
</ComponentBox>
Expand All @@ -67,7 +67,7 @@ export const FieldArraySelectionPath = () => {
>
<Flex.Stack>
<Field.ArraySelection label="My selections" path="/myPath" />
<Value.ArraySelection label="My selections" path="/myPath" />
<Value.ArraySelection inheritLabel path="/myPath" />
</Flex.Stack>
</Form.Handler>
</ComponentBox>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
title: 'Selection'
description: '`Value.Selection` is a component for displaying a string value based on a user selection.'
componentType: 'base-value'
hideInMenu: true
showTabs: true
tabs:
- title: Info
key: '/info'
- title: Demos
key: '/demos'
- title: Properties
key: '/properties'
breadcrumb:
- text: Forms
href: /uilib/extensions/forms/
- text: Value
href: /uilib/extensions/forms/Value/
- text: Selection
href: /uilib/extensions/forms/Value/Selection/
---

import Info from 'Docs/uilib/extensions/forms/Value/Selection/info'
import Demos from 'Docs/uilib/extensions/forms/Value/Selection/demos'

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

export const Placeholder = () => {
return (
<ComponentBox scope={{ Value }}>
<Value.Selection placeholder="No values selected" />
</ComponentBox>
)
}

export const WithValue = () => {
return (
<ComponentBox scope={{ Value }}>
<Value.Selection value="Bar" />
</ComponentBox>
)
}

export const Label = () => {
return (
<ComponentBox scope={{ Value }}>
<Value.Selection label="Label text" showEmpty />
</ComponentBox>
)
}

export const LabelAndValue = () => {
return (
<ComponentBox scope={{ Value }}>
<Value.Selection label="Label text" value="Foo" />
</ComponentBox>
)
}

export const Inline = () => {
return (
<ComponentBox scope={{ Value }}>
<P>
This is before the component
<Value.Selection value="Baz" inline />
This is after the component
</P>
</ComponentBox>
)
}

export const FieldSelectionPath = () => {
return (
<ComponentBox>
<Form.Handler
data={{
selection: 'bar',
myList: [
{ value: 'foo', title: 'Foo' },
{ value: 'bar', title: 'Bar' },
{ value: 'baz', title: 'Baz' },
],
}}
>
<Flex.Stack>
<Field.Selection
path="/selection"
dataPath="/myList"
variant="radio"
label="My selection"
/>
<Value.Selection
path="/selection"
dataPath="/myList"
inheritLabel
/>
</Flex.Stack>
</Form.Handler>
</ComponentBox>
)
}

export const FieldSelectionAndOption = () => {
return (
<ComponentBox>
<Form.Handler>
<Flex.Stack>
<Field.Selection
label="My selection"
path="/myPath"
variant="radio"
value="bar"
>
<Field.Option value="foo" title="Foo" />
<Field.Option value="bar" title="Bar" />
<Field.Option value="baz" title="Baz" />
</Field.Selection>

<Value.Selection label="My selection" path="/myPath" />
</Flex.Stack>
</Form.Handler>
</ComponentBox>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
showTabs: true
---

import * as Examples from './Examples'

## Demos

### Placeholder

<Examples.Placeholder />

### Value

<Examples.WithValue />

### Label

<Examples.Label />

### Label and value

<Examples.LabelAndValue />

### Inline

<Examples.Inline />

### Field.Selection with path

When using the same `path` as on a `Field.Selection`, the title will be used as the displayed value.

<Examples.FieldSelectionPath />

### Field.Option and Field.Selection

When using the same `path` as on a `Field.Selection`, the `Field.Option` title will be used as the displayed value.

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

## Description

`Value.Selection` is a component for displaying a string value based on a user selection.

There is a corresponding [Field.Selection](/uilib/extensions/forms/base-fields/Selection) component.

```jsx
import { Value } from '@dnb/eufemia/extensions/forms'
render(<Value.Selection />)
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
showTabs: true
---

import TranslationsTable from 'dnb-design-system-portal/src/shared/parts/TranslationsTable'
import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable'
import { SelectionProperties } from '@dnb/eufemia/src/extensions/forms/Value/Selection/SelectionDocs'
import { ValueProperties } from '@dnb/eufemia/src/extensions/forms/Value/ValueDocs'

## Properties

### Field-specific props

<PropertiesTable props={SelectionProperties} />

### General props

<PropertiesTable props={ValueProperties} />
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ showTabs: true

[Field.Option](/uilib/extensions/forms/base-fields/Option/) is a related component.

There is a corresponding [Value.Selection](/uilib/extensions/forms/Value/Selection) component.

```tsx
import { Field } from '@dnb/eufemia/extensions/forms'
render(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Change log for the Eufemia Forms extension.

- Added [Iterate.PushContainer](/uilib/extensions/forms/Iterate/PushContainer/) to create new items in an array.
- Added [Value.ArraySelection](/uilib/extensions/forms/Value/ArraySelection/) component to render an array of values.
- Added [Value.Selection](/uilib/extensions/forms/Value/Selection/) component to render a selection value.

## v10.43

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import React, { useCallback, useContext, useMemo } from 'react'
import { Checkbox, HelpButton, ToggleButton } from '../../../../components'
import classnames from 'classnames'
import OptionField from '../Option'
import FieldBlock from '../../FieldBlock'
import { useFieldProps } from '../../hooks'
import { ReturnAdditional } from '../../hooks/useFieldProps'
import { FieldHelpProps, FieldProps, FormError } from '../../types'
import { pickSpacingProps } from '../../../../components/flex/utils'
import { getStatus } from '../Selection'
import { getStatus, mapOptions } from '../Selection'
import { HelpButtonProps } from '../../../../components/HelpButton'
import ToggleButtonGroupContext from '../../../../components/toggle-button/ToggleButtonGroupContext'
import DataContext from '../../DataContext/Context'
Expand Down Expand Up @@ -219,6 +218,7 @@ export function useCheckboxOrToggleOptions({
/>
)
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[
disabled,
emptyValue,
Expand All @@ -234,29 +234,7 @@ export function useCheckboxOrToggleOptions({
]
)

const mapOptions = useCallback(
(children: React.ReactNode) => {
return React.Children.toArray(children).map(
(child: React.ReactElement<OptionProps>, i) => {
if (React.isValidElement(child)) {
if (child.type === OptionField) {
return createOption(child.props, i)
}

if (child.props.children) {
const nestedChildren = mapOptions(child.props.children)
return React.cloneElement(child, child.props, nestedChildren)
}
}

return child
}
)
},
[createOption]
)

const result = mapOptions(children)
const result = mapOptions(children, { createOption })

if (path) {
setFieldProps?.(path + '/arraySelectionData', collectedData)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,10 +324,6 @@ function renderRadioItems({
const optionsCount =
React.Children.count(children) + (dataList?.length || 0)

const Component = (
variant === 'radio' ? Radio : ToggleButton
) as typeof Radio & typeof ToggleButton

const createOption = (props: OptionProps, i: number) => {
const { error, title, help, children, ...rest } = props

Expand All @@ -339,6 +335,10 @@ function renderRadioItems({
</HelpButton>
) : undefined

const Component = (
variant === 'radio' ? Radio : ToggleButton
) as typeof Radio & typeof ToggleButton

return (
<Component
id={optionsCount === 1 ? id : undefined}
Expand All @@ -354,34 +354,36 @@ function renderRadioItems({
)
}

const mapOptions = (children: React.ReactNode) => {
return React.Children.map(
children,
(child: React.ReactElement<OptionProps>, i) => {
if (React.isValidElement(child)) {
if (child.type === OptionField) {
return createOption(child.props, i)
}

if (child.props.children) {
const nestedChildren = mapOptions(child.props.children)
return React.cloneElement(child, child.props, nestedChildren)
}
}

return child
}
)
}

return [
...(dataList || []).map((props, i) => {
return createOption(props as OptionProps, i)
}),
...(mapOptions(children) || []),
...(mapOptions(children, { createOption }) || []),
].filter(Boolean)
}

export function mapOptions(children: React.ReactNode, { createOption }) {
return React.Children.map(
children,
(child: React.ReactElement<OptionProps>, i) => {
if (React.isValidElement(child)) {
if (child.type === OptionField) {
return createOption(child.props, i)
}

if (child.props.children) {
const nestedChildren = mapOptions(child.props.children, {
createOption,
})
return React.cloneElement(child, child.props, nestedChildren)
}
}

return child
}
)
}

export function makeOptions<T = DrawerListProps['data']>(
children: React.ReactNode
): T {
Expand Down
Loading

0 comments on commit 7432986

Please sign in to comment.