Skip to content

Commit

Permalink
feat(Forms): merge FieldGroup into FieldBlock (#2645)
Browse files Browse the repository at this point in the history
FieldGroup and FieldBlock has been merged into one component named
FieldBlock. In addition, I added more example code and description in
the Create your own component section.

---------

Co-authored-by: Tobias Høegh <[email protected]>
  • Loading branch information
henit and tujoworker authored Sep 13, 2023
1 parent 9a2e52f commit 56a1867
Show file tree
Hide file tree
Showing 19 changed files with 363 additions and 333 deletions.
Original file line number Diff line number Diff line change
@@ -1,51 +1,152 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { useCallback } from 'react'
import ComponentBox from '../../../../shared/tags/ComponentBox'
import { Input, Slider } from '@dnb/eufemia/src'
import {
DataContext,
Layout,
StepsLayout,
Field,
Value,
Visibility,
FieldGroup,
FieldBlock,
useDataValue,
} from '@dnb/eufemia/src/extensions/forms'

export const FirstExampleDemo = () => {
export const CreateBasicFieldComponent = () => {
return (
<ComponentBox
scope={{
DataContext,
Layout,
Field,
FieldGroup,
FieldBlock,
useDataValue,
}}
>
{() => {
const MyComponent = (props) => {
const { value } = useDataValue(props)
const MyCustomField = (props) => {
const preparedProps = {
...props,
validator: (value) => {
return value === 'secret'
? new Error('Do not reveal the secret!')
: undefined
},
}

const {
info,
warning,
error,
value,
handleChange,
handleFocus,
handleBlur,
} = useDataValue(preparedProps)

return (
<FieldBlock
label="What is the secret of the custom field?"
info={info}
warning={warning}
error={error}
>
<Input
value={value}
on_change={({ value }) => handleChange(value)}
on_focus={handleFocus}
on_blur={handleBlur}
/>
</FieldBlock>
)
}

return (
<MyCustomField
value="Nothing to see here"
onChange={(value) => console.log('onChange', value)}
/>
)
}}
</ComponentBox>
)
}

export const CreateComposedFieldComponent = () => {
return (
<ComponentBox
scope={{
DataContext,
Layout,
Field,
FieldBlock,
Slider,
useDataValue,
useCallback,
}}
>
{() => {
const MyComposedField = (props) => {
const birthYear = useDataValue({
path: '/birthYear',
})

const handleBirthYearChange = useCallback(
(sliderData) => {
birthYear.handleChange(sliderData.value)
},
[birthYear],
)

return (
<FieldGroup warning={value.warning}>
<FieldBlock label={props.label ?? 'Name and age'}>
<Layout.Row>
<Field.String {...value.text} />
<Field.Number {...value.number} />
<Field.String
path="/firstName"
label="First name"
width="medium"
minLength={2}
/>
<Field.String
path="/lastName"
label="Last name"
width="medium"
required
/>
<Layout.FlexItem width="large">
<Slider
min={1900}
max={new Date().getFullYear()}
step={1}
label="Birth year"
label_direction="vertical"
// @ts-ignore
value={birthYear.value}
on_change={handleBirthYearChange}
on_drag_start={birthYear.handleFocus}
on_drag_end={birthYear.handleBlur}
status={birthYear.error?.message}
tooltip
alwaysShowTooltip
/>
</Layout.FlexItem>
</Layout.Row>
</FieldGroup>
</FieldBlock>
)
}

const data = {
firstName: 'John',
birthYear: 2000,
}

return (
<DataContext.Provider
data={{
myComponent: {
warning: 'Show one warning',
text: { label: 'String field', value: 'Some value' },
number: { label: 'Number field', value: '123' },
},
}}
data={data}
onChange={(data) => console.log('onChange', data)}
>
<MyComponent path="/myComponent" />
<MyComposedField label="My custom label" />
</DataContext.Provider>
)
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,47 @@ breadcrumb:
---

import ListBasisAPIs from './create-component/ListBasisAPIs'
import { FirstExampleDemo } from './Examples'
import {
CreateBasicFieldComponent,
CreateComposedFieldComponent,
} from './Examples'

# Create your own component

Eufemia Forms contains of helper fields and tools so you can declaratively create interactive form components that flawlessly integrates between existing data and your custom form components.
Eufemia Forms contains helper fields and tools so you can declaratively create interactive form components that flawlessly integrates between existing data and your custom form components.

By using the building blocks for field components, you save development time, and at the same time ensure that local, custom components work similarly, and fit into the setup with [the standardized field components](/uilib/extensions/forms/base-fields/).

```jsx
import {
DataContext,
Field,
FieldBlock,
FieldGroup,
Iterate,
Value,
ValueBlock,
Visibility,
useDataValue,
} from '@dnb/eufemia/extensions/forms'
```

Here is a first example on how to compose your own component:
Here is an example of a custom component. Notice how the props received by your field component are passed through the `useDataValue` hook. This hook does not change the API of the props, so the props returned by the hook share the same typescript type with which it was called. However, it adds a few additional properties to simplify the standardization of field behavior. This in the form of the handler functions `handleFocus`, `handleChange` and `handleBlur`. Even if field components externally have these callback functions named with "on" (eg "onChange"), these will remain untouched, while the "handle" variants add handling that saves you a lot of extra work.

When you call these three functions from your own implementation of the user experience for the field component, as shown below, a lot will happen in the background. All available validation functions will be called at the right time, changes in value will be synchronized with any surrounding `DataContext`, co-operation between several fields that should display error messages collectively instead of individually, and not least it ensures that error messages are not displayed on unnecessary times such as while the user is making changes to the field.

<CreateBasicFieldComponent />

In the example above, you see how you can create your own user input functionality in a standardized context using `FieldBlock`. This allows you to display labels, error messages and other surrounding elements in a consistent manner with the ready-made fields found in Eufemia Forms.

Remember that everything that happens by using `useDataValue` and the rest of the available helper functionality, you can override the behaviour individually to make the component work exactly as you want.

If – for example; you need to carry out your own custom validation and cannot use the built-in validation with a JSON Schema or by sending in a derivative validator (as is done in the example above), you can write your own logic for it, and send the result in as props to `FieldBlock` in the form of `error`. All direct props override standard handling, so you have full control over your component.

If you need something that looks even more different than the usual fields, you can drop `FieldBlock` and display surrounding elements in other ways – but still get all the help of a data flow logic, such as `useDataValue` offers.

Here follows an example that retrieves data from a surrounding DataContext, and creates a composite field based on other components from Eufemia:

<FirstExampleDemo />
<CreateComposedFieldComponent />

## Components

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: 'FieldBlock'
description: '`FieldBlock` is the base component for receiving user input where the target data is of type `string`.'
description: '`FieldBlock` is a reusable wrapper for building [Field-components](/uilib/extensions/forms/create-component/Field). It shows surrounding elements through properties from `FieldProps` like `label` and `error`, and ensure that spacing between different fields work as required when put into surrounding components like `FlexContainer` or `Card`. It can also be used to group multiple inner FieldBlock component, composing error messages together as one component.'
componentType: 'basis-api'
hideInMenu: true
showTabs: true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
import { FieldBlock, TestElement } from '@dnb/eufemia/src/extensions/forms'
import {
FieldBlock,
Layout,
Field,
TestElement,
} from '@dnb/eufemia/src/extensions/forms'

export const Default = () => {
return (
Expand Down Expand Up @@ -123,3 +128,16 @@ export const WithDescriptionAndSecondary = () => {
</ComponentBox>
)
}

export const GroupMultipleFields = () => {
return (
<ComponentBox scope={{ FieldBlock, Layout, Field }}>
<FieldBlock label="Label text" info="For your information">
<Layout.Row>
<Field.String width="small" minLength={3} />
<Field.Number minimum={10} />
</Layout.Row>
</FieldBlock>
</ComponentBox>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,7 @@ import * as Examples from './Examples'
<em>(vertical only)</em>

<Examples.WithDescriptionAndSecondary />

### Group multiple fields

<Examples.GroupMultipleFields />
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Demos from 'Docs/uilib/extensions/forms/create-component/FieldBlock/demos

## Description

`FieldBlock` is the reusable wrapper for building [Field-components](/uilib/extensions/forms/create-component/Field). It shows surrounding elements through properties from `FieldProps` like `label` and `error`, and ensure that spacing between different fields work as required when put into surrounding components like `FlexContainer` or `Card`.
`FieldBlock` is a reusable wrapper for building [Field-components](/uilib/extensions/forms/create-component/Field). It shows surrounding elements through properties from `FieldProps` like `label` and `error`, and ensure that spacing between different fields work as required when put into surrounding components like `FlexContainer` or `Card`. It can also be used to group multiple inner FieldBlock component, composing error messages together as one component.

```jsx
import { FieldBlock } from '@dnb/eufemia/extensions/forms'
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Loading

0 comments on commit 56a1867

Please sign in to comment.