Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Forms): add support for using a function references instead of a string based id #4331

Merged
merged 1 commit into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading