diff --git a/packages/dnb-design-system-portal/src/docs/uilib/about-the-lib/releases/eufemia/v11-info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/about-the-lib/releases/eufemia/v11-info.mdx
index 41521c0cbf1..6047990ef8d 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/about-the-lib/releases/eufemia/v11-info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/about-the-lib/releases/eufemia/v11-info.mdx
@@ -132,13 +132,11 @@ The `InputPassword` component has been moved to `Field.Password`, and is now a p
- replace `withValue` with `hasValue`.
-## Form.useError
+## Eufemia Forms
- replace `useError` with `useValidation`.
-
-## Form.Iterate
-
-- Rename label variable `{itemNr}` to `{itemNo}`.
+- replace Form.Iterate label variable `{itemNr}` with `{itemNo}`.
+- replace `Form.FieldProps` with `Field.Provider`.
## NumberFormat
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/FieldProps.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/FieldProps.mdx
deleted file mode 100644
index 6513f4c641f..00000000000
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/FieldProps.mdx
+++ /dev/null
@@ -1,25 +0,0 @@
----
-title: 'FieldProps'
-description: '`Form.FieldProps` is a provider for forwarding fields properties, such as `required` or `disabled` to all nested field components.'
-showTabs: true
-tabs:
- - title: Info
- key: '/info'
- - title: Demos
- key: '/demos'
- - title: Properties
- key: '/properties'
-breadcrumb:
- - text: Forms
- href: /uilib/extensions/forms/
- - text: Form
- href: /uilib/extensions/forms/Form/
- - text: FieldProps
- href: /uilib/extensions/forms/Form/FieldProps
----
-
-import Info from 'Docs/uilib/extensions/forms/Form/FieldProps/info'
-import Demos from 'Docs/uilib/extensions/forms/Form/FieldProps/demos'
-
-
-
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/FieldProps/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/FieldProps/properties.mdx
deleted file mode 100644
index df1e7c05ed1..00000000000
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/FieldProps/properties.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-showTabs: true
----
-
-import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable'
-import { FieldPropsProperties } from '@dnb/eufemia/src/extensions/forms/Form/FieldProps/FieldPropsDocs'
-
-## Properties
-
-
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/changelog.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/changelog.mdx
index efbdd57d007..4e60560b772 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/changelog.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/changelog.mdx
@@ -80,7 +80,7 @@ Change log for the Eufemia Forms extension.
## v10.30
-- Added [Form.FieldProps](/uilib/extensions/forms/Form/FieldProps/) component to forward field properties, such as `required` or `disabled` to all nested field components.
+- Added `Form.FieldProps` (which got renamed to [Field.Provider](/uilib/extensions/forms/feature-fields/Provider/)) component to forward field properties, such as `required` or `disabled` to all nested field components.
- Added `locale` and `translations` to [Form.Handler](/uilib/extensions/forms/Form/Handler/) component to support custom translations.
- Added `disabled` and `required` to [Form.Handler](/uilib/extensions/forms/Form/Handler/) component and pass these props to the children fields.
- Added `fieldPropsWhenHidden` to [Form.Visibility](/uilib/extensions/forms/Form/Visibility/) component to pass props to the children when visibility is hidden.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Provider.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Provider.mdx
new file mode 100644
index 00000000000..4e729b7c780
--- /dev/null
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Provider.mdx
@@ -0,0 +1,26 @@
+---
+title: 'Field.Provider'
+description: '`Field.Provider` is a provider for forwarding fields properties, such as `required` or `disabled` to all nested field components.'
+hideInMenu: true
+showTabs: true
+tabs:
+ - title: Info
+ key: '/info'
+ - title: Demos
+ key: '/demos'
+ - title: Properties
+ key: '/properties'
+breadcrumb:
+ - text: Forms
+ href: /uilib/extensions/forms/
+ - text: Feature fields
+ href: /uilib/extensions/forms/feature-fields/
+ - text: Field.Provider
+ href: /uilib/extensions/forms/feature-fields/Provider
+---
+
+import Info from 'Docs/uilib/extensions/forms/feature-fields/Provider/info'
+import Demos from 'Docs/uilib/extensions/forms/feature-fields/Provider/demos'
+
+
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/FieldProps/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Provider/Examples.tsx
similarity index 87%
rename from packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/FieldProps/Examples.tsx
rename to packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Provider/Examples.tsx
index 52902b8713a..b67a2603886 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/FieldProps/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Provider/Examples.tsx
@@ -9,10 +9,10 @@ export const Required = () => {
-
+
-
+
@@ -30,14 +30,14 @@ export const Disabled = () => {
-
+
-
+
@@ -51,14 +51,14 @@ export const Inverted = () => {
-
+
-
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/FieldProps/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Provider/demos.mdx
similarity index 100%
rename from packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/FieldProps/demos.mdx
rename to packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Provider/demos.mdx
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/FieldProps/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Provider/info.mdx
similarity index 50%
rename from packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/FieldProps/info.mdx
rename to packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Provider/info.mdx
index 97ca06be7c5..9ea686db66c 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/FieldProps/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Provider/info.mdx
@@ -4,17 +4,17 @@ showTabs: true
## Description
-`Form.FieldProps` is a provider for forwarding fields properties, such as `required` or `disabled` to all nested field components.
+`Field.Provider` is a provider for forwarding fields properties, such as `required` or `disabled` to all nested field components.
## Usage
```tsx
import { Form, Field } from '@dnb/eufemia/extensions/forms'
render(
-
+
- ,
+ ,
)
```
-Keep in mind, you can also set `required` or `disabled` on the [Form.Handler](/uilib/extensions/forms/Form/Handler/). And invert the logic via the `Form.FieldProps` by using `required={false}` or `disabled={false}`.
+Keep in mind, you can also set `required` or `disabled` on the [Form.Handler](/uilib/extensions/forms/Form/Handler/). And invert the logic via the `Field.Provider` by using `required={false}` or `disabled={false}`.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Provider/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Provider/properties.mdx
new file mode 100644
index 00000000000..504c6029914
--- /dev/null
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Provider/properties.mdx
@@ -0,0 +1,10 @@
+---
+showTabs: true
+---
+
+import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable'
+import { FieldProviderProperties } from '@dnb/eufemia/src/extensions/forms/Field/Provider/FieldProviderDocs'
+
+## Properties
+
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx
index 071facf2e9f..3d42ae7f0f7 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx
@@ -411,7 +411,7 @@ The `required` property is a boolean that indicates whether the field is require
```
-**Note:** You can use the `required` property on the [Form.Handler](/uilib/extensions/forms/Form/Handler/) or [Wizard.Step](/uilib/extensions/forms/Wizard/Step/) components ([example](/uilib/extensions/forms/Form/Handler/demos/#required-and-optional-fields)). Additionally, the [Form.Section](/uilib/extensions/forms/Form/Section/) component as well as the [Form.FieldProps](/uilib/extensions/forms/Form/FieldProps/) provider has a `required` property, which will make all nested fields within that section required.
+**Note:** You can use the `required` property on the [Form.Handler](/uilib/extensions/forms/Form/Handler/) or [Wizard.Step](/uilib/extensions/forms/Wizard/Step/) components ([example](/uilib/extensions/forms/Form/Handler/demos/#required-and-optional-fields)). Additionally, the [Form.Section](/uilib/extensions/forms/Form/Section/) component as well as the [Field.Provider](/uilib/extensions/forms/feature-fields/Provider/) provider has a `required` property, which will make all nested fields within that section required.
```tsx
diff --git a/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/Provider.tsx b/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/Provider.tsx
index 765012caaff..d82927a2910 100644
--- a/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/Provider.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/Provider.tsx
@@ -28,7 +28,7 @@ import {
} from '../../types'
import type { IsolationProviderProps } from '../../Form/Isolation/Isolation'
import { debounce } from '../../../../shared/helpers'
-import FieldPropsProvider from '../../Form/FieldProps'
+import FieldPropsProvider from '../../Field/Provider'
import useUpdateEffect from '../../../../shared/helpers/useUpdateEffect'
import { isAsync } from '../../../../shared/helpers/isAsync'
import { useSharedState } from '../../../../shared/helpers/useSharedState'
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Provider/FieldProvider.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/Provider/FieldProvider.tsx
new file mode 100644
index 00000000000..d9e5c14044b
--- /dev/null
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/Provider/FieldProvider.tsx
@@ -0,0 +1,48 @@
+import React from 'react'
+import { Props as DataContextProps } from '../../DataContext/Provider'
+import { FormStatusProps } from '../../../../components/FormStatus'
+import FieldProviderContext from './FieldProviderContext'
+import SharedProvider from '../../../../shared/Provider'
+import { ContextProps } from '../../../../shared/Context'
+import useFieldProvider from './useFieldProvider'
+import { FieldProps, Path } from '../../types'
+
+export type FieldProviderProps = FieldProps & {
+ children: React.ReactNode
+
+ /**
+ * Locale to use for all nested Eufemia components
+ */
+ locale?: DataContextProps['locale']
+
+ /**
+ * Provide your own translations. Use the same format as defined in the translation files
+ */
+ translations?: DataContextProps['translations']
+
+ /** For internal use only */
+ overwriteProps?: {
+ [key: Path]: FieldProps
+ }
+
+ /** For internal use only */
+ formElement?: ContextProps['formElement']
+
+ /** For internal use only */
+ FormStatus?: { globalStatus: FormStatusProps }
+}
+
+function FieldProviderProvider(props: FieldProviderProps) {
+ const { children, ...restProps } = props
+ const { sharedProviderParams, ...providerValue } =
+ useFieldProvider(restProps)
+
+ return (
+
+ {children}
+
+ )
+}
+
+FieldProviderProvider._supportsSpacingProps = 'children'
+export default FieldProviderProvider
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Provider/FieldProviderContext.ts b/packages/dnb-eufemia/src/extensions/forms/Field/Provider/FieldProviderContext.ts
new file mode 100644
index 00000000000..677643207a6
--- /dev/null
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/Provider/FieldProviderContext.ts
@@ -0,0 +1,16 @@
+import React from 'react'
+import { UseFieldProps } from '../../types'
+
+export type FieldProviderContextProps = {
+ extend: (props: T) => T
+ inheritedProps?: UseFieldProps
+ inheritedContext?: UseFieldProps
+}
+
+const extend: FieldProviderContextProps['extend'] = (props) => props
+const FieldProviderContext =
+ React.createContext({
+ extend,
+ })
+
+export default FieldProviderContext
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/FieldProps/FieldPropsDocs.ts b/packages/dnb-eufemia/src/extensions/forms/Field/Provider/FieldProviderDocs.ts
similarity index 84%
rename from packages/dnb-eufemia/src/extensions/forms/Form/FieldProps/FieldPropsDocs.ts
rename to packages/dnb-eufemia/src/extensions/forms/Field/Provider/FieldProviderDocs.ts
index 44edc9fab0a..789ff072943 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Form/FieldProps/FieldPropsDocs.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/Provider/FieldProviderDocs.ts
@@ -1,7 +1,7 @@
import { PropertiesTableProps } from '../../../../shared/types'
import { dataValueProperties } from '../../hooks/DataValueDocs'
-export const FieldPropsProperties: PropertiesTableProps = {
+export const FieldProviderProperties: PropertiesTableProps = {
required: dataValueProperties.required,
disabled: dataValueProperties.disabled,
locale: {
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/FieldProps/__tests__/FieldProps.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/Provider/__tests__/FieldProvider.test.tsx
similarity index 54%
rename from packages/dnb-eufemia/src/extensions/forms/Form/FieldProps/__tests__/FieldProps.test.tsx
rename to packages/dnb-eufemia/src/extensions/forms/Field/Provider/__tests__/FieldProvider.test.tsx
index 8aa7406b9d7..c8f58a83808 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Form/FieldProps/__tests__/FieldProps.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/Provider/__tests__/FieldProvider.test.tsx
@@ -1,22 +1,131 @@
import React from 'react'
import { render } from '@testing-library/react'
+import FieldProviderContext from '../FieldProviderContext'
import { Form, Field } from '../../..'
import nbNO from '../../../constants/locales/nb-NO'
const nb = nbNO['nb-NO']
-describe('Form.FieldProps', () => {
+describe('Field.Provider', () => {
it('should have constant of _supportsSpacingProps="children"', () => {
- expect(Form.FieldProps._supportsSpacingProps).toBe('children')
+ expect(Field.Provider._supportsSpacingProps).toBe('children')
+ })
+
+ it('should merge inheritedContext with props passed to extend', () => {
+ let collectedProps = null
+
+ const Collector = (props) => {
+ return (
+
+ {({ extend }) => {
+ collectedProps = extend(props)
+ return null
+ }}
+
+ )
+ }
+
+ render(
+
+
+
+ )
+
+ expect(collectedProps).toEqual({
+ disabled: true,
+ myProp: 'value',
+ })
+ })
+
+ it('props passed to extend should override inheritedContext', () => {
+ let collectedProps = null
+
+ const Collector = (props) => {
+ return (
+
+ {({ extend }) => {
+ collectedProps = extend(props)
+ return null
+ }}
+
+ )
+ }
+
+ render(
+
+
+
+ )
+
+ expect(collectedProps).toEqual({
+ disabled: false,
+ myProp: 'value',
+ })
+ })
+
+ it('props passed to extend should override nested inheritedContext', () => {
+ let collectedProps = null
+
+ const Collector = (props) => {
+ return (
+
+ {({ extend }) => {
+ collectedProps = extend(props)
+ return null
+ }}
+
+ )
+ }
+
+ render(
+
+
+
+
+
+ )
+
+ expect(collectedProps).toEqual({
+ disabled: true,
+ myProp: 'value',
+ })
+ })
+
+ it('second provider should override nested inheritedContext', () => {
+ let collectedProps = null
+
+ const Collector = (props) => {
+ return (
+
+ {({ extend }) => {
+ collectedProps = extend(props)
+ return null
+ }}
+
+ )
+ }
+
+ render(
+
+
+
+
+
+ )
+
+ expect(collectedProps).toEqual({
+ disabled: false,
+ myProp: 'value',
+ })
})
describe('disable', () => {
it('should disable the input and button', () => {
render(
-
+
-
+
)
const input = document.querySelector('input')
@@ -28,10 +137,10 @@ describe('Form.FieldProps', () => {
it('should not disable the input when prop is set', () => {
render(
-
+
-
+
)
const input = document.querySelector('input')
@@ -44,10 +153,10 @@ describe('Form.FieldProps', () => {
it('should handle the disabled prop from the Form.Handler', () => {
const { rerender } = render(
-
+
-
+
)
@@ -59,10 +168,10 @@ describe('Form.FieldProps', () => {
rerender(
-
+
-
+
)
@@ -71,10 +180,10 @@ describe('Form.FieldProps', () => {
rerender(
-
+
-
+
)
@@ -84,12 +193,12 @@ describe('Form.FieldProps', () => {
it('should handle nested FieldProps', () => {
const { rerender } = render(
-
-
+
+
-
-
+
+
)
const input = document.querySelector('input')
@@ -99,24 +208,24 @@ describe('Form.FieldProps', () => {
expect(button).toBeDisabled()
rerender(
-
-
+
+
-
-
+
+
)
expect(input).not.toBeDisabled()
expect(button).not.toBeDisabled()
rerender(
-
-
+
+
-
-
+
+
)
expect(input).not.toBeDisabled()
@@ -125,10 +234,10 @@ describe('Form.FieldProps', () => {
it('should support data-* attributes in fields', () => {
render(
-
+
-
+
)
const input = document.querySelector('input')
@@ -142,9 +251,9 @@ describe('Form.FieldProps', () => {
describe('require', () => {
it('should require the input and button', () => {
render(
-
+
-
+
)
expect(document.querySelector('.dnb-form-status')).toHaveTextContent(
@@ -154,9 +263,9 @@ describe('Form.FieldProps', () => {
it('should not require the input when prop is set', () => {
render(
-
+
-
+
)
expect(
@@ -167,9 +276,9 @@ describe('Form.FieldProps', () => {
it('should handle the required prop from the Form.Handler', () => {
const { rerender } = render(
-
+
-
+
)
@@ -179,9 +288,9 @@ describe('Form.FieldProps', () => {
rerender(
-
+
-
+
)
@@ -191,9 +300,9 @@ describe('Form.FieldProps', () => {
rerender(
-
+
-
+
)
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Provider/__tests__/useFieldProvider.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/Provider/__tests__/useFieldProvider.test.tsx
new file mode 100644
index 00000000000..50692763097
--- /dev/null
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/Provider/__tests__/useFieldProvider.test.tsx
@@ -0,0 +1,89 @@
+import React from 'react'
+import { renderHook } from '@testing-library/react'
+import useFieldProvider from '../useFieldProvider'
+import FieldProviderContext from '../FieldProviderContext'
+
+describe('useFieldProvider', () => {
+ it('should return extend function and inherited props', () => {
+ const props = { overwriteProps: {}, test: 'propField' }
+ const { result } = renderHook(useFieldProvider, {
+ initialProps: props,
+ })
+
+ expect(result.current.extend).toBeInstanceOf(Function)
+ expect(result.current.inheritedProps).toEqual({ test: 'propField' })
+ expect(result.current.inheritedContext).toEqual({
+ test: 'propField',
+ })
+ })
+
+ it('extend function should merge overwriteProps correctly', () => {
+ const props = {
+ overwriteProps: { path: { value: 'overwriteField' } },
+ test: 'propField',
+ }
+ const { result } = renderHook(useFieldProvider, {
+ initialProps: props,
+ })
+
+ const valueProps = { path: '/test/path', value: 'valueProps' }
+
+ expect(result.current.extend(valueProps)).toEqual({
+ path: '/test/path',
+ test: 'propField',
+ value: 'overwriteField',
+ })
+ })
+
+ it('should pass inheritedContext to extend function', () => {
+ const props = {
+ overwriteProps: {},
+ }
+ const inheritedContext = { disabled: true }
+ const inheritedProps = null
+ const extend = () => null
+
+ const { result } = renderHook(useFieldProvider, {
+ initialProps: props,
+ wrapper: ({ children }) => (
+
+ {children}
+
+ ),
+ })
+
+ const valueProps = {}
+
+ expect(result.current.extend(valueProps)).toEqual({
+ disabled: true,
+ })
+ })
+
+ it('props passed to extend should override inheritedContext', () => {
+ const props = {
+ overwriteProps: {},
+ }
+ const inheritedContext = { disabled: true }
+ const inheritedProps = null
+ const extend = () => null
+
+ const { result } = renderHook(useFieldProvider, {
+ initialProps: props,
+ wrapper: ({ children }) => (
+
+ {children}
+
+ ),
+ })
+
+ const valueProps = { disabled: false }
+
+ expect(result.current.extend(valueProps)).toEqual({
+ disabled: false,
+ })
+ })
+})
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Provider/index.ts b/packages/dnb-eufemia/src/extensions/forms/Field/Provider/index.ts
new file mode 100644
index 00000000000..ccc0219cae3
--- /dev/null
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/Provider/index.ts
@@ -0,0 +1,2 @@
+export { default } from './FieldProvider'
+export * from './FieldProvider'
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Provider/useFieldProvider.ts b/packages/dnb-eufemia/src/extensions/forms/Field/Provider/useFieldProvider.ts
new file mode 100644
index 00000000000..948906382d6
--- /dev/null
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/Provider/useFieldProvider.ts
@@ -0,0 +1,112 @@
+import { useCallback, useContext, useMemo, useRef } from 'react'
+import {
+ assignPropsWithContext,
+ extendDeep,
+} from '../../../../shared/component-helper'
+import FieldProviderContext from './FieldProviderContext'
+import DataContext, { ContextState } from '../../DataContext/Context'
+import SharedContext, { ContextProps } from '../../../../shared/Context'
+import type { FieldProps } from '../../types'
+import { FieldProviderProps } from './FieldProvider'
+
+function useFieldProvider(props?: Omit) {
+ const { formElement, FormStatus, overwriteProps, ...restProps } =
+ props || {}
+ const nestedContext = useContext(FieldProviderContext)
+ const inheritedProps = nestedContext?.inheritedContext
+
+ const sharedContext = useContext(SharedContext)
+ const dataContextRef = useRef()
+ dataContextRef.current = useContext(DataContext)
+
+ /**
+ * Always use data context as the last source for localization
+ */
+ const locale = dataContextRef.current?.props?.locale ?? restProps?.locale
+
+ const nestedFieldProps = useMemo(() => {
+ if (inheritedProps && Object.keys(inheritedProps).length > 0) {
+ return { ...inheritedProps, ...restProps } as FieldProps
+ }
+
+ return restProps
+ }, [inheritedProps, restProps])
+
+ const sharedProviderParams: ContextProps = {}
+
+ if (typeof nestedFieldProps.disabled === 'boolean') {
+ sharedProviderParams.formElement = {
+ disabled: nestedFieldProps.disabled,
+ }
+ }
+ if (formElement) {
+ sharedProviderParams.formElement = formElement
+ }
+ if (FormStatus) {
+ sharedProviderParams.FormStatus = FormStatus
+ }
+ if (locale) {
+ sharedProviderParams.locale = locale
+ }
+ sharedProviderParams.translations = useMemo(() => {
+ const translations = extendDeep(
+ {},
+ sharedContext.translations,
+ restProps?.translations,
+ dataContextRef.current?.props?.translations
+ ) as ContextProps
+
+ return translations
+ }, [restProps?.translations, sharedContext.translations])
+
+ const extend = useCallback(
+ (fieldProps: T) => {
+ // Extract props from data context to be used in fields
+ const { required: requiredByContext } = dataContextRef.current
+
+ // Extract props from overwriteProps to be used in values
+ const key = overwriteProps && fieldProps?.path?.split('/')?.pop()
+ const overwrite = overwriteProps?.[key]
+ // Overwrite given schema props
+ if (overwrite && fieldProps?.schema) {
+ Object.keys(fieldProps.schema).forEach((key) => {
+ if (overwrite?.[key]) {
+ fieldProps.schema[key] = overwrite[key]
+ }
+ })
+ }
+
+ const props = overwrite
+ ? { ...fieldProps, ...overwrite }
+ : fieldProps
+ const required =
+ requiredByContext ?? nestedContext?.inheritedContext?.required
+
+ const value =
+ typeof required !== 'undefined' ||
+ Object.keys(nestedFieldProps).length > 0
+ ? assignPropsWithContext(
+ props,
+ { required },
+ nestedFieldProps as Record
+ )
+ : props
+
+ return value as T
+ },
+ [
+ nestedContext?.inheritedContext?.required,
+ nestedFieldProps,
+ overwriteProps,
+ ]
+ )
+
+ return {
+ extend,
+ inheritedProps: restProps,
+ inheritedContext: nestedFieldProps,
+ sharedProviderParams,
+ }
+}
+
+export default useFieldProvider
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/index.ts b/packages/dnb-eufemia/src/extensions/forms/Field/index.ts
index 2190e5f122a..cbd10281a77 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/index.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/index.ts
@@ -1,3 +1,4 @@
+export { default as Provider } from './Provider'
export { default as Composition } from './Composition'
export { default as String } from './String'
export { default as Number } from './Number'
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/FieldProps/FieldProps.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/FieldProps/FieldProps.tsx
deleted file mode 100644
index 20bd6a6f7e2..00000000000
--- a/packages/dnb-eufemia/src/extensions/forms/Form/FieldProps/FieldProps.tsx
+++ /dev/null
@@ -1,141 +0,0 @@
-import React, { useCallback, useContext, useMemo, useRef } from 'react'
-import DataContext, { ContextState } from '../../DataContext/Context'
-import { Props as DataContextProps } from '../../DataContext/Provider'
-import { FormStatusProps } from '../../../../components/FormStatus'
-import {
- assignPropsWithContext,
- extendDeep,
-} from '../../../../shared/component-helper'
-import FieldPropsContext from './FieldPropsContext'
-import SharedProvider from '../../../../shared/Provider'
-import SharedContext, { ContextProps } from '../../../../shared/Context'
-import type { FieldProps, Path, UseFieldProps } from '../../types'
-
-export type FieldPropsProps = FieldProps & {
- children: React.ReactNode
-
- /**
- * Locale to use for all nested Eufemia components
- */
- locale?: DataContextProps['locale']
-
- /**
- * Provide your own translations. Use the same format as defined in the translation files
- */
- translations?: DataContextProps['translations']
-
- /** For internal use only */
- overwriteProps?: {
- [key: Path]: FieldProps
- }
-
- /** For internal use only */
- deep?: boolean
-
- /** For internal use only */
- formElement?: ContextProps['formElement']
-
- /** For internal use only */
- FormStatus?: { globalStatus: FormStatusProps }
-}
-
-function FieldPropsProvider(props: FieldPropsProps) {
- const {
- children,
- formElement,
- FormStatus,
- overwriteProps,
- deep,
- ...restProps
- } = props
-
- const sharedProviderParams: ContextProps = {}
- const nestedContext = useContext(FieldPropsContext)
- const sharedContext = useContext(SharedContext)
- const dataContextRef = useRef()
- dataContextRef.current = useContext(DataContext)
-
- /**
- * Always use data context as the last source for localization
- */
- const locale = dataContextRef.current?.props?.locale ?? restProps?.locale
-
- const nestedFieldProps = useMemo(() => {
- return Object.assign(
- nestedContext?.inheritedProps || {},
- restProps
- ) as UseFieldProps
- }, [nestedContext?.inheritedProps, restProps])
-
- if (typeof nestedFieldProps.disabled === 'boolean') {
- sharedProviderParams.formElement = {
- disabled: nestedFieldProps.disabled,
- }
- }
- if (formElement) {
- sharedProviderParams.formElement = formElement
- }
- if (FormStatus) {
- sharedProviderParams.FormStatus = FormStatus
- }
- if (locale) {
- sharedProviderParams.locale = locale
- }
- sharedProviderParams.translations = useMemo(() => {
- const translations = extendDeep(
- {},
- sharedContext.translations,
- restProps?.translations,
- dataContextRef.current?.props?.translations
- ) as ContextProps
-
- return translations
- }, [restProps?.translations, sharedContext.translations])
-
- const extend = useCallback(
- (fieldProps: T) => {
- // Extract props from data context to be used in fields
- const { required: requiredByContext } = dataContextRef.current
-
- // Extract props from overwriteProps to be used in fields
- const key = overwriteProps && fieldProps?.path?.split('/')?.pop()
- const overwrite = overwriteProps?.[key]
-
- // Overwrite given schema props
- if (overwrite && fieldProps?.schema) {
- Object.keys(fieldProps.schema).forEach((key) => {
- if (overwrite?.[key]) {
- fieldProps.schema[key] = overwrite[key]
- }
- })
- }
-
- const value = assignPropsWithContext(
- overwrite ? { ...fieldProps, ...overwrite } : fieldProps,
- {
- required:
- requiredByContext ?? nestedContext?.inheritedContext?.required,
- },
- nestedFieldProps as Record
- )
-
- return (deep ? nestedContext.extend(value) : value) as T
- },
- [deep, nestedContext, nestedFieldProps, overwriteProps]
- )
-
- return (
-
- {children}
-
- )
-}
-
-FieldPropsProvider._supportsSpacingProps = 'children'
-export default FieldPropsProvider
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/FieldProps/FieldPropsContext.ts b/packages/dnb-eufemia/src/extensions/forms/Form/FieldProps/FieldPropsContext.ts
deleted file mode 100644
index f4d3db77ab7..00000000000
--- a/packages/dnb-eufemia/src/extensions/forms/Form/FieldProps/FieldPropsContext.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from 'react'
-import { UseFieldProps } from '../../types'
-
-export type FieldPropsContextProps = {
- extend: (props: T) => T
- inheritedProps?: UseFieldProps
- inheritedContext?: UseFieldProps
-}
-
-const extend: FieldPropsContextProps['extend'] = (props) => props
-const FieldPropsContext = React.createContext({
- extend,
-})
-
-export default FieldPropsContext
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/FieldProps/index.ts b/packages/dnb-eufemia/src/extensions/forms/Form/FieldProps/index.ts
deleted file mode 100644
index f04ba774dd0..00000000000
--- a/packages/dnb-eufemia/src/extensions/forms/Form/FieldProps/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export { default } from './FieldProps'
-export * from './FieldProps'
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Section/Section.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/Section/Section.tsx
index 0701798869b..7712f998baf 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Form/Section/Section.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Form/Section/Section.tsx
@@ -2,7 +2,7 @@ import React, { useCallback, useContext, useMemo } from 'react'
import SectionContext, { SectionContextState } from './SectionContext'
import DataContext from '../../DataContext/Context'
import Provider from '../../DataContext/Provider/Provider'
-import FieldPropsProvider from '../FieldProps'
+import FieldPropsProvider from '../../Field/Provider'
import SectionContainerProvider from './containers/SectionContainerProvider'
import ViewContainer from './ViewContainer'
import EditContainer from './EditContainer'
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Section/__tests__/Section.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/Section/__tests__/Section.test.tsx
index 9f3127e185d..4c2992accd1 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Form/Section/__tests__/Section.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Form/Section/__tests__/Section.test.tsx
@@ -4,7 +4,7 @@ import { fireEvent, render } from '@testing-library/react'
import { Field, Form, JSONSchema, Tools, Value } from '../../..'
import { SectionProps } from '../Section'
import { Props as FieldNameProps } from '../../../Field/Name'
-import FieldPropsProvider from '../../FieldProps'
+import FieldPropsProvider from '../../../Field/Provider'
import { GenerateRef as GeneratePropsRef } from '../../../Tools/ListAllProps'
import { GenerateRef as GenerateSchemaRef } from '../../../Tools/GenerateSchema'
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/Visibility.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/Visibility.tsx
index f9b8cbca37e..244f103abf1 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/Visibility.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/Visibility.tsx
@@ -5,7 +5,7 @@ import useMountEffect from '../../../../shared/helpers/useMountEffect'
import HeightAnimation, {
HeightAnimationProps,
} from '../../../../components/HeightAnimation'
-import FieldProps from '../FieldProps'
+import FieldProvider from '../../Field/Provider'
import useVisibility from './useVisibility'
import type { Path, UseFieldProps } from '../../types'
@@ -142,7 +142,7 @@ function Visibility({
compensateForGap={compensateForGap}
{...rest}
>
- {content}
+ {content}
)
}
@@ -151,7 +151,7 @@ function Visibility({
const props = !open ? fieldPropsWhenHidden : null
return (
- {content}
+ {content}
)
}
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/index.ts b/packages/dnb-eufemia/src/extensions/forms/Form/index.ts
index 97b605f8d96..ddb4dd9a3bb 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Form/index.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/Form/index.ts
@@ -1,3 +1,5 @@
+import FieldProvider from '../Field/Provider/FieldProvider'
+
export { default as Handler } from './Handler'
export { default as Element } from './Element'
export { default as Appearance } from './Appearance'
@@ -9,7 +11,6 @@ export { default as SubHeading } from './SubHeading'
export { default as Visibility } from './Visibility'
export { default as Section } from './Section'
export { default as Isolation } from './Isolation'
-export { default as FieldProps } from './FieldProps'
export { default as useData } from './data-context/useData'
export { default as setData } from './data-context/setData'
export { default as getData } from './data-context/getData'
@@ -28,3 +29,9 @@ export { default as useLocale } from '../hooks/useTranslation'
* @deprecated Use `useValidation` instead
*/
export { default as useError } from './data-context/useValidation'
+
+/**
+ * Can be removed in v11
+ * @deprecated Use `Field.Provider` instead
+ */
+export const FieldProps = FieldProvider
diff --git a/packages/dnb-eufemia/src/extensions/forms/Wizard/Step/Step.tsx b/packages/dnb-eufemia/src/extensions/forms/Wizard/Step/Step.tsx
index f3d7606853e..da8ce39c1fc 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Wizard/Step/Step.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Wizard/Step/Step.tsx
@@ -5,7 +5,7 @@ import { Props as FlexContainerProps } from '../../../../components/flex/Contain
import WizardContext from '../Context/WizardContext'
import Flex from '../../../../components/flex/Flex'
import { convertJsxToString } from '../../../../shared/component-helper'
-import FieldProps from '../../Form/FieldProps'
+import FieldProvider from '../../Field/Provider'
import type { VisibleWhen } from '../../Form/Visibility'
// SSR warning fix: https://gist.github.com/gaearon/e7d97cdf38a2907924ea12e4ebdf3c85
@@ -107,7 +107,7 @@ function Step(props: Props): JSX.Element {
{...restProps}
>
{fieldProps ? (
- {children}
+ {children}
) : (
children
)}
diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts
index 8ed7f045ece..db64ee9bf93 100644
--- a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts
@@ -24,7 +24,7 @@ import {
} from '../types'
import { Context as DataContext, ContextState } from '../DataContext'
import { clearedData } from '../DataContext/Provider/Provider'
-import FieldPropsContext from '../Form/FieldProps/FieldPropsContext'
+import FieldProviderContext from '../Field/Provider/FieldProviderContext'
import { combineDescribedBy, warn } from '../../../shared/component-helper'
import useId from '../../../shared/helpers/useId'
import useUpdateEffect from '../../../shared/helpers/useUpdateEffect'
@@ -84,7 +84,7 @@ export default function useFieldProps(
localeProps: Props & FieldPropsGeneric,
{ executeOnChangeRegardlessOfError = false } = {}
): typeof localeProps & ReturnAdditional {
- const { extend } = useContext(FieldPropsContext)
+ const { extend } = useContext(FieldProviderContext)
const props = extend(localeProps)
const {