Skip to content

Commit

Permalink
feat(Forms): add support for using a function instance as a reference…
Browse files Browse the repository at this point in the history
… instead of a string based id
  • Loading branch information
tujoworker committed Nov 23, 2024
1 parent 69dc60a commit 0d766e7
Show file tree
Hide file tree
Showing 21 changed files with 387 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const { getValue, data, filterData, reduceToVisibleFields } =
- `filterData` will filter the data based on your own logic.
- `reduceToVisibleFields` will reduce the given data set to only contain the visible fields (mounted fields).

You link them together via the `id` (string) property.
You link them together via the `id` (string, function, object or React Context as the reference) property.

TypeScript support:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function Component() {
}
```

You link them together via the `id` (string) property.
You link them together via the `id` (string, function, object or React Context as the reference) property.

Related helpers:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ render(

## Usage

You can use the `Form.useData` hook with or without an `id` (string) property, which is optional and can be used to link the data to a specific [Form.Handler](/uilib/extensions/forms/Form/Handler/) component.
You can use the `Form.useData` hook with or without an `id` (string, function, object or React Context as the reference) property, which is optional and can be used to link the data to a specific [Form.Handler](/uilib/extensions/forms/Form/Handler/) component.

### Without an `id` property

Expand All @@ -66,7 +66,7 @@ function Component() {

### With an `id` property

While in this example, "Component" is outside the `Form.Handler` context, but linked together via the `id` (string) property:
While in this example, "Component" is outside the `Form.Handler` context, but linked together via the `id` (string, function, object or React Context as the reference) property:

```jsx
import { Form } from '@dnb/eufemia/extensions/forms'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ You can check out examples in the demo section.

## Usage of the `Form.useSnapshot` hook

You can use the `Form.useSnapshot` hook with or without an `id` (string) property, which is optional and can be used to link the data to a specific [Form.Handler](/uilib/extensions/forms/Form/Handler/) component.
You can use the `Form.useSnapshot` hook with or without an `id` (string, function, object or React Context as the reference) property, which is optional and can be used to link the data to a specific [Form.Handler](/uilib/extensions/forms/Form/Handler/) component.

### Without an `id` property

Expand All @@ -85,7 +85,7 @@ function Component() {

### With an `id` property

While in this example, "Component" is outside the `Form.Handler` context, but linked together via the `id` (string) property:
While in this example, "Component" is outside the `Form.Handler` context, but linked together via the `id` (string, function, object or React Context as the reference) property:

```jsx
import { Form } from '@dnb/eufemia/extensions/forms'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function Component() {
}
```

Or by linking the hook together with the form by using the `id` (string) property:
Or by linking the hook together with the form by using the `id` (string, function, object or React Context as the reference) property:

```jsx
import { Form } from '@dnb/eufemia/extensions/forms'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ const MyForm = () => {
}
```

When using the `useStep` hook outside of the `Wizard.Container` context, you need to provide an unique `id` (string):
When using the `useStep` hook outside of the `Wizard.Container` context, you need to provide an unique `id` (string, function, object or React Context as the reference):

```tsx
import { Form, Wizard } from '@dnb/eufemia/extensions/forms'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ function MyForm() {

## Without a router

You connect the hook with the `Wizard.Container` component via an unique `id` (string). The `id` will be used in the URL query string: `url?unique-id-step=1`.
You connect the hook with the `Wizard.Container` component via an unique `id` (string, function, object or React Context as the reference). The `id` will be used in the URL query string: `url?unique-id-step=1`.

```jsx
import { Form, Wizard } from '@dnb/eufemia/extensions/forms'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function MyForm() {
}
```

You can also connect the hook with the `Wizard.Container` via an `id` (string). This lets you render the hook outside of the context:
You can also connect the hook with the `Wizard.Container` via an `id` (string, function, object or React Context as the reference). This lets you render the hook outside of the context:

```jsx
import { Form } from '@dnb/eufemia/extensions/forms'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,11 @@ Here is an example of how to use these methods:
```jsx
import { Form } from '@dnb/eufemia/extensions/forms'

const myFormId = 'unique-id' // or a function, object or React Context reference

function MyForm() {
return (
<Form.Handler id="unique-id">
<Form.Handler id={myFormId}>
<MyComponent />
</Form.Handler>
)
Expand All @@ -145,15 +147,15 @@ function MyComponent() {
data,
filterData,
reduceToVisibleFields,
} = Form.useData() // optionally provide an id (unique-id)
} = Form.useData() // optionally provide an id or reference
}

// You can also use the setData:
Form.setData('unique-id', { companyName: 'DNB' })
Form.setData(myFormId, { companyName: 'DNB' })

// ... and the getData – method when ever you need to:
const { getValue, data, filterData, reduceToVisibleFields } =
Form.getData('unique-id')
Form.getData(myFormId)
```

- `getValue` will return the value of the given path.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import {
Path,
EventStateObject,
EventReturnWithStateObject,
Identifier,
FieldProps,
ValueProps,
OnChange,
OnSubmitParams,
} from '../types'
import { Props as ProviderProps } from './Provider'
import { SnapshotName } from '../Form/Snapshot'
import { SharedStateId } from '../../../shared/helpers/useSharedState'

export type MountState = {
isPreMounted?: boolean
Expand Down Expand Up @@ -85,7 +85,7 @@ export type FieldConnections = {
}

export interface ContextState {
id?: Identifier
id?: SharedStateId
hasContext: boolean
/** The dataset for the form / form wizard */
data: any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ import { debounce } from '../../../../shared/helpers'
import FieldPropsProvider from '../../Field/Provider'
import useUpdateEffect from '../../../../shared/helpers/useUpdateEffect'
import { isAsync } from '../../../../shared/helpers/isAsync'
import { useSharedState } from '../../../../shared/helpers/useSharedState'
import {
SharedStateId,
createReferenceKey,
useSharedState,
} from '../../../../shared/helpers/useSharedState'
import SharedContext, { ContextProps } from '../../../../shared/Context'
import useTranslation from '../../hooks/useTranslation'
import DataContext, {
Expand Down Expand Up @@ -74,7 +78,7 @@ export interface Props<Data extends JsonObject>
/**
* Unique ID to communicate with the hook Form.useData
*/
id?: string
id?: SharedStateId
/**
* Unique ID to connect with a GlobalStatus
*/
Expand Down Expand Up @@ -618,10 +622,10 @@ export default function Provider<Data extends JsonObject>(
// - Shared state
const sharedData = useSharedState<Data>(id)
const sharedAttachments = useSharedState<SharedAttachments<Data>>(
id + '-attachments'
createReferenceKey(id, 'attachments')
)
const sharedDataContext = useSharedState<ContextState>(
id + '-data-context'
createReferenceKey(id, 'data-context')
)

const setSharedData = sharedData.set
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const ProviderProperties: PropertiesTableProps = {
},
id: {
doc: 'Unique id for connecting Form.Handler and helper tools such as Form.useData.',
type: 'string',
type: ['string', 'Function', 'Object', 'React.Context'],
status: 'optional',
},
schema: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import React from 'react'
import { fireEvent, render } from '@testing-library/react'
import { Form, DataContext, Field } from '../../..'
Expand Down Expand Up @@ -151,4 +152,17 @@ describe('Form.Element', () => {
expect(attributes).toEqual(['class', 'aria-label'])
expect(formElement.getAttribute('aria-label')).toBe('Aria Label')
})

it('should ensure that only a string can be set as the id', () => {
const myId = () => null
render(
// @ts-expect-error
<Form.Element id={myId}>
<Form.SubmitButton>Submit</Form.SubmitButton>
</Form.Element>
)

const formElement = document.querySelector('form')
expect(formElement).not.toHaveAttribute('id')
})
})
Loading

0 comments on commit 0d766e7

Please sign in to comment.