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

release of v10.58 #4333

Merged
merged 13 commits into from
Nov 29, 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 @@ -8,7 +8,7 @@ import {
} from '@dnb/eufemia/src/extensions/forms'
import { Flex } from '@dnb/eufemia/src'

export const TestdataSchema: JSONSchema = {
export const TestDataSchema: JSONSchema = {
type: 'object',
properties: {
requiredString: { type: 'string' },
Expand Down Expand Up @@ -37,7 +37,7 @@ export const TestdataSchema: JSONSchema = {
required: ['requiredString'],
}

export interface Testdata {
export type TestData = {
requiredString: string
string?: string
number?: number
Expand All @@ -53,7 +53,7 @@ export interface Testdata {
}>
}

export const testdata: Testdata = {
export const testData: TestData = {
requiredString: 'This is a text',
string: 'String value',
number: 123,
Expand Down Expand Up @@ -81,12 +81,12 @@ export const Default = () => {
scope={{
DataContext,
Value,
testdata,
TestdataSchema,
testData,
TestDataSchema,
}}
>
<DataContext.Provider
defaultData={testdata}
defaultData={testData}
onChange={(data) => console.log('onChange', data)}
onPathChange={(path, value) =>
console.log('onPathChange', path, value)
Expand Down Expand Up @@ -177,13 +177,13 @@ export const ValidationWithJsonSchema = () => {
scope={{
DataContext,
Value,
testdata,
TestdataSchema,
testData,
TestDataSchema,
}}
>
<DataContext.Provider
data={testdata}
schema={TestdataSchema}
data={testData}
schema={TestDataSchema}
onChange={(data) => console.log('onChange', data)}
onPathChange={(path, value) =>
console.log('onPathChange', path, value)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
import { Flex, P } from '@dnb/eufemia/src'
import { Form, Field } from '@dnb/eufemia/src/extensions/forms'
import {
Form,
Field,
Wizard,
Value,
} from '@dnb/eufemia/src/extensions/forms'

export const BasicUsage = () => {
return (
Expand All @@ -19,3 +24,30 @@ export const BasicUsage = () => {
</ComponentBox>
)
}

export const UsageInWizard = () => {
return (
<ComponentBox data-visual-test="forms-card-in-wizard">
<Form.Handler>
<Wizard.Container>
<Wizard.Step>
<Form.Card>
<Form.Section>
<Form.Section.ViewContainer
title="In a Wizard"
variant="basic"
>
<Value.String defaultValue="Something" />
</Form.Section.ViewContainer>
<Form.Section.EditContainer variant="basic">
<Field.String defaultValue="Something" />
</Form.Section.EditContainer>
</Form.Section>
</Form.Card>
</Wizard.Step>
</Wizard.Container>
<Form.SubmitButton text="Happy coding!" />
</Form.Handler>
</ComponentBox>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ import * as Examples from './Examples'
## Demos

<Examples.BasicUsage />

<VisibleWhenVisualTest>
<Examples.UsageInWizard />
</VisibleWhenVisualTest>
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,28 +7,126 @@ 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.

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

const existingData = { firstName: 'Nora' }

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

### TypeScript support

You can define the TypeScript type structure for your data like so:

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

type MyDataContext = {
firstName?: string
}

// Method #1 – without initial data
function MyForm() {
return (
<Form.Handler<MyDataContext>
onSubmit={(data) => {
// "firstName" is of type string
console.log(data.firstName)
}}
>
...
</Form.Handler>
)
}

// Method #2 – with data (initial values)
const existingData: MyDataContext = {
firstName: 'Nora',
}
function MyForm() {
return (
<Form.Handler
defaultData={existingData}
onSubmit={(data) => {
// "firstName" is of type string
console.log(data.firstName)
}}
>
...
</Form.Handler>
)
}

// Method #3 – type definition on the event parameter
const submitHandler = (data: MyDataContext) => {
// "firstName" is of type string
console.log(data.firstName)
}
function MyForm() {
return <Form.Handler onSubmit={submitHandler}>...</Form.Handler>
}

// Method #4 – type definition for the submit handler
import type { OnSubmit } from '@dnb/eufemia/extensions/forms'
const submitHandler: OnSubmit<MyDataContext> = (data) => {
// "firstName" is of type string
console.log(data.firstName)
}
function MyForm() {
return <Form.Handler onSubmit={submitHandler}>...</Form.Handler>
}
```

To disable types you can:

```tsx
<Form.Handler<any>>...</Form.Handler>
```

## Decoupling the form element

For more flexibility, you can decouple the form element from the form context by using the `decoupleForm` property. It is recommended to use the `Form.Element` to wrap your rest of your form:

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

render(
<Form.Handler
data={existingData}
onChange={...}
onSubmit={...}
>
Your Form
</Form.Handler>,
)
function MyApp() {
return (
<Form.Handler decoupleForm>
<AppRelatedThings>
<Form.Element>
<Form.MainHeading>Heading</Form.MainHeading>
<Form.Card>
<Field.Email />
</Form.Card>
<Form.SubmitButton />
</Form.Element>
</AppRelatedThings>
</Form.Handler>
)
}
```

The form data can be handled outside of the form. This is useful if you want to use the form data in other components:
## 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 MyForm() {
function MyComponent() {
const {
getValue,
update,
Expand All @@ -37,9 +135,18 @@ function MyForm() {
data,
filterData,
reduceToVisibleFields,
} = Form.useData('unique')
} = Form.useData()

return <Form.Handler id="unique">...</Form.Handler>
return <>...</>
}

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

Expand All @@ -51,53 +158,33 @@ 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).

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

### TypeScript support
### Using a form ID

You can define the TypeScript type structure for your data like so:
The form data can be handled outside of the form. This is useful if you want to use the form data in other components:

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

type MyDataSet = {
firstName?: string
}
const myFormId = 'unique-id' // or a function, object or React Context reference

const data: MyDataSet = {
firstName: 'Nora',
function MyComponent() {
const { data } = Form.useData(myFormId)

return <>...</>
}

// Method #1
function MyForm() {
function MyApp() {
return (
<Form.Handler
data={data}
onSubmit={(data) => {
console.log(data.firstName)
}}
/>
<>
<Form.Handler id={myFormId}>...</Form.Handler>
<MyComponent />
</>
)
}

// Method #2
const submitHandler = (data: MyDataSet) => {
console.log(data.firstName)
}
function MyForm() {
return <Form.Handler data={data} onSubmit={submitHandler} />
}

// Method #3
import type { OnSubmit } from '@dnb/eufemia/extensions/forms'
const submitHandler: OnSubmit<MyDataSet> = (data) => {
console.log(data.firstName)
}
function MyForm() {
return <Form.Handler data={data} onSubmit={submitHandler} />
}
```

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

## Async `onChange` and `onSubmit` event handlers

**NB:** When using an async `onChange` event handler, the `data` parameter will only include validated data. This lets you utilize the `data` parameter directly in your request, without having to further process or transform it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ In all async operations, you can simply return an error object to display it in
```tsx
import { Form } from '@dnb/eufemia/extensions/forms'

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

// Async function
const onSubmit = async (data) => {
try {
Expand All @@ -12,7 +14,7 @@ const onSubmit = async (data) => {
})
const data = await response.json()

Form.setData('unique-id', data) // Whatever you want to do with the data
Form.setData(myFormId, data) // Whatever you want to do with the data
} catch (error) {
return error // Will display the error message in the form
}
Expand All @@ -32,7 +34,7 @@ const onSubmit = async (data) => {

function Component() {
return (
<Form.Handler id="unique-id" onSubmit={onSubmit}>
<Form.Handler id={myFormId} onSubmit={onSubmit}>
...
</Form.Handler>
)
Expand Down
Loading
Loading