Skip to content

Commit

Permalink
feat(Forms): introduce decoupleForm prop to Form.Handler (#4332)
Browse files Browse the repository at this point in the history
  • Loading branch information
tujoworker authored Nov 25, 2024
1 parent 76bddf0 commit 0b02b6e
Show file tree
Hide file tree
Showing 12 changed files with 451 additions and 279 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: 'Handler'
description: '`Form.Handler` provides both the DataContext.Provider and a HTML form element.'
description: 'The `Form.Handler` is the root component of your form. It provides a HTML form element and handles the form data.'
showTabs: true
tabs:
- title: Info
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,57 @@ import AsyncChangeExample from './parts/async-change-example.mdx'

## Description

The `Form.Handler` component provides a HTML form element.
The `Form.Handler` is the root component of your form. It provides a HTML form element and handles the form data.

```jsx
import { Form } from '@dnb/eufemia/extensions/forms'

render(
<Form.Handler
data={existingData}
onChange={...}
onSubmit={...}
>
Your Form
</Form.Handler>,
)
const existingData = { firstName: 'Nora' }

function MyForm() {
return (
<Form.Handler
defaultData={existingData}
onSubmit={...}
>
Your Form
</Form.Handler>
)
}
```

## Data handling
## Decoupling the form element

The form data can be handled outside of the form. This is useful if you want to use the form data in other components:
For more flexibility, you can decouple the form element from the form context by using the `decoupleFormElement` property. It is recommended to use the `Form.Element` to wrap your rest of your form:

```jsx
import { Form } from '@dnb/eufemia/extensions/forms'

const myFormId = 'unique-id' // or a function, object or React Context reference
function MyApp() {
return (
<Form.Handler decoupleFormElement>
<AppRelatedThings>
<Form.Element>
<Form.MainHeading>Heading</Form.MainHeading>
<Form.Card>
<Field.Email />
</Form.Card>
<Form.SubmitButton />
</Form.Element>
</AppRelatedThings>
</Form.Handler>
)
}
```

function MyForm() {
## Data handling

You can access, mutate and filter data inside of the form context by using the `Form.useData` hook:

```jsx
import { Form } from '@dnb/eufemia/extensions/forms'

function MyComponent() {
const {
getValue,
update,
Expand All @@ -41,9 +66,18 @@ function MyForm() {
data,
filterData,
reduceToVisibleFields,
} = Form.useData(myFormId)
} = Form.useData()

return <>...</>
}

return <Form.Handler id={myFormId}>...</Form.Handler>
function MyApp() {
return (
<>
<Form.Handler>...</Form.Handler>
<MyComponent />
</>
)
}
```

Expand All @@ -55,6 +89,31 @@ function MyForm() {
- `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).

### Using a form ID

The form data can be handled outside of the form. This is useful if you want to use the form data in other components:

```jsx
import { Form } from '@dnb/eufemia/extensions/forms'

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

function MyComponent() {
const { data } = Form.useData(myFormId)

return <>...</>
}

function MyApp() {
return (
<>
<Form.Handler id={myFormId}>...</Form.Handler>
<MyComponent />
</>
)
}
```

More examples can be found in the [useData](/uilib/extensions/forms/Form/useData/) hook docs.

### TypeScript support
Expand All @@ -76,7 +135,7 @@ const data: MyDataSet = {
function MyForm() {
return (
<Form.Handler
data={data}
defaultData={data}
onSubmit={(data) => {
console.log(data.firstName)
}}
Expand All @@ -89,7 +148,7 @@ const submitHandler = (data: MyDataSet) => {
console.log(data.firstName)
}
function MyForm() {
return <Form.Handler data={data} onSubmit={submitHandler} />
return <Form.Handler defaultData={data} onSubmit={submitHandler} />
}

// Method #3
Expand All @@ -98,7 +157,7 @@ const submitHandler: OnSubmit<MyDataSet> = (data) => {
console.log(data.firstName)
}
function MyForm() {
return <Form.Handler data={data} onSubmit={submitHandler} />
return <Form.Handler defaultData={data} onSubmit={submitHandler} />
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,30 @@ showTabs: true

`Form` provides the main forms-helpers including data provider and event handling.

```jsx
```tsx
import { Form, Field } from '@dnb/eufemia/extensions/forms'
render(
<Form.Handler data={existingData} onSubmit={submitHandler}>
<Field.Email path="/email" />
<Form.ButtonRow>
<Form.SubmitButton />
</Form.ButtonRow>
</Form.Handler>,
)

const existingData = {
email: '[email protected]',
}

function MyForm() {
return (
<Form.Handler
defaultData={existingData}
onSubmit={async (data) => {
await makeRequest(data)
}}
>
<Form.MainHeading>Heading</Form.MainHeading>
<Form.Card>
<Field.Email path="/email" />
</Form.Card>

<Form.ButtonRow>
<Form.SubmitButton />
</Form.ButtonRow>
</Form.Handler>
)
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const submitHandler = async (data) => {

function Component() {
return (
<Form.Handler data={existingData} onSubmit={submitHandler}>
<Form.Handler defaultData={existingData} onSubmit={submitHandler}>
<Field.Email path="/email" />
<Value.Date path="/date" />
<Form.SubmitButton />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,16 +231,16 @@ You may check out an [interactive example](/uilib/extensions/forms/Form/useData/
For filtering data during form submit (`onSubmit`), you can use the `filterData` method given as a parameter to the `onSubmit` event callback:

```tsx
const onSubmit = (data, { filterData }) => {
// Same method as in the previous example
const filteredDataA = filterData(filterDataPaths)
const filteredDataB = filterData(filterDataHandler)
console.log(filteredDataA)
console.log(filteredDataB)
}

render(
<Form.Handler onSubmit={onSubmit}>
<Form.Handler
onSubmit={(data, { filterData }) => {
// Same method as in the previous example
const filteredDataA = filterData(filterDataPaths)
const filteredDataB = filterData(filterDataHandler)
console.log(filteredDataA)
console.log(filteredDataB)
}}
>
<Field.String path="/foo" />
</Form.Handler>,
)
Expand Down Expand Up @@ -428,7 +428,7 @@ Eufemia Forms will easily link up with the [GlobalStatus](/uilib/components/glob
```tsx
<GlobalStatus />

<Form.Handler >
<Form.Handler>
My Form
</Form.Handler>
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,10 @@ export interface ContextState {
disabled?: boolean
required?: boolean
submitState: Partial<EventStateObject>
isInsideFormElement?: boolean
prerenderFieldProps?: boolean
decoupleFormElement?: boolean
hasElementRef?: React.MutableRefObject<boolean>
restHandlerProps?: Record<string, unknown>
props: ProviderProps<unknown>
}

Expand Down Expand Up @@ -211,7 +213,6 @@ export const defaultContextState: ContextState = {
hasFieldError: () => false,
ajvInstance: makeAjvInstance(),
contextErrorMessages: undefined,
isInsideFormElement: false,
props: null,
}

Expand Down
Loading

0 comments on commit 0b02b6e

Please sign in to comment.