Skip to content

Commit

Permalink
feat(useFieldProps): rename useDataValue to useFieldProps (#3381)
Browse files Browse the repository at this point in the history
For a more semantic name I propose to rename this hook. It is made with
backwards compatibility.
  • Loading branch information
tujoworker authored Mar 15, 2024
1 parent a5ef0bd commit 123a543
Show file tree
Hide file tree
Showing 28 changed files with 134 additions and 128 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import {
Field,
Value,
FieldBlock,
useDataValue,
useFieldProps,
DataContext,
} from '@dnb/eufemia/src/extensions/forms'

export const CreateBasicFieldComponent = () => {
return (
<ComponentBox
scope={{
useDataValue,
useFieldProps,
}}
hideCode
>
Expand All @@ -42,7 +42,7 @@ export const CreateBasicFieldComponent = () => {
handleChange,
handleFocus,
handleBlur,
} = useDataValue(preparedProps)
} = useFieldProps(preparedProps)

return (
<FieldBlock
Expand Down Expand Up @@ -138,13 +138,13 @@ export const CreateComposedFieldComponent = () => {
<ComponentBox
scope={{
DataContext,
useDataValue,
useFieldProps,
}}
hideCode
>
{() => {
const MyComposedField = (props) => {
const birthYear = useDataValue({
const birthYear = useFieldProps({
path: '/birthYear',
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,39 +24,39 @@ By using the building blocks for field components, you save development time, an
import {
FieldBlock,
ValueBlock,
useDataValue,
useFieldProps,
} from '@dnb/eufemia/extensions/forms'
```

<ListBasisAPIs />

## FieldBlock and useDataValue
## FieldBlock and useFieldProps

The `FieldBlock` component and the `useDataValue` hook are the basis for all field components in Eufemia Forms.
The `FieldBlock` component and the `useFieldProps` hook are the basis for all field components in Eufemia Forms.

`FieldBlock` provides a standardized way to display labels, error messages and other surrounding elements in a consistent manner.

While `useDataValue` provides a standardized way to handle data flow, validation and error messages in a consistent manner.
While `useFieldProps` provides a standardized way to handle data flow, validation and error messages in a consistent manner.

<CreateBasicFieldComponent />

### More details

This example shows a custom component. The `useDataValue` hook receives the props and adds extra properties to standardize field behavior. These includes `handleFocus`, `handleChange`, and `handleBlur` functions. Even if the field components has external callbacks like "onChange", these won't be altered. The "handle" variants simplifies your code.
This example shows a custom component. The `useFieldProps` hook receives the props and adds extra properties to standardize field behavior. These includes `handleFocus`, `handleChange`, and `handleBlur` functions. Even if the field components has external callbacks like "onChange", these won't be altered. The "handle" variants simplifies your code.

### The example explained

Using these two form helpers in your field component triggers several automatic processes. These include timely validation checks, syncing value changes with the [DataContext](/uilib/extensions/forms/extended-features/DataContext/), coordinating error messages across multiple fields, and preventing premature error displays while the user is editing the field.

Keep in mind, you can customize the behavior of [useDataValue](/uilib/extensions/forms/create-component/useDataValue) and other helper functions to make the component work exactly as you want.
Keep in mind, you can customize the behavior of [useFieldProps](/uilib/extensions/forms/create-component/useFieldProps) and other helper functions to make the component work exactly as you want.

### Your own validation

If you need custom validation that can't use the built-in JSON Schema or a derivative validator (like in the example above), you can create your own logic. Then, pass the result as an `error` prop to `FieldBlock`. All direct props override standard handling, giving you full control over your component.

### Customized even further

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.
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 `useFieldProps` offers.

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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: 'useDataValue'
description: 'The `useDataValue` hook standardize handling of the value flow for a single consumer component representing one data point.'
title: 'useFieldProps'
description: 'The `useFieldProps` hook standardize handling of the value flow for a single consumer component representing one data point.'
componentType: 'basis-api'
hideInMenu: true
showTabs: true
Expand All @@ -14,12 +14,12 @@ breadcrumb:
href: /uilib/extensions/forms/
- text: Create your component
href: /uilib/extensions/forms/create-component/
- text: useDataValue
href: /uilib/extensions/forms/create-component/useDataValue/
- text: useFieldProps
href: /uilib/extensions/forms/create-component/useFieldProps/
---

import Info from 'Docs/uilib/extensions/forms/create-component/useDataValue/info'
import Demos from 'Docs/uilib/extensions/forms/create-component/useDataValue/demos'
import Info from 'Docs/uilib/extensions/forms/create-component/useFieldProps/info'
import Demos from 'Docs/uilib/extensions/forms/create-component/useFieldProps/demos'

<Info />
<Demos />
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import {
FieldBlock,
Form,
JSONSchema,
useDataValue,
useFieldProps,
} from '@dnb/eufemia/src/extensions/forms'
import { Flex, Slider } from '@dnb/eufemia/src'

export const CustomComponentExample = () => {
return (
<ComponentBox scope={{ useDataValue, FieldBlock }}>
<ComponentBox scope={{ useFieldProps, FieldBlock }}>
{() => {
const MySliderComponent = (props) => {
const fromInput = React.useCallback(
Expand Down Expand Up @@ -59,7 +59,7 @@ export const CustomComponentExample = () => {
handleChange,
handleFocus,
handleBlur,
} = useDataValue(preparedProps)
} = useFieldProps(preparedProps)

const steps = { minimum, maximum, step }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ draft: true

# Description

The `useDataValue` hook standardize handling of the value flow for a single consumer component representing one data point. It holds error state, hides it while the field is in focus, connects to surrounding `DataContext` (if present) and other things that all field or value components needs to do. By implementing custom field or value components and passing the received props through `useDataValue`, all these features work the same way as other field or value components, and you only need to implement the specific unique features of that component.
The `useFieldProps` hook standardize handling of the value flow for a single consumer component representing one data point. It holds error state, hides it while the field is in focus, connects to surrounding `DataContext` (if present) and other things that all field or value components needs to do. By implementing custom field or value components and passing the received props through `useFieldProps`, all these features work the same way as other field or value components, and you only need to implement the specific unique features of that component.

## How to use

```tsx
import { useDataValue } from '@dnb/eufemia/extensions/forms'
const { value } = useDataValue(componentProps)
import { useFieldProps } from '@dnb/eufemia/extensions/forms'
const { value } = useFieldProps(componentProps)
```

Advanced usage:

```tsx
import {
useDataValue,
useFieldProps,
useErrorMessage,
} from '@dnb/eufemia/extensions/forms'

Expand Down Expand Up @@ -50,7 +50,7 @@ const {

// Component Properties
...rest
} = useDataValue(preparedProps)
} = useFieldProps(preparedProps)

render(
<Input
Expand Down Expand Up @@ -117,7 +117,7 @@ const validateRequired = (
: undefined
}

const { error, hasError } = useDataValue({
const { error, hasError } = useFieldProps({
value: undefined,
required: true,
validateInitially: true,
Expand Down Expand Up @@ -152,10 +152,10 @@ render(
)
```

But when you handle errors via `useDataValue`, you may rather provide an object with messages, which will be used to display the error:
But when you handle errors via `useFieldProps`, you may rather provide an object with messages, which will be used to display the error:

```ts
const { error, hasError } = useDataValue({
const { error, hasError } = useFieldProps({
required: true,
errorMessages: {
required: 'Show this when "required" fails!',
Expand All @@ -167,7 +167,7 @@ const { error, hasError } = useDataValue({
In order to invoke an error without a change and blur event, you can use `validateInitially`:

```ts
const { error, hasError } = useDataValue({
const { error, hasError } = useFieldProps({
value: undefined,
required: true,
validateInitially: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,4 @@ If you want the state of the value to live inside the input component, do not se

### Creating custom field components

The `useDataValue` hook that is used in all existing field components is exported to make it possible [to create custom field components](/uilib/extensions/forms/create-component/) that have the same properties and follow the same flow as the standard components, without the need to recreate all the basic state handling features.
The `useFieldProps` hook that is used in all existing field components is exported to make it possible [to create custom field components](/uilib/extensions/forms/create-component/) that have the same properties and follow the same flow as the standard components, without the need to recreate all the basic state handling features.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Checkbox, ToggleButton } from '../../../../components'
import classnames from 'classnames'
import OptionField from '../Option'
import FieldBlock from '../../FieldBlock'
import { useDataValue } from '../../hooks'
import { useFieldProps } from '../../hooks'
import { FieldProps } from '../../types'
import { pickSpacingProps } from '../../../../components/flex/utils'
import ToggleButtonGroupContext from '../../../../components/toggle-button/ToggleButtonGroupContext'
Expand Down Expand Up @@ -38,7 +38,7 @@ function ArraySelection(props: Props) {
emptyValue,
handleChange,
children,
} = useDataValue(props)
} = useFieldProps(props)

const fieldBlockProps = {
forId: id,
Expand Down
4 changes: 2 additions & 2 deletions packages/dnb-eufemia/src/extensions/forms/Field/Date/Date.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useCallback, useContext, useMemo } from 'react'
import { DatePicker, HelpButton } from '../../../../components'
import { useDataValue } from '../../hooks'
import { useFieldProps } from '../../hooks'
import {
FieldProps,
FieldHelpProps,
Expand Down Expand Up @@ -74,7 +74,7 @@ function DateComponent(props: Props) {
handleFocus,
handleBlur,
handleChange,
} = useDataValue(preparedProps)
} = useFieldProps(preparedProps)

return (
<FieldBlock
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useCallback, useContext } from 'react'
import SharedContext from '../../../../shared/Context'
import { FieldHelpProps, FieldProps } from '../../types'
import { pickSpacingProps } from '../../../../components/flex/utils'
import { useDataValue } from '../../hooks'
import { useFieldProps } from '../../hooks'
import classnames from 'classnames'
import FieldBlock from '../../FieldBlock'
import { MultiInputMask } from '../../../../components/input-masked'
Expand Down Expand Up @@ -55,7 +55,7 @@ function Expiry(props: ExpiryProps) {
handleFocus,
handleBlur,
handleChange,
} = useDataValue(preparedProps)
} = useFieldProps(preparedProps)

const expiry: ExpiryValue = {
month: ensureValidMonth(value?.substring(0, 2)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import SharedContext from '../../../../shared/Context'
import FieldBlockContext from '../../FieldBlock/FieldBlockContext'
import classnames from 'classnames'
import FieldBlock from '../../FieldBlock'
import { useDataValue } from '../../hooks'
import { useFieldProps } from '../../hooks'
import {
FieldProps,
FieldHelpProps,
Expand Down Expand Up @@ -204,7 +204,7 @@ function NumberComponent(props: Props) {
handleFocus,
handleBlur,
handleChange,
} = useDataValue(preparedProps)
} = useFieldProps(preparedProps)

const onKeyDownHandler = useCallback(
({ key, event }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import classnames from 'classnames'
import countries, { CountryType } from '../../constants/countries'
import StringField, { Props as StringFieldProps } from '../String'
import FieldBlock from '../../FieldBlock'
import { useDataValue } from '../../hooks'
import { useFieldProps } from '../../hooks'
import {
FieldHelpProps,
FieldProps,
Expand Down Expand Up @@ -48,7 +48,7 @@ export type Props = FieldHelpProps &
noAnimation?: boolean
}

// Important for the default value to be defined here, and not after the useDataValue call, to avoid the UI jumping
// Important for the default value to be defined here, and not after the useFieldProps call, to avoid the UI jumping
// back to +47 once the user empty the field so handleChange send out undefined.
const defaultCountryCode = '+47'
const defaultPlaceholder = '00 00 00 00'
Expand Down Expand Up @@ -156,7 +156,7 @@ function PhoneNumber(props: Props) {
filterCountries = ccFilter !== 'Prioritized'
? makeCountryFilterSet(ccFilter)
: undefined,
} = useDataValue(preparedProps)
} = useFieldProps(preparedProps)

function fromExternal(external: string) {
const [, phoneNumber] = splitValue(external)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import countries, {
prioritizedCountries,
CountryType,
} from '../../constants/countries'
import { useDataValue } from '../../hooks'
import { useFieldProps } from '../../hooks'
import { FieldHelpProps, FieldProps } from '../../types'
import FieldBlock from '../../FieldBlock'
import useErrorMessage from '../../hooks/useErrorMessage'
Expand Down Expand Up @@ -79,7 +79,7 @@ function SelectCountry(props: Props) {
filterCountries = ccFilter !== 'Prioritized'
? makeCountryFilterSet(ccFilter)
: undefined,
} = useDataValue(preparedProps)
} = useFieldProps(preparedProps)

const dataRef = React.useRef(null)
const langRef = React.useRef(lang)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
import classnames from 'classnames'
import { makeUniqueId } from '../../../../shared/component-helper'
import OptionField from '../Option'
import { useDataValue } from '../../hooks'
import { useFieldProps } from '../../hooks'
import { FormError, FieldProps, FieldHelpProps } from '../../types'
import { pickSpacingProps } from '../../../../components/flex/utils'
import type { FormStatusText } from '../../../../components/FormStatus'
Expand Down Expand Up @@ -53,7 +53,7 @@ function Selection(props: Props) {
setHasFocus,
handleChange,
children,
} = useDataValue(props)
} = useFieldProps(props)

const handleDropdownChange = useCallback(
({ data: { selectedKey } }) => {
Expand All @@ -74,7 +74,7 @@ function Selection(props: Props) {
)

// Specific handleShow and handleHide because Dropdown preserve the initially received callbacks, so changes
// due to `useCallback` usage will have no effect, leading to useDataValues handleFocus and handleBlur sending out old
// due to `useCallback` usage will have no effect, leading to useFieldPropss handleFocus and handleBlur sending out old
// copies of value as arguments.
const handleShow = useCallback(
({ data }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { TextareaProps } from '../../../../components/Textarea'
import SharedContext from '../../../../shared/Context'
import FieldBlockContext from '../../FieldBlock/FieldBlockContext'
import FieldBlock from '../../FieldBlock'
import { useDataValue } from '../../hooks'
import { useFieldProps } from '../../hooks'
import { pickSpacingProps } from '../../../../components/flex/utils'
import { toCapitalized } from '../../../../shared/component-helper'
import type { TextCounterProps } from '../../../../fragments/TextCounter'
Expand Down Expand Up @@ -196,7 +196,7 @@ function StringComponent(props: Props) {
handleFocus,
handleBlur,
handleChange,
} = useDataValue(preparedProps)
} = useFieldProps(preparedProps)

const transformInstantly = useCallback(
(value: string) => (props.capitalize ? toCapitalized(value) : value),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1032,7 +1032,7 @@ describe('Field.String', () => {
})
})

describe('useDataValue and FieldBlock', () => {
describe('useFieldProps and FieldBlock', () => {
const inputError = 'StatusMessage error'
const inputWarning = 'StatusMessage warning'
const inputInfo = 'StatusMessage info'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Checkbox, ToggleButton } from '../../../../components'
import classnames from 'classnames'
import ButtonRow from '../../Form/ButtonRow'
import FieldBlock from '../../FieldBlock'
import { useDataValue } from '../../hooks'
import { useFieldProps } from '../../hooks'
import { FieldProps } from '../../types'
import { pickSpacingProps } from '../../../../components/flex/utils'
import SharedContext from '../../../../shared/Context'
Expand Down Expand Up @@ -44,7 +44,7 @@ function Toggle(props: Props) {
hasError,
ariaAttributes,
handleChange,
} = useDataValue(preparedProps)
} = useFieldProps(preparedProps)

const handleCheckboxChange = useCallback(
({ checked }) => {
Expand Down
Loading

0 comments on commit 123a543

Please sign in to comment.