+
Here is a paragraph with some nonsense lipsum text. Contrary to popular
belief, Lorem Ipsum passage, and going through the cites of the word in
classical literature, discovered the undoubtable source. Lorem Ipsum
@@ -78,8 +95,10 @@ The default font family for all web applications is `Roboto`, however for headli
### Roboto bold
+**NB!** bold is generally not used, use medium, unless there is a specific unique use case.
+
-
+
Here is a paragraph with some nonsense lipsum text. Contrary to popular
belief, Lorem Ipsum passage, and going through the cites of the word in
classical literature, discovered the undoubtable source. Lorem Ipsum
diff --git a/packages/dnb-design-system-portal/src/docs/quickguide-designer/typography/font-weights.mdx b/packages/dnb-design-system-portal/src/docs/quickguide-designer/typography/font-weights.mdx
index 656e1eca38e..a3ecae9eb72 100644
--- a/packages/dnb-design-system-portal/src/docs/quickguide-designer/typography/font-weights.mdx
+++ b/packages/dnb-design-system-portal/src/docs/quickguide-designer/typography/font-weights.mdx
@@ -1,6 +1,8 @@
## Font Weights
-Achieved with HTML classes: `.dnb-typo-regular`, `.dnb-typo-medium`, `.dnb-typo-bold`
+Achieved with HTML classes: `.dnb-t__weight--regular`,`.dnb-t__weight--medium` or `.dnb-t__weight--bold`.
+
+The old classes, `.dnb-typo-regular`, `.dnb-typo-medium` and `.dnb-typo-bold`, still work, but will also set font-family and font-style.
### Body Regular
@@ -8,7 +10,7 @@ Achieved with HTML classes: `.dnb-typo-regular`,
+
Here is a paragraph with some nonsense lipsum text. Contrary to popular
belief, Lorem Ipsum passage, and going through the cites of the word in
classical literature, discovered the undoubtable source. Lorem Ipsum
@@ -17,11 +19,10 @@ no need to use a class.
-
### Body Medium
-
+
Here is a paragraph with some nonsense lipsum text. Contrary to popular
belief, Lorem Ipsum passage, and going through the cites of the word in
classical literature, discovered the undoubtable source. Lorem Ipsum
@@ -29,12 +30,13 @@ no need to use a class.
Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC.
-
### Body Bold
+**NB!** bold is generally not used, use medium, unless there is a specific unique use case.
+
-
+
Here is a paragraph with some nonsense lipsum text. Contrary to popular
belief, Lorem Ipsum passage, and going through the cites of the word in
classical literature, discovered the undoubtable source. Lorem Ipsum
diff --git a/packages/dnb-design-system-portal/src/docs/quickguide-designer/typography/typographic-elements.mdx b/packages/dnb-design-system-portal/src/docs/quickguide-designer/typography/typographic-elements.mdx
index 2c81995945d..294a9e2b1f1 100644
--- a/packages/dnb-design-system-portal/src/docs/quickguide-designer/typography/typographic-elements.mdx
+++ b/packages/dnb-design-system-portal/src/docs/quickguide-designer/typography/typographic-elements.mdx
@@ -137,16 +137,16 @@ This is an overview of the default, basic typographic elements such as **heading
### Note:
-There are two methods to create small text. One, is to use the `.dnb-p--small` modifier class which can be used on paragraphs etc. and allows you to use a bottom margin. The other method is to just use a `` tag which is inline and cannot have a margin.
+There are two methods to create small text. One, is to use the `.dnb-t__size--small` modifier class. The other method is to just use a `` tag.
### Example
-
- This is a paragraph with a modifier class . This is the small
- content. Quem facilisi moderatius id eam, id tamquam albucius per. Vel
- quem congue appareat cu, mei te eros convenire. Sea bonorum epicuri ea,
- ei exerci tacimates pro, aliquam pertinacia eu vim.
+
+ This is a paragraph with a modifier class `.dnb-t__size--small`.
+ This is the small content. Quem facilisi moderatius id eam, id tamquam
+ albucius per. Vel quem congue appareat cu, mei te eros convenire. Sea
+ bonorum epicuri ea, ei exerci tacimates pro, aliquam pertinacia eu vim.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/about-the-lib/releases/eufemia/v10-info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/about-the-lib/releases/eufemia/v10-info.mdx
index 1b91730ef90..2fbd71f4e1b 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/about-the-lib/releases/eufemia/v10-info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/about-the-lib/releases/eufemia/v10-info.mdx
@@ -367,7 +367,7 @@ The Anchor was moved from `/elements` to `/components`.
2. Only camelCase props are supported for Drawer, so you will need to update the prop names.
3. `Modal.Inner` or `Modal.Content` converts to `Drawer.Body`.
- 4. `Modal.Bar` converts to `Drawer.Navigaton`.
+ 4. `Modal.Bar` converts to `Drawer.Navigation`.
5. `Modal` was a class component and `Drawer` is a functional component.
When you convert from ` ` or ` ` to ` ` – follow these steps:
@@ -378,7 +378,7 @@ The Anchor was moved from `/elements` to `/components`.
2. Only camelCase props are supported for Dialog, so you will need to update the prop names.
3. `Modal.Inner` or `Modal.Content` converts to `Dialog.Body`.
- 4. `Modal.Bar` converts to `Dialog.Navigaton`.
+ 4. `Modal.Bar` converts to `Dialog.Navigation`.
5. `Modal` was a class component and `Dialog` is a functional component.
### [Lists](/uilib/elements/lists)
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 f85e7c2f3cc..300565f693e 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
@@ -148,6 +148,8 @@ The `InputPassword` component has been moved to `Field.Password`, and is now a p
- replace `useError` with `useValidation`.
- replace Form.Iterate label variable `{itemNr}` with `{itemNo}`.
- replace `Form.FieldProps` with `Field.Provider`.
+- replace `... ` with `... `.
+- replace `... ` with `... `.
## NumberFormat
@@ -199,4 +201,9 @@ const errorMessages = {
- Got removed. Simply provide your error message as a object in the `errorMessages` property with an `useMemo` hook.
+## DrawerList
+
+- replace type `DrawerListDataObjectUnion` with `DrawerListDataArrayItem`.
+- replace type `DrawerListDataObject` with `DrawerListDataArrayObject`.
+
_February, 6. 2024_
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/autocomplete/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/components/autocomplete/Examples.tsx
index f1e5ff9c8df..f1f1102db33 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/autocomplete/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/autocomplete/Examples.tsx
@@ -543,6 +543,50 @@ export const AutocompleteDisabledExample = () => (
)
+export const AutocompleteDisabledOptionsExample = () => (
+
+
+
+
+ The Shawshank Redemption
+
+ ),
+ year: 1994,
+ },
+ {
+ disabled: true,
+ content: ['The Godfather', 'Line with more info'],
+ year: 1972,
+ },
+ {
+ disabled: true,
+ content: [
+ 'The Godfather: Part II',
+
+ Anchor 1
+ ,
+
+ Anchor 2
+ ,
+ 'Line with more info',
+ ],
+ year: 1974,
+ },
+ { disabled: true, content: 'The Dark Knight', year: 2008 },
+ ]}
+ label="Label"
+ bottom
+ />
+
+
+)
+
export const AutocompleteContentAsArrayExample = () => (
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/autocomplete/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/autocomplete/properties.mdx
index 41bacf01df2..2732054046d 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/autocomplete/properties.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/autocomplete/properties.mdx
@@ -6,6 +6,7 @@ import TranslationsTable from 'dnb-design-system-portal/src/shared/parts/Transla
import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable'
import { autocompleteProperties } from '@dnb/eufemia/src/components/autocomplete/AutocompleteDocs'
import { DrawerListProperties } from '@dnb/eufemia/src/fragments/drawer-list/DrawerListDocs'
+import DrawerListDataDoc from '../fragments/drawer-list/_prop-data.mdx'
## Properties
@@ -17,59 +18,7 @@ You may check out the [DrawerList Properties](#drawerlist-properties) down below
-## Data structure
-
-```js
-// 1. as array
-const data = [
- // Every data item can, beside "content" - contain what ever
- {
- // (optional) can be what ever
- selected_key: 'key_0',
-
- // (optional) is show instead of "content", once selected
- selected_value: 'Item 1 Value',
- suffix_value: 'Addition 1',
-
- // Item content as a string, array or React Element
- content: 'Item 1 Content',
- },
-
- // more items ...
- {
- selected_key: 'key_1',
- content: (
- <>
-
- Searchable content
- >
- ),
- },
- {
- selected_key: 'key_2',
- selected_value: 'Item 3 Value',
- suffix_value: 'Addition 3',
- content: (
-
-
- Searchable content
-
- ),
- },
- {
- selected_key: 'key_3',
- selected_value: 'Item 4 Value',
- suffix_value: 'Addition 4',
- content: ['Item 4 Content A', <>Custom Component>],
- },
-]
-
-// 2. as object
-const data = {
- a: 'A',
- b: 'B',
-}
-```
+
## Translations
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/autocomplete/visual-tests.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/autocomplete/visual-tests.mdx
index 848c1bd6bea..98faa46b3c1 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/autocomplete/visual-tests.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/autocomplete/visual-tests.mdx
@@ -5,9 +5,11 @@ draft: true
import {
AutocompleteOpened,
AutocompleteDisabledExample,
+ AutocompleteDisabledOptionsExample,
} from 'Docs/uilib/components/autocomplete/Examples'
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/card/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/components/card/Examples.tsx
index f6805a5f3c5..444d749ad75 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/card/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/card/Examples.tsx
@@ -5,7 +5,6 @@ import {
Flex,
Grid,
H2,
- Hr,
P,
Section,
Table,
@@ -17,16 +16,12 @@ import { Field, Form } from '@dnb/eufemia/src/extensions/forms'
export const Default = () => {
return (
-
-
+
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi
cursus pharetra elit in bibendum.
-
- Praesent nunc ipsum, convallis eget convallis gravida, vehicula
- vitae metus.
-
)
@@ -35,11 +30,11 @@ export const Default = () => {
export const NestedCards = () => {
return (
-
+
First Card
-
+
Second Card
-
+
Third Card (for edge cases only)
@@ -176,10 +171,8 @@ export const Stack = () => {
return (
-
-
-
-
+ Stacked content
+ Stacked content
)
@@ -188,12 +181,12 @@ export const Stack = () => {
export const VerticalFields = () => {
return (
-
+
-
+
)
}
@@ -201,12 +194,12 @@ export const VerticalFields = () => {
export const HorizontalFields = () => {
return (
-
+
-
+
)
}
@@ -274,3 +267,20 @@ export const WithNestedSection = () => {
)
}
+
+export const WithOutset = () => {
+ return (
+
+
+ I'm left aligned
+
+ Card content
+
+ Nested card
+
+
+
+
+
+ )
+}
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/card/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/card/demos.mdx
index 3c88104ef8b..0db53fa4213 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/card/demos.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/card/demos.mdx
@@ -12,17 +12,23 @@ import * as Examples from './Examples'
### Vertical fields
+When using Eufemia Forms, you may want to use [Form.Card](/uilib/extensions/forms/Form/Card/) instead of the original Card component.
+
### Horizontal fields
+When using Eufemia Forms, you may want to use [Form.Card](/uilib/extensions/forms/Form/Card/) instead of the original Card component.
+
### Stack
-The Card components needs to have `stack={true}` or `align="stretch"` in order to stretch its children components.
+When `stack` is set to `true`, the Card will add a gap between its children and stretch them to the full.
+
+For [form components](uilib/extensions/forms/), you should use [Form.Card](/uilib/extensions/forms/Form/Card/) instead of the original Card component.
-For [form components](uilib/extensions/forms/), you should use `stack={true}` to get the correct spacing.
+When `stack` is set to `true`, the Card will add a gap between its children and stretch them to the full.
@@ -32,6 +38,13 @@ Nested cards have `responsive={false}` by default and will not behave responsive
+## With `outset`
+
+When using `outset`, the Card will break out of the layout container.
+On small screens (mobile) the outset is removed.
+
+
+
### Without padding
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/Examples.tsx
index f42990760e3..ee284002148 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/Examples.tsx
@@ -250,11 +250,11 @@ export const DropdownDirections = () => {
data={[
['Vertical', 'alignment'],
<>
- Vertical
+ Vertical
alignment
>,
-
+
Horizontal
alignment
@@ -446,6 +446,26 @@ export const DropdownDisabled = () => (
)
+export const DropdownDisabledOptions = () => (
+
+
+
+
+
+)
+
export const DropdownDisabledTertiary = () => (
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/demos.mdx
index 564a2d0ff6c..da1f84accdb 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/demos.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/demos.mdx
@@ -12,6 +12,7 @@ import {
DropdownTertiaryRight,
DropdownMoreMenu,
DropdownDisabled,
+ DropdownDisabledOptions,
DropdownCustomEvent,
DropdownSizes,
DropdownCustomWidth,
@@ -87,6 +88,10 @@ With long list to make it scrollable and searchable
+Individual options can also be disabled.
+
+
+
### Disabled tertiary dropdown
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/events.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/events.mdx
index 8f04391fb14..368183e0d09 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/events.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/events.mdx
@@ -4,12 +4,12 @@ showTabs: true
## Events
-| Events | Description |
-| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `on_change` | _(optional)_ will be called on state changes made by the user. Returns an object with the new selected `data` item `{ data, event, attributes, value }`. |
-| `on_select` | _(optional)_ will be called once the user selects an item by a click or keyboard navigation. Returns an object with the new selected `data` item `{ data, event, attributes, value, active_item }`. The **active_item** property is the currently selected item by keyboard navigation |
-| `on_show` | _(optional)_ will be called once the user presses the dropdown. Returns the data item `{ data, attributes }`. |
-| `on_hide` | _(optional)_ will be called once the user presses the dropdown again, or clicks somewhere else. Returns the data item `{ data, attributes }`. |
+| Events | Description |
+| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `on_change` | _(optional)_ will be called on state changes made by the user. Returns an object with the new selected `data` item `{ data, event, attributes, value }`. |
+| `on_select` | _(optional)_ will be called once the user focuses or selects an item by a click or keyboard navigation. Returns an object with the new selected `data` item `{ data, event, attributes, value, active_item }`. The **active_item** property is the currently selected item by keyboard navigation |
+| `on_show` | _(optional)_ will be called once the user presses the dropdown. Returns the data item `{ data, attributes }`. |
+| `on_hide` | _(optional)_ will be called once the user presses the dropdown again, or clicks somewhere else. Returns the data item `{ data, attributes }`. |
### The `on_change` vs `on_select` difference
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/properties.mdx
index 46db4976934..7f8fe31a14e 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/properties.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/properties.mdx
@@ -5,6 +5,7 @@ showTabs: true
import TranslationsTable from 'dnb-design-system-portal/src/shared/parts/TranslationsTable'
import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable'
import { DrawerListProperties } from '@dnb/eufemia/src/fragments/drawer-list/DrawerListDocs'
+import DrawerListDataDoc from '../fragments/drawer-list/_prop-data.mdx'
## Properties
@@ -54,49 +55,7 @@ Should either be an index (integer) of the data array or a key – defined by `s
If `data` is an object, use the object key as the `value` to define the selected item. Can be a string or integer.
-## Data structure
-
-```js
-// 1. as array
-const data = [
- // Every data item can, beside "content" - contain what ever
- {
- // (optional) can be what ever
- selectedKey: 'key_0',
-
- // (optional) is show instead of "content", once selected
- selected_value: 'Item 1 Value',
- suffix_value: 'Addition 1',
-
- // Item content as a string or array
- content: 'Item 1 Content',
- },
-
- // more items ...
- {
- selectedKey: 'key_1',
- content: ['Item 2 Value', 'Item 2 Content'],
- },
- {
- selectedKey: 'key_2',
- selected_value: 'Item 3 Value',
- suffix_value: 'Addition 3',
- content: ['Item 3 Content A', 'Item 3 Content B'],
- },
- {
- selectedKey: 'key_3',
- selected_value: 'Item 4 Value',
- suffix_value: 'Addition 4',
- content: ['Item 4 Content A', <>Custom Component>],
- },
-]
-
-// 2. as object
-const data = {
- a: 'A',
- b: 'B',
-}
-```
+
## Translations
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/fragments.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/fragments.mdx
index 65fddd82274..504d7525830 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/fragments.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/fragments.mdx
@@ -10,10 +10,6 @@ import ListFragments from 'dnb-design-system-portal/src/shared/parts/ListFragmen
# Fragments
-Fragments are small, low-level and reusable parts used inside other components.
-
-You may use them only to build new components from.
-
## Import
You import them like so:
@@ -26,6 +22,12 @@ import {
} from '@dnb/eufemia/fragments'
```
+## Description
+
+Fragments are small, low-level and reusable parts used inside other components.
+
+You may use them only to build new components from.
+
## Available Fragments
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/Examples.tsx
index 4061bfc69d5..f4513acd0b9 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/Examples.tsx
@@ -126,6 +126,31 @@ export const DrawerListExampleDefault = () => (
)
+export const DrawerListExampleDisabled = () => (
+
+
+
+
+
+)
+
export const DrawerListExampleSingleItem = () => (
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/_prop-data.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/_prop-data.mdx
new file mode 100644
index 00000000000..f3bf6c00d99
--- /dev/null
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/_prop-data.mdx
@@ -0,0 +1,125 @@
+import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable'
+import { DrawerListItem } from '@dnb/eufemia/src/fragments/drawer-list/DrawerListDocs'
+
+## The `data` property
+
+The `data` can be structured in two main ways: as an array, or as an object. An array is preferred as it gives you the most options.
+
+### `data` as an array
+
+```ts
+// an array can contain complex items and offers the most control
+const data = [
+ {
+ content: "Item 1",
+ },
+ {
+ content: Item 2
+ },
+ {
+ content: ["Item 3", "Line 2", Line 3 ]
+ },
+ {
+ content: ['Main account', '1234 12 12345'],
+ selected_value: 'Main account (605,22 kr)',
+ suffix_value: '605,22 kr',
+ },
+ {
+ content: ['Old account', Closed ],
+ disabled: true,
+ suffix_value: '0,00 kr',
+ },
+]
+
+// If you only use the `content` property, you can use it directly in the array.
+// This list is identical to the one above:
+const data = [
+ "Item 1",
+ Item 2 ,
+ ["Item 3", "Line 2", Line 3 ],
+ {
+ content: ['Main account', '1234 12 12345'],
+ selected_value: 'Main account (605,22 kr)',
+ suffix_value: '605,22 kr',
+ },
+ {
+ content: ['Old account', Closed ],
+ disabled: true,
+ suffix_value: '0,00 kr',
+ },
+]
+
+const onChange = ({ data, value }) => {
+ console.log(data) // returns the item as it appears in the array
+ console.log(value) // returns the index of the item
+}
+```
+
+Each object in the array have the following properties:
+
+
+
+### `data` as an object
+
+A simpler alternative, but with less options
+
+```ts
+// Each entry can contain the same type of value as the array's `content` property
+const data = {
+ first: "Item 1",,
+ second: Item 2 ,
+ last: ["Item 3", "Line 2", Line 3 ],
+}
+
+const onChange = ({ data, value }) => {
+ console.log(data)
+ // returns a generated object representing the item:
+ // {
+ // selectedKey: 'first',
+ // value: 'first',
+ // content: 'Item 1',
+ // type: 'object'
+ // }
+
+ console.log(value) // returns the key ("first", "second", or "last"), instead of an index
+
+}
+
+```
+
+### `data` types overview
+
+The following is an overview of all the types that the `data` prop accepts. (These are not actual names of actual types in the library.)
+
+```ts
+// The visual content that is shown in one DrawerList item.
+// An array can be used to define multiple lines.
+type CONTENT = string | React.Node | (string | React.Node)[]
+
+// An array item
+type ARRAY_OBJECT = {
+ content: CONTENT
+ disabled?: boolean
+ selectedKey?: string | number
+ selected_value?: string | React.Node
+ suffix_value?: string | React.Node
+}
+
+// `data` as an array. A list of "ARRAY_OBJECT" types is preferred,
+// but the "CONTENT" type can be useful for simple lists.
+type ARRAY = (CONTENT | ARRAY_OBJECT)[]
+
+// `data` as an object. Can only contain the "CONTENT" type.
+// Each `key` behaves like the "ARRAY_OBJECT"'s `selectedKey`.
+type RECORD = Record
+
+// An object or array that represents the entire DrawerList list.
+type DATA = ARRAY | RECORD
+
+// The final type of the `data` prop:
+let data: DATA | () => DATA
+```
+
+#### JSON string
+
+There is technically support for sending in a JSON string of the data to the `data` prop. But this is an old functionality that we do not really support anymore.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/demos.mdx
index f08e26e89d1..8b0f9fe927a 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/demos.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/demos.mdx
@@ -6,6 +6,7 @@ import {
DrawerListExampleInteractive,
DrawerListExampleOnlyToVisualize,
DrawerListExampleDefault,
+ DrawerListExampleDisabled,
DrawerListExampleSingleItem,
DrawerListExampleMarkup,
} from 'Docs/uilib/components/fragments/drawer-list/Examples'
@@ -24,6 +25,10 @@ import {
+### Disabled
+
+
+
### Custom event and link on single item
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/properties.mdx
index baf76d87302..7cd82e4e733 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/properties.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/properties.mdx
@@ -4,7 +4,10 @@ showTabs: true
import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable'
import { DrawerListProperties } from '@dnb/eufemia/src/fragments/drawer-list/DrawerListDocs'
+import DrawerListDataDoc from './_prop-data.mdx'
## Properties
+
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/progress-indicator/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/components/progress-indicator/Examples.tsx
index baae35ec130..30d98e9ca8b 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/progress-indicator/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/progress-indicator/Examples.tsx
@@ -66,7 +66,9 @@ export const ProgressIndicatorCircularLabelInsideExample = () => (
labelDirection="inside"
data-visual-test="progress-indicator-label-inside"
>
- {72}%
+
+ {72}%
+
)
@@ -360,7 +362,7 @@ const StyledLabel = styled.span`
`
const MyCustomLabel = ({ children, ...rest }) => (
{children}
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/section/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/section/properties.mdx
index f7f18d8e6bc..72f44708195 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/section/properties.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/section/properties.mdx
@@ -2,21 +2,12 @@
showTabs: true
---
+import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable'
+import { SectionProperties } from '@dnb/eufemia/src/components/section/SectionDocs'
+
## Properties
-| Properties | Description |
-| --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `variant` | _(optional)_ defines the semantic purpose and subsequently the style of the visual helper. Will take precedence over the style_type property |
-| `element` | _(optional)_ define what HTML element should be used. Defaults to ``. |
-| `breakout` | _(optional)_ use `true` to enable a fullscreen breakout look. Supports also media query breakpoints like `{ small: boolean }`. Defaults to `true`. |
-| `outline` | _(optional)_ define a custom border color. If `true` is given, `color-black-8` is used. Use a Eufemia [color](/uilib/usage/customisation/colors/). Supports also media query breakpoints like `{ small: 'black-8' }` |
-| `roundedCorner` | _(optional)_ use `true` to enable rounded corners (border-radius). Supports also media query breakpoints like `{ small: boolean }`. Defaults to `false`. |
-| `backgroundColor` | _(optional)_ define a custom background color, instead of a variant. Use a Eufemia [color](/uilib/usage/customisation/colors/). Supports also media query breakpoints like `{ small: 'white' }`. |
-| `dropShadow` | _(optional)_ use `true` to show the default Eufemia DropShadow. Supports also media query breakpoints like `{ small: true }`. |
-| `textColor` | _(optional)_ define a custom text color to compliment the backgroundColor. Use a Eufemia [color](/uilib/usage/customisation/colors/). Supports also media query breakpoints like `{ small: 'black-80' }`. |
-| `innerSpace` | _(optional)_ will add a padding around the content. Supports also media query breakpoints like `{small: { top: 'medium' }}`. |
-| `innerRef` | _(optional)_ by providing a React Ref we can get the internally used element (DOM). E.g. `inner_ref={myRef}` by using `React.createRef()` or `React.useRef()`. |
-| [Space](/uilib/layout/space/properties) | _(optional)_ spacing properties like `top` or `bottom` are supported. |
+
## Variants
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/textarea/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/components/textarea/Examples.tsx
index 0fe519ec67c..1411a3540a3 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/textarea/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/textarea/Examples.tsx
@@ -6,7 +6,7 @@
import React from 'react'
import ComponentBox from '../../../../shared/tags/ComponentBox'
import styled from '@emotion/styled'
-import { Textarea, HelpButton, Flex, Card } from '@dnb/eufemia/src'
+import { Textarea, HelpButton, Flex } from '@dnb/eufemia/src'
import { Field, Form } from '@dnb/eufemia/src/extensions/forms'
export const RowsCols = () => (
@@ -154,7 +154,7 @@ export const MaxLength = () => (
-
+
(
characterCounter={{ max: 3, variant: 'up' }}
/>
-
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/upload/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/components/upload/Examples.tsx
index 349c14ec012..ed3ce133a2d 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/components/upload/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/components/upload/Examples.tsx
@@ -12,7 +12,6 @@ import {
Section,
Upload,
} from '@dnb/eufemia/src'
-import { UploadValue } from '@dnb/eufemia/src/extensions/forms/Field/Upload'
export function createMockFile(name: string, size: number, type: string) {
const file = new File([], name, { type })
@@ -35,46 +34,6 @@ const useMockFiles = (setFiles, extend) => {
}, [])
}
-export async function mockAsyncFileUpload(
- newFiles: UploadValue,
-): Promise {
- const promises = newFiles.map(async (file, index) => {
- const formData = new FormData()
- formData.append('file', file.file, file.file.name)
-
- await new Promise((resolve) =>
- setTimeout(resolve, Math.floor(Math.random() * 2000) + 1000),
- )
-
- const mockResponse = {
- ok: (index + 2) % 2 === 0, // Every other request will fail
- json: async () => ({
- server_generated_id: `${file.file.name}_${crypto.randomUUID()}`,
- }),
- }
-
- return await Promise.resolve(mockResponse)
- .then((res) => {
- if (res.ok) return res.json()
- throw new Error('Unable to upload this file')
- })
- .then((data) => {
- return {
- ...file,
- id: data.server_generated_id,
- }
- })
- .catch((error) => {
- return {
- ...file,
- errorMessage: error.message,
- }
- })
- })
-
- return await Promise.all(promises)
-}
-
export const UploadPrefilledFileList = () => (
+ Default paragraph
+ Regular weight paragraph (same as default)
+ Medium weight paragraph
+
+ )
+}
+export function ParagraphSizeModifiers() {
+ return (
+
+ x-small paragraph
+ small paragraph
+ medium paragraph
+ basis paragraph (same as default)
+ large paragraph
+ x-large paragraph
+ xx-large paragraph
+
+ )
+}
+export function ParagraphAlignmentModifiers() {
+ return (
+
+ Right aligned paragraph
+ Center aligned paragraph
+ Left aligned paragraph
+
+ )
+}
+export function ParagraphFamilyModifiers() {
+ return (
+
+ Basis family paragraph (same as default)
+
+ Heading family paragraph (only different on some themes)
+
+ Monospace family paragraph
+
+ )
+}
+
+export function ParagraphLineHeightModifiers() {
+ return (
+
+ x-small line-height paragraph
+ small line-height paragraph
+ medium line-height paragraph
+
+ basis line-height paragraph (same as default)
+
+ large line-height paragraph
+ x-large line-height paragraph
+ xx-large line-height paragraph
+
+ )
+}
+export function ParagraphAdditionalModifiers() {
+ return (
+
+
+
Bold weight paragraph
+
Underline paragraph
+
Italic paragraph
+
+
+ )
+}
+
export function ParagraphDefault() {
return (
Strong paragraph (medium weight)
- {/* Italic paragraph */}
- {/* Underline paragraph */}
Numbers 0123456789
Code paragraph
@@ -172,22 +240,6 @@ export function ParagraphAdditional() {
)
}
-export function ParagraphModifiers() {
- return (
-
-
-
Default paragraph
-
Medium weight paragraph
-
Small paragraph
-
Small paragraph with medium weight
- {/* (Bold is currently not supported by DNB UX) */}
- {/*
Bold weight paragraph
*/}
- {/*
Small paragraph with bold weight
*/}
-
-
- )
-}
-
export function ParagraphRegressionTests() {
const PWrap = ({ customSize = null, ...props }) => {
const size = props.size || customSize
@@ -196,10 +248,10 @@ export function ParagraphRegressionTests() {
{size}
-
+
{size} - Weight medium
-
+
{size} - Weight bold
>
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/elements/paragraph/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/elements/paragraph/demos.mdx
index 1da55828471..b06fbe36dad 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/elements/paragraph/demos.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/elements/paragraph/demos.mdx
@@ -3,28 +3,75 @@ showTabs: true
---
import {
+ ParagraphWeightModifiers,
+ ParagraphSizeModifiers,
+ ParagraphAlignmentModifiers,
+ ParagraphFamilyModifiers,
+ ParagraphLineHeightModifiers,
+ ParagraphAdditionalModifiers,
ParagraphDefault,
ParagraphSmall,
ParagraphAdditional,
ParagraphRegressionTests,
- ParagraphModifiers,
} from 'Docs/uilib/elements/paragraph/Examples'
## Demos
### Paragraphs modifiers
-
+These are the standard available modifiers for paragraph typography:
-### Paragraphs `basis` sized
+- [Weight](#weight)
+- [Size](#size)
+- [Alignment](#alignment)
+- [Font family](#font-family)
+- [Line height](#line-height)
+
+As well as some [other modifiers](#other-modifiers).
+
+#### Weight
+
+
+
+#### Size
+
+Also automatically sets the matching line-height (`line` prop).
+
+
+
+#### Alignment
+
+
+
+#### Font family
+
+
+
+#### Line height
+
+Line-height will be set automatically based on the `size` props, but can also be set separately if needed.
+
+
+
+#### Other modifiers
+
+Although bold, italic and underline are not a standard part of the Eufemia design system for typography (in particular, "medium" should be used instead of "bold"), we still include them as an option for convenience. And there are also cases where an accessibility case can be made for their use.
+
+
+
+### Children tag styling
+
+Paragraph also adds some default styling to child typography HTML elements. Like `` or ``.
+
+#### Paragraphs `basis` sized
-#### Paragraph `small` sized
+##### Paragraph `small` sized
-#### Additional Paragraph formatting (not defined yet)
+##### Additional Paragraph formatting (not defined yet)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/elements/paragraph/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/elements/paragraph/info.mdx
index bb55617fbed..36c978ae877 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/elements/paragraph/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/elements/paragraph/info.mdx
@@ -10,24 +10,12 @@ import { P } from '@dnb/eufemia/elements'
## Description
-Paragraphs are a block-level elements, used to structure and format text contents.
+Paragraphs are block-level elements, used to structure and format text contents.
-## Paragraph class modifiers
+Paragraph has some default typography styling even without any props being set.
-Eufemia comes with several styles you can use on paragraphs and other HTML text elements:
-
-**Weights**
-
-- `.dnb-p` (Body text)
-- `.dnb-p--medium`
-
-**Sizes**
-
-- `.dnb-p--small`
-- `.dnb-p--x-small`
-
-**Variants**
+Read more [about Fonts in the Designer Guides](/quickguide-designer/fonts/).
-- `.dnb-p--lead`
+### Typography CSS classes
-Read more [about Fonts in the Designer Guides](/quickguide-designer/fonts/).
+Both Paragraph and the [Span](uilib/elements/span/) component have the same typography props that uses the [typography helper classes](uilib/typography/helper-classes/).
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/elements/span.mdx b/packages/dnb-design-system-portal/src/docs/uilib/elements/span.mdx
new file mode 100644
index 00000000000..96150b716e1
--- /dev/null
+++ b/packages/dnb-design-system-portal/src/docs/uilib/elements/span.mdx
@@ -0,0 +1,13 @@
+---
+title: 'Span'
+theme: 'sbanken'
+showTabs: true
+hideTabs:
+ - title: Events
+---
+
+import SpanInfo from 'Docs/uilib/elements/span/info'
+import SpanDemos from 'Docs/uilib/elements/span/demos'
+
+
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/elements/span/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/elements/span/Examples.tsx
new file mode 100644
index 00000000000..31d3b1e3f98
--- /dev/null
+++ b/packages/dnb-design-system-portal/src/docs/uilib/elements/span/Examples.tsx
@@ -0,0 +1,77 @@
+/**
+ * UI lib Component Example
+ *
+ */
+
+import React from 'react'
+import ComponentBox from '../../../../shared/tags/ComponentBox'
+import Anchor from '@dnb/eufemia/src/components/Anchor'
+import { Span, P, H4 } from '@dnb/eufemia/src/elements'
+
+export function SpanBasic() {
+ return (
+
+
+ Here is a paragraph with a x-small word
+ and some medium weight text in it.
+
+
+ Heading 4 with x-large word
+
+
+
+ Anchor with medium weight words
+
+
+ )
+}
+
+export function SpanModifiers() {
+ return (
+
+
+ Default span
+
+ Medium weight span
+
+ Basis size span
+
+
+ X-small span with medium weight
+
+
+
+ )
+}
+
+export function SpanRegressionTests() {
+ const SpanWrap = (props) => {
+ const size = props.size || 'default'
+ return (
+
+ {size}
+
+
+ {size} - Weight medium
+
+
+
+ {size} - Weight bold
+
+
+ )
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/elements/span/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/elements/span/demos.mdx
new file mode 100644
index 00000000000..b7c34c9b398
--- /dev/null
+++ b/packages/dnb-design-system-portal/src/docs/uilib/elements/span/demos.mdx
@@ -0,0 +1,25 @@
+---
+showTabs: true
+---
+
+import {
+ SpanBasic,
+ SpanRegressionTests,
+ SpanModifiers,
+} from 'Docs/uilib/elements/span/Examples'
+
+## Demos
+
+For more detailed examples of every prop, see the [Paragraph demos](uilib/elements/paragraph/#demos).
+
+### Basics
+
+
+
+### Span modifiers
+
+
+
+
+
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/elements/span/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/elements/span/info.mdx
new file mode 100644
index 00000000000..0532948b1dc
--- /dev/null
+++ b/packages/dnb-design-system-portal/src/docs/uilib/elements/span/info.mdx
@@ -0,0 +1,19 @@
+---
+showTabs: true
+---
+
+## Import
+
+```tsx
+import { Span } from '@dnb/eufemia/elements'
+```
+
+## Description
+
+Spans are inline-elements, used to define parts of text content.
+
+Span does not define any default styling, if no props are set, it will just be a regular inline `` element.
+
+### Typography CSS classes
+
+Both Span and the [Paragraph](uilib/elements/paragraph/) component have the same typography props that uses the [typography helper classes](uilib/typography/helper-classes/).
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/elements/span/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/elements/span/properties.mdx
new file mode 100644
index 00000000000..fba727d761b
--- /dev/null
+++ b/packages/dnb-design-system-portal/src/docs/uilib/elements/span/properties.mdx
@@ -0,0 +1,10 @@
+---
+showTabs: true
+---
+
+import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable'
+import { SpanProperties } from '@dnb/eufemia/src/elements/span/SpanDocs'
+
+## Properties
+
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/DataContext/Provider/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/DataContext/Provider/Examples.tsx
index dd9eaefe695..20e00c36cbc 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/DataContext/Provider/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/DataContext/Provider/Examples.tsx
@@ -6,7 +6,7 @@ import {
Value,
JSONSchema,
} from '@dnb/eufemia/src/extensions/forms'
-import { Card, Flex } from '@dnb/eufemia/src'
+import { Flex } from '@dnb/eufemia/src'
export const TestdataSchema: JSONSchema = {
type: 'object',
@@ -102,7 +102,7 @@ export const Default = () => {
sessionStorageId="provider-example-1"
>
-
+
{
-
+
@@ -192,7 +192,7 @@ export const ValidationWithJsonSchema = () => {
onSubmitRequest={() => console.log('onSubmitRequest')}
>
-
+
{
-
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Examples.tsx
index 6e8b3ff8249..7809aa84605 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Examples.tsx
@@ -1,6 +1,6 @@
import React from 'react'
import ComponentBox from '../../../../shared/tags/ComponentBox'
-import { Input, Slider, Card, Flex, NumberFormat } from '@dnb/eufemia/src'
+import { Input, Slider, Flex, NumberFormat } from '@dnb/eufemia/src'
import {
Form,
Field,
@@ -46,7 +46,7 @@ export const CreateBasicFieldComponent = () => {
const preparedProps = {
label: 'What is the secret of this field?',
fromInput,
- validator: (value) => {
+ onChangeValidator: (value) => {
if (value === 'secret') {
return new Error('Do not reveal the secret!')
}
@@ -110,7 +110,7 @@ export const GettingStarted = () => {
>
Bedrift
-
+
{
/>
-
+
@@ -229,7 +229,7 @@ export const BaseFieldComponents = () => {
Value,
}}
>
-
+
{
value={true}
onChange={(value) => console.log('onChange', value)}
/>
-
+
)
}
@@ -257,13 +257,13 @@ export const FeatureFields = () => {
Value,
}}
>
-
+
-
+
)
}
@@ -278,20 +278,20 @@ export const LayoutComponents = () => {
Profile
-
+
Name
-
+
-
+
More information
-
+
)
@@ -322,12 +322,12 @@ export const VisibilityBasedOnData = () => {
Profile
-
+
Name
-
+
{
/>
-
+
More information
-
+
@@ -374,7 +374,7 @@ export const UsingFormHandler = () => {
>
Profile
-
+
@@ -384,7 +384,7 @@ export const UsingFormHandler = () => {
-
+
)
@@ -413,13 +413,13 @@ export const Validation = () => {
>
Profile
-
+
-
+
)
@@ -454,7 +454,7 @@ export const UsingWizard = () => {
Profile
-
+
Name
{
label="Etternavn"
required
/>
-
+
@@ -475,13 +475,13 @@ export const UsingWizard = () => {
Profile
-
+
More information
-
+
@@ -489,7 +489,7 @@ export const UsingWizard = () => {
Profile
-
+
@@ -498,7 +498,7 @@ export const UsingWizard = () => {
-
+
@@ -550,13 +550,13 @@ export const UsingFormSection = () => {
},
}}
>
-
+
Your account
-
+
)
@@ -641,14 +641,14 @@ export const UsingIterate = () => {
Accounts
-
+
-
+
@@ -676,7 +676,7 @@ export const Transformers = () => {
return (
-
+
{
/>
-
+
)
}
@@ -710,7 +710,7 @@ export const QuickStart = () => {
onChange={console.log}
onSubmit={console.log}
>
-
+
{
required
/>
-
+
)
}
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/ButtonRow/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/ButtonRow/Examples.tsx
index eda154f5c04..7e82019924c 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/ButtonRow/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/ButtonRow/Examples.tsx
@@ -1,6 +1,6 @@
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
import { Form, Field, Wizard } from '@dnb/eufemia/src/extensions/forms'
-import { Button, Card } from '@dnb/eufemia/src'
+import { Button } from '@dnb/eufemia/src'
import { send as sendIcon } from '@dnb/eufemia/src/icons'
export const Default = () => {
@@ -18,13 +18,13 @@ export const WithLayout = () => {
return (
console.log('onSubmit', data)}>
-
+
Cancel
-
+
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Card.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Card.mdx
new file mode 100644
index 00000000000..c6b15764c1b
--- /dev/null
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Card.mdx
@@ -0,0 +1,26 @@
+---
+title: 'Card'
+description: '`Form.Card` is a wrapper for the Card component to make it easier to use inside a form.'
+order: 1
+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: Card
+ href: /uilib/extensions/forms/Form/Card/
+---
+
+import Info from './Card/info.mdx'
+import Demos from './Card/demos.mdx'
+
+
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Card/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Card/Examples.tsx
new file mode 100644
index 00000000000..cdef9b83088
--- /dev/null
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Card/Examples.tsx
@@ -0,0 +1,21 @@
+import ComponentBox from '../../../../../../shared/tags/ComponentBox'
+import { Flex, P } from '@dnb/eufemia/src'
+import { Form, Field } from '@dnb/eufemia/src/extensions/forms'
+
+export const BasicUsage = () => {
+ return (
+
+
+ Main heading
+
+
+
+
+ Nested card
+
+
+
+
+
+ )
+}
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Card/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Card/demos.mdx
new file mode 100644
index 00000000000..c02baef433b
--- /dev/null
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Card/demos.mdx
@@ -0,0 +1,10 @@
+---
+showTabs: true
+hideInMenu: true
+---
+
+import * as Examples from './Examples'
+
+## Demos
+
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Card/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Card/info.mdx
new file mode 100644
index 00000000000..85cd5152f81
--- /dev/null
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Card/info.mdx
@@ -0,0 +1,11 @@
+---
+showTabs: true
+hideInMenu: true
+---
+
+## Description
+
+`Form.Card` is a wrapper for the [Card](/uilib/components/card/) component to make it easier to use inside a form.
+
+- It will set `outset` to `true` by default.
+- It will set `stack` to `true` by default.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Card/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Card/properties.mdx
new file mode 100644
index 00000000000..0fe97c298d6
--- /dev/null
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Card/properties.mdx
@@ -0,0 +1,12 @@
+---
+showTabs: true
+hideInMenu: true
+---
+
+import TranslationsTable from 'dnb-design-system-portal/src/shared/parts/TranslationsTable'
+import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable'
+import { FormCardProperties } from '@dnb/eufemia/src/extensions/forms/Form/Card/CardDocs'
+
+## Properties
+
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/Examples.tsx
index bcec3cdca64..bd12e44ed13 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/Examples.tsx
@@ -7,7 +7,7 @@ import {
Tools,
} from '@dnb/eufemia/src/extensions/forms'
import { stop as stopIcon } from '@dnb/eufemia/src/icons'
-import { Button, Card, Flex, P } from '@dnb/eufemia/src'
+import { Button, Flex, P } from '@dnb/eufemia/src'
import { debounceAsync } from '@dnb/eufemia/src/shared/helpers/debounce'
import { createRequest } from '../SubmitIndicator/Examples'
@@ -15,7 +15,7 @@ export const RequiredAndOptionalFields = () => {
return (
-
+
{
/>
-
+
)
@@ -37,12 +37,12 @@ export const AsyncSubmit = () => {
console.log('onSubmit', data)}
>
-
+
-
+
)
@@ -73,9 +73,9 @@ export const AsyncSubmitComplete = () => {
>
Heading
-
+
-
+
@@ -157,7 +157,7 @@ export const AsyncChangeAndValidation = () => {
label='Type "valid" to validate the field'
path="/myField"
required
- validator={validator}
+ onChangeValidator={validator}
onChange={onChangeField}
autoComplete="off"
/>
@@ -196,13 +196,13 @@ export const SessionStorage = () => {
}}
sessionStorageId="session-key"
>
-
+
-
+
)
@@ -218,14 +218,14 @@ export const Autofill = () => {
Delivery address
-
+
Your name
-
+
-
+
Your address
@@ -247,14 +247,14 @@ export const Autofill = () => {
postalCode={{ required: true, path: '/postalCode' }}
city={{ required: true, path: '/city' }}
/>
-
+
-
+
More information about this form.
-
+
@@ -281,7 +281,7 @@ export const Locale = () => {
locale={data?.locale}
translations={myTranslations}
>
-
+
{
Norsk
English
-
+
)
}
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Isolation/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Isolation/Examples.tsx
index 9f5371c66e2..f4f0547bb9b 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Isolation/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Isolation/Examples.tsx
@@ -1,5 +1,5 @@
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
-import { Card, Flex, HeightAnimation } from '@dnb/eufemia/src'
+import { Flex, HeightAnimation } from '@dnb/eufemia/src'
import { Field, Form, Tools } from '@dnb/eufemia/src/extensions/forms'
import React from 'react'
@@ -53,7 +53,7 @@ export const CommitHandleRef = () => {
contactPersons: [{ title: 'Hanne', value: 'hanne' }],
}}
>
-
+
Ny hovedkontaktperson
@@ -89,7 +89,7 @@ export const CommitHandleRef = () => {
-
+
{
mySelection: 'hanne',
}}
>
-
+
Legg til ny hovedkontaktperson
@@ -177,7 +177,7 @@ export const TransformCommitData = () => {
-
+
)
}
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/MainHeading/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/MainHeading/Examples.tsx
index e906026a322..349167730d9 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/MainHeading/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/MainHeading/Examples.tsx
@@ -1,5 +1,5 @@
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
-import { Card, Flex, P } from '@dnb/eufemia/src'
+import { Flex, P } from '@dnb/eufemia/src'
import { Form } from '@dnb/eufemia/src/extensions/forms'
export const Default = () => {
@@ -25,9 +25,9 @@ export const AboveCard = () => {
return (
This is a main heading
-
+
Card contents
-
+
)
}
@@ -44,9 +44,9 @@ export const WithHelpButton = () => {
>
This is a main heading
-
+
Card contents
-
+
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/MainHeading/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/MainHeading/demos.mdx
index d193006f0e3..e55b65a168f 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/MainHeading/demos.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/MainHeading/demos.mdx
@@ -16,7 +16,7 @@ import * as Examples from './Examples'
### Above Card
-When placed above a [Card](/uilib/components/card/) component, the heading will be indented to align with the card content.
+When placed above a [Form.Card](/uilib/extensions/forms/Form/Card/) component, the heading will be indented to align with the card content.
On small screens, the indention will be removed.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/MainHeading/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/MainHeading/info.mdx
index 521a9d6cd00..df1038ce677 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/MainHeading/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/MainHeading/info.mdx
@@ -15,10 +15,10 @@ render(
Header
-
+
Header
-
+
,
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Section/EditContainer/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Section/EditContainer/Examples.tsx
index 841f4e88028..016d4954eb2 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Section/EditContainer/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Section/EditContainer/Examples.tsx
@@ -1,5 +1,4 @@
import ComponentBox from '../../../../../../../shared/tags/ComponentBox'
-import { Card } from '@dnb/eufemia/src'
import { Field, Form, Value } from '@dnb/eufemia/src/extensions/forms'
export const ViewAndEditContainer = () => {
@@ -35,7 +34,7 @@ export const ViewAndEditContainer = () => {
},
}}
>
-
+
Your account
{
-
+
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Section/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Section/Examples.tsx
index a8f08f769b9..fcc7927b59d 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Section/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Section/Examples.tsx
@@ -1,5 +1,5 @@
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
-import { Card, Flex, P } from '@dnb/eufemia/src'
+import { Flex, P } from '@dnb/eufemia/src'
import {
Field,
Form,
@@ -26,10 +26,10 @@ export const NestedPathSection = () => {
const MyNameSection = (props: SectionProps) => {
return (
-
+
-
+
)
}
@@ -87,13 +87,13 @@ export const ViewAndEditContainer = () => {
},
}}
>
-
+
Your account
-
+
)
@@ -136,13 +136,13 @@ export const ViewAndEditContainerValidation = () => {
},
}}
>
-
+
Your account
-
+
)
@@ -188,13 +188,13 @@ export const BasicViewAndEditContainer = () => {
},
}}
>
-
+
Your account
-
+
)
@@ -210,7 +210,7 @@ export const OverwriteProps = () => {
const MyNameSection = (props) => {
return (
-
+
{
minLength={10}
/>
-
+
)
}
@@ -262,12 +262,12 @@ export const AllFieldsRequired = () => {
const MyNameSection = (props: SectionProps) => {
return (
-
+
-
+
)
}
@@ -307,7 +307,7 @@ export const SchemaSupport = () => {
const MyNameSection = (props: SectionProps) => {
return (
-
+
{
minLength={10}
/>
-
+
)
}
@@ -368,7 +368,7 @@ export const WithVisibility = () => {
const MySection = ({ children, ...props }) => {
return (
-
+
{
{children}
-
+
@@ -463,11 +463,11 @@ export const NestedSections = () => {
function MySection(props: SectionProps) {
return (
-
+
-
+
)
}
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/SubHeading/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/SubHeading/Examples.tsx
index a4a1d409121..79d46e9ac46 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/SubHeading/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/SubHeading/Examples.tsx
@@ -1,5 +1,5 @@
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
-import { Card, Flex, P } from '@dnb/eufemia/src'
+import { Flex, P } from '@dnb/eufemia/src'
import { Form } from '@dnb/eufemia/src/extensions/forms'
export const TextOnly = () => {
@@ -33,12 +33,12 @@ export const PrecedingFlexContainer = () => {
export const InsideCard = () => {
return (
-
+
This is a sub heading
Card contents
-
+
)
}
@@ -47,9 +47,9 @@ export const AboveCard = () => {
return (
This is a sub heading
-
+
Card contents
-
+
)
}
@@ -76,9 +76,9 @@ export const WithHelpButton = () => {
>
This is a sub heading
-
+
Card contents
-
+
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/SubHeading/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/SubHeading/demos.mdx
index 2d02eed90d1..5e4402acbae 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/SubHeading/demos.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/SubHeading/demos.mdx
@@ -24,7 +24,7 @@ import * as Examples from './Examples'
### Above Card
-When placed above a [Card](/uilib/components/card/) component, the heading will be indented to align with the card content.
+When placed above a [Form.Card](/uilib/extensions/forms/Form/Card/) component, the heading will be indented to align with the card content.
On small screens, the indention will be removed.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/SubHeading/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/SubHeading/info.mdx
index 632adab563d..15dc5da44a4 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/SubHeading/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/SubHeading/info.mdx
@@ -15,10 +15,10 @@ render(
Header
-
+
Header
-
+
,
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/SubmitIndicator/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/SubmitIndicator/Examples.tsx
index a124f1ad4c3..914d6ef2cdf 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/SubmitIndicator/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/SubmitIndicator/Examples.tsx
@@ -1,4 +1,4 @@
-import { Button, Card, Flex, FormLabel } from '@dnb/eufemia/src'
+import { Button, Flex, FormLabel } from '@dnb/eufemia/src'
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
import { Field, FieldBlock, Form } from '@dnb/eufemia/src/extensions/forms'
import { debounceAsync } from '@dnb/eufemia/src/shared/helpers/debounce'
@@ -26,13 +26,13 @@ export const AsyncSubmitBehavior = () => {
return (
-
+
Cancel
-
+
)
}}
@@ -55,12 +55,12 @@ export const AsyncChangeBehavior = () => {
return (
-
+
{
Cancel
-
+
)
}}
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Visibility/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Visibility/Examples.tsx
index 480d8b08450..90e05ecb254 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Visibility/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Visibility/Examples.tsx
@@ -1,6 +1,6 @@
import React from 'react'
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
-import { Card, Flex, HeightAnimation, P } from '@dnb/eufemia/src'
+import { Flex, HeightAnimation, P } from '@dnb/eufemia/src'
import {
Field,
Form,
@@ -239,11 +239,11 @@ export const FilterData = () => {
filterData={filterDataPaths}
animate
>
-
+
Result:
-
+
@@ -268,7 +268,7 @@ export function InheritVisibility() {
return (
-
+
-
+
)
@@ -298,7 +298,7 @@ export function VisibilityOnValidation() {
return (
-
+
-
+
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/schema-validation/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/schema-validation/Examples.tsx
index b22ae543cf1..e0824a922da 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/schema-validation/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/schema-validation/Examples.tsx
@@ -1,5 +1,5 @@
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
-import { Card, Flex } from '@dnb/eufemia/src'
+import { Flex } from '@dnb/eufemia/src'
import { Form, Field, Iterate } from '@dnb/eufemia/src/extensions/forms'
import { trash as TrashIcon } from '@dnb/eufemia/src/icons'
@@ -26,12 +26,12 @@ export const DataSetSchema = () => {
required: ['name', 'address'],
}}
>
-
+
Company information
-
+
@@ -62,7 +62,7 @@ export const IfRuleSchema = () => {
else: { required: ['name'] },
}}
>
-
+
Customer information
@@ -74,7 +74,7 @@ export const IfRuleSchema = () => {
path="/companyName"
labelDescription="Company name (required for corporate customers)"
/>
-
+
@@ -149,14 +149,14 @@ export const DependantListSchema = () => {
>
Customer information
-
+
-
+
Accounts
-
+
Standard accounts
@@ -189,7 +189,7 @@ export const DependantListSchema = () => {
label="Account number"
/>
-
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useData/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useData/info.mdx
index eba8a84dc16..34c96df04a6 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useData/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useData/info.mdx
@@ -265,6 +265,6 @@ function ComponentB() {
**tl;dr:** the `useData` hook returns unvalidated data.
-When you use an async `onChange` or `validator` event handler on a field, it will delay the "submitted" value, because of its async nature.
+When you use an async `onChange`, `onChangeValidator` or `onBlurValidator` event handler on a field, it will delay the "submitted" value, because of its async nature.
That means, if you want to access the value of a field immediately, you can use the `useData` hook for that, as it always returns unvalidated data, in sync.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useSnapshot/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useSnapshot/Examples.tsx
index 7162b4c0f6a..c764ab8db4e 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useSnapshot/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useSnapshot/Examples.tsx
@@ -1,5 +1,5 @@
import React from 'react'
-import { Button, Card } from '@dnb/eufemia/src'
+import { Button } from '@dnb/eufemia/src'
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
import {
Field,
@@ -81,7 +81,7 @@ export const UndoRedo = () => {
return (
<>
-
+
{
/>
-
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/AnimatedContainer/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/AnimatedContainer/Examples.tsx
index 59a45d025ca..73bfba9c940 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/AnimatedContainer/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/AnimatedContainer/Examples.tsx
@@ -1,5 +1,4 @@
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
-import { Card } from '@dnb/eufemia/src'
import { Field, Form, Iterate } from '@dnb/eufemia/src/extensions/forms'
export const Default = () => {
@@ -15,7 +14,7 @@ export const Default = () => {
}}
id="myForm"
>
-
+
Empty list>}
@@ -34,7 +33,7 @@ export const Default = () => {
pushValue={'Item ' + String(count('/myList') + 1)}
text="Add new item"
/>
-
+
)
}
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Array/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Array/Examples.tsx
index f1b0908c017..1c947014598 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Array/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Array/Examples.tsx
@@ -1,5 +1,5 @@
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
-import { Card, Flex, Table, Td, Th, Tr } from '@dnb/eufemia/src'
+import { Flex, Table, Td, Th, Tr } from '@dnb/eufemia/src'
import {
Iterate,
Field,
@@ -179,7 +179,7 @@ export const ArrayFromFormHandler = () => {
Avengers
-
+
console.log('Iterate/onChange', value)}
@@ -215,7 +215,7 @@ export const ArrayFromFormHandler = () => {
path="/avengers"
pushValue={{}}
/>
-
+
@@ -298,14 +298,14 @@ export const ViewAndEditContainer = () => {
Accounts
-
+
-
+
@@ -375,7 +375,7 @@ export const InitiallyOpen = () => {
-
+
@@ -395,7 +395,7 @@ export const InitiallyOpen = () => {
variant="tertiary"
pushValue={{}}
/>
-
+
@@ -439,11 +439,11 @@ export const InitialOpenWithToolbarVariant = () => {
Statsborgerskap
-
+
{
+ onChangeValidator={(arrayValue) => {
const findFirstDuplication = (arr) =>
arr.findIndex((e, i) => arr.indexOf(e) !== i)
@@ -479,7 +479,7 @@ export const InitialOpenWithToolbarVariant = () => {
pushValue={null}
text="Legg til flere statsborgerskap"
/>
-
+
@@ -532,10 +532,10 @@ export const WithArrayValidator = () => {
defaultData={{ items: ['foo'] }}
onSubmit={async () => console.log('onSubmit')}
>
-
+
{
+ onChangeValidator={(arrayValue) => {
if (!(arrayValue && arrayValue.length > 1)) {
return new Error('You need at least two items')
}
@@ -560,7 +560,7 @@ export const WithArrayValidator = () => {
text="Add"
/>
-
+
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/PushContainer/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/PushContainer/Examples.tsx
index 1672f72837b..6485fe45460 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/PushContainer/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/PushContainer/Examples.tsx
@@ -6,7 +6,7 @@ import {
Value,
} from '@dnb/eufemia/src/extensions/forms'
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
-import { Card, Flex } from '@dnb/eufemia/src'
+import { Flex } from '@dnb/eufemia/src'
import React from 'react'
export { ViewAndEditContainer } from '../Array/Examples'
@@ -79,14 +79,14 @@ export const InitiallyOpen = () => {
Accounts
-
+
-
+
@@ -244,18 +244,18 @@ export const IsolatedData = () => {
Representatives
-
+
-
+
-
+
Data Context
-
+
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/Composition/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/Composition/Examples.tsx
index 4f4a3a26812..5ab52ab1fb7 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/Composition/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/Composition/Examples.tsx
@@ -1,4 +1,3 @@
-import { Card } from '@dnb/eufemia/src'
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
import { Form, Value } from '@dnb/eufemia/src/extensions/forms'
@@ -57,7 +56,7 @@ export const WithSummaryList = () => {
city: 'Oslo',
}}
>
-
+
Subheading
@@ -76,7 +75,7 @@ export const WithSummaryList = () => {
-
+
)
@@ -95,7 +94,7 @@ export const WithSummaryListGridLayout = () => {
city: 'Oslo',
}}
>
-
+
Subheading
@@ -112,7 +111,7 @@ export const WithSummaryListGridLayout = () => {
-
+
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/Provider/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/Provider/Examples.tsx
index da661b658e6..fb4fdf6be97 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/Provider/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/Provider/Examples.tsx
@@ -1,13 +1,12 @@
import React from 'react'
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
-import { Card } from '@dnb/eufemia/src'
import { Field, Form, Value } from '@dnb/eufemia/src/extensions/forms'
export function InheritVisibility() {
return (
-
+
-
+
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/SummaryList/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/SummaryList/Examples.tsx
index 93f658babe3..bb2176e7b92 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/SummaryList/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/SummaryList/Examples.tsx
@@ -1,5 +1,4 @@
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
-import { Card } from '@dnb/eufemia/src'
import { Field, Form, Value } from '@dnb/eufemia/src/extensions/forms'
export const DefaultLayout = () => {
@@ -11,14 +10,14 @@ export const DefaultLayout = () => {
lastName: 'Doe',
}}
>
-
+
Subheading
-
+
)
@@ -33,14 +32,14 @@ export const GridLayout = () => {
lastName: 'Doe',
}}
>
-
+
Subheading
-
+
)
@@ -55,14 +54,14 @@ export const HorizontalLayout = () => {
lastName: 'Doe',
}}
>
-
+
Subheading
-
+
)
@@ -81,7 +80,7 @@ export const CombinedLayout = () => {
city: 'Oslo',
}}
>
-
+
Subheading
@@ -98,7 +97,7 @@ export const CombinedLayout = () => {
-
+
)
@@ -108,7 +107,7 @@ export function InheritVisibility() {
return (
-
+
-
+
)
@@ -134,7 +133,7 @@ export function InheritLabel() {
return (
-
+
@@ -142,8 +141,18 @@ export function InheritLabel() {
-
+
)
}
+
+export const HorizontalLayoutWithoutLabel = () => (
+
+
+
+
+
+
+
+)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/SummaryList/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/SummaryList/demos.mdx
index b1c891bb857..7b8b89f87f8 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/SummaryList/demos.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/SummaryList/demos.mdx
@@ -31,3 +31,7 @@ Using [Value.Composition](/uilib/extensions/forms/Value/Composition/) to combine
### Inherit label
+
+
+
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/SummaryList/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/SummaryList/properties.mdx
index d4398df79c6..86204dfa623 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/SummaryList/properties.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/SummaryList/properties.mdx
@@ -8,4 +8,7 @@ import { SummaryListProperties } from '@dnb/eufemia/src/extensions/forms/Value/S
## Properties
-
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Buttons/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Buttons/Examples.tsx
index a385a2be20c..cfbe98bb5ee 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Buttons/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Buttons/Examples.tsx
@@ -1,4 +1,4 @@
-import { Card, P } from '@dnb/eufemia/src'
+import { P } from '@dnb/eufemia/src'
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
import { Form, Wizard } from '@dnb/eufemia/src/extensions/forms'
@@ -9,9 +9,9 @@ export const Default = () => {
const Step = ({ title }) => {
return (
-
+
Contents of {title}
-
+
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Container/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Container/Examples.tsx
index 728efbe86fd..a9c53d3479b 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Container/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Container/Examples.tsx
@@ -8,7 +8,7 @@ import {
Value,
Wizard,
} from '@dnb/eufemia/src/extensions/forms'
-import { Card, P } from '@dnb/eufemia/src'
+import { P } from '@dnb/eufemia/src'
export const Default = () => {
return (
@@ -26,12 +26,12 @@ export const Default = () => {
const Step1 = () => (
Heading
-
+
Contents
-
-
+
+
Contents
-
+
@@ -40,12 +40,12 @@ export const Default = () => {
const Step2 = () => (
Heading
-
+
Contents
-
-
+
+
Contents
-
+
@@ -57,7 +57,7 @@ export const Default = () => {
return (
Summary
-
+
Deliver address
@@ -76,7 +76,7 @@ export const Default = () => {
-
+
@@ -175,19 +175,19 @@ export const AsyncWizardContainer = () => {
const Step1 = () => {
return (
-
+
-
+
@@ -199,9 +199,9 @@ export const AsyncWizardContainer = () => {
Heading
-
+
Contents of step 2
-
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Container/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Container/info.mdx
index ba21ad10d31..89729d6ded8 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Container/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Container/info.mdx
@@ -31,12 +31,12 @@ import { Form, Wizard } from '@dnb/eufemia/extensions/forms'
const Step1 = () => (
Heading
-
+
Contents
-
-
+
+
Contents
-
+
@@ -60,12 +60,12 @@ import { Form, Wizard } from '@dnb/eufemia/extensions/forms'
const Step2 = () => (
-
+
Contents
-
-
+
+
Contents
-
+
@@ -98,9 +98,9 @@ import { Form, Wizard } from '@dnb/eufemia/extensions/forms'
const MyStep = () => {
const { setActiveIndex, activeIndex } = Wizard.useStep()
return (
-
+
setActiveIndex(1)}>Go to step 2
-
+
)
}
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/EditButton/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/EditButton/info.mdx
index efafdf5dc4c..fcef0a4dfd7 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/EditButton/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/EditButton/info.mdx
@@ -16,11 +16,11 @@ render(
-
+
-
+
,
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Examples.tsx
index 01d092bf1b0..1b78b9c38d5 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Examples.tsx
@@ -1,7 +1,7 @@
import React from 'react'
import ComponentBox from '../../../../../shared/tags/ComponentBox'
import { Form, Wizard } from '@dnb/eufemia/src/extensions/forms'
-import { Card, P } from '@dnb/eufemia/src'
+import { P } from '@dnb/eufemia/src'
export const IntroExample = () => {
return (
@@ -20,16 +20,16 @@ export const IntroExample = () => {
>
Heading
-
+
Step 1
-
+
Heading
-
+
Step 2
-
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Step/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Step/Examples.tsx
index 1ae62f30fc2..0b3521bb535 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Step/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Step/Examples.tsx
@@ -1,4 +1,4 @@
-import { Card, P } from '@dnb/eufemia/src'
+import { P } from '@dnb/eufemia/src'
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
import {
Field,
@@ -78,9 +78,9 @@ export const EditButton = () => {
const Step = ({ title }) => {
return (
-
+
Contents
-
+
@@ -92,13 +92,13 @@ export const EditButton = () => {
return (
-
+
-
+
)
}
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Step/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Step/info.mdx
index 915005f7a1b..5ecd086fd4e 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Step/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/Step/info.mdx
@@ -13,12 +13,12 @@ const Step1 = () => {
return (
Heading
-
+
Contents
-
-
+
+
Contents
-
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/location-hooks/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/location-hooks/Examples.tsx
index 3a9a3b24027..f5f50849359 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/location-hooks/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Wizard/location-hooks/Examples.tsx
@@ -1,6 +1,6 @@
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
import { navigate, useLocation } from '@reach/router'
-import { Card, P } from '@dnb/eufemia/src'
+import { P } from '@dnb/eufemia/src'
import { Form, Wizard } from '@dnb/eufemia/src/extensions/forms'
export const Default = () => {
@@ -24,9 +24,9 @@ export const Default = () => {
const MyStep = ({ title }) => {
return (
-
+
Contents of {title}
-
+
)
@@ -62,9 +62,9 @@ export const ReachRouter = () => {
const MyStep = ({ title }) => {
return (
-
+
Contents of {title}
-
+
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/ArraySelection/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/ArraySelection/Examples.tsx
index 31235d5a664..9f9e815f5c1 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/ArraySelection/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/ArraySelection/Examples.tsx
@@ -1,4 +1,4 @@
-import { Card, Section } from '@dnb/eufemia/src'
+import { Flex, Section } from '@dnb/eufemia/src'
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
import { Field, Form } from '@dnb/eufemia/src/extensions/forms'
@@ -112,6 +112,21 @@ export const CheckboxDisabled = () => (
)
+export const CheckboxDisabledOptions = () => (
+
+ console.log('onChange', value)}
+ >
+
+
+
+
+
+
+)
+
export const CheckboxInfo = () => (
(
data-visual-test="array-selection-checkbox-nesting-logic"
>
-
-
-
-
-
- {
- return Array.isArray(value)
- ? value.includes('showInput')
- : false
- },
- }}
- animate
- compensateForGap="auto" // makes animation smooth
+
+
+
-
-
+
-
- {
- return Array.isArray(value)
- ? value.includes('showAdditionalOption')
- : false
- },
- }}
- animate
- compensateForGap="auto" // makes animation smooth
- >
-
+
{
return Array.isArray(value)
- ? value.includes('showMeMore')
+ ? value.includes('showInput')
: false
},
}}
+ animate
+ compensateForGap="auto" // makes animation smooth
>
-
-
-
-
+
+ {
+ return Array.isArray(value)
+ ? value.includes('showAdditionalOption')
+ : false
+ },
+ }}
+ animate
+ compensateForGap="auto" // makes animation smooth
+ >
+
+ {
+ return Array.isArray(value)
+ ? value.includes('showMeMore')
+ : false
+ },
+ }}
+ >
+
+
+
+
+
+
+
+
)
@@ -431,6 +448,22 @@ export const ButtonDisabled = () => (
)
+export const ButtonDisabledOptions = () => (
+
+ console.log('onChange', value)}
+ >
+
+
+
+
+
+
+)
+
export const ButtonInfo = () => (
(
export const ButtonNestingWithLogic = () => (
-
+
(
-
+
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/ArraySelection/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/ArraySelection/demos.mdx
index 77d5c266a57..a2fbfe60259 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/ArraySelection/demos.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/ArraySelection/demos.mdx
@@ -50,6 +50,10 @@ import * as Examples from './Examples'
+#### Checkbox disabled options
+
+
+
#### Checkbox info
@@ -108,6 +112,10 @@ You can nest other fields and show them based on your desired logic.
+#### Button disabled options
+
+
+
#### Button info
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/ArraySelection/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/ArraySelection/info.mdx
index acdfe6e543d..85cf5d7e925 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/ArraySelection/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/ArraySelection/info.mdx
@@ -8,7 +8,7 @@ showTabs: true
There is a corresponding [Value.ArraySelection](/uilib/extensions/forms/Value/ArraySelection) component.
-The [Field.Option](/uilib/extensions/forms/base-fields/Option/) is a related component.
+Uses the [Field.Option](/uilib/extensions/forms/base-fields/Option/) pseudo-component to define options.
```jsx
import { Field } from '@dnb/eufemia/extensions/forms'
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Indeterminate.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Indeterminate.mdx
index 3fb815e6d6c..28ffa8dedc7 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Indeterminate.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Indeterminate.mdx
@@ -1,7 +1,7 @@
---
title: 'Indeterminate'
description: 'The `Field.Indeterminate` component is used to display and handle the indeterminate state of a checkbox.'
-componentType: 'primitive'
+componentType: 'base-toggle'
hideInMenu: true
showTabs: true
theme: 'sbanken'
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Indeterminate/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Indeterminate/Examples.tsx
index 97972da09e8..e279565538c 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Indeterminate/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Indeterminate/Examples.tsx
@@ -1,4 +1,4 @@
-import { Card, Flex } from '@dnb/eufemia/src'
+import { Flex } from '@dnb/eufemia/src'
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
import { Field, Form } from '@dnb/eufemia/src/extensions/forms'
@@ -6,7 +6,7 @@ export const MixedIndeterminateDependence = () => {
return (
-
+
{
valueOn="on"
valueOff="off"
/>
-
+
@@ -44,7 +44,7 @@ export const PropagateIndeterminateDependence = () => {
const { data } = Form.useData()
return (
<>
-
+
Checked
Unchecked
@@ -72,7 +72,7 @@ export const PropagateIndeterminateDependence = () => {
valueOn="on"
valueOff="off"
/>
-
+
>
)
}
@@ -104,7 +104,7 @@ export const NestedIndeterminateDependence = () => {
return (
-
+
{
/>
-
+
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Number/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Number/Examples.tsx
index 116d955d18f..f52023fe382 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Number/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Number/Examples.tsx
@@ -1,5 +1,5 @@
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
-import { Slider, Grid, Flex, Card } from '@dnb/eufemia/src'
+import { Slider, Grid, Flex } from '@dnb/eufemia/src'
import { Field, Form } from '@dnb/eufemia/src/extensions/forms'
import React from 'react'
@@ -30,7 +30,7 @@ export const LabelAndValue = () => {
export const LabelAndDescription = () => {
return (
-
+
{
labelDescription="\nDescription text with new line using \\n"
placeholder="Enter a text..."
/>
-
+
)
}
@@ -49,7 +49,7 @@ export const LabelAndDescription = () => {
export const WithStatus = () => {
return (
-
+
{
warning="Aliqua eu aute id qui esse aliqua dolor in aute magna commodo anim enim et. Velit incididunt exercitation est magna ex irure dolore nisi eiusmod ea exercitation."
required
/>
-
+
)
}
@@ -79,7 +79,7 @@ export const WithStatus = () => {
export const HorizontalLayout = () => {
return (
-
+
{
width="stretch"
/>
-
+
)
}
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Option.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Option.mdx
index e84d4456511..ee9434ae05f 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Option.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Option.mdx
@@ -1,6 +1,6 @@
---
title: 'Option'
-description: '`Field.Option` is a wrapper component for selecting an option to be used in a dropdown or similar user experiences.'
+description: '`Field.Option` is a pseudo-component for defining an option to be used in a dropdown or similar user experiences.'
componentType: 'base-selection'
hideInMenu: true
showTabs: true
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Option/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Option/info.mdx
index c0f5c0b6dd6..c0f94174e84 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Option/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Option/info.mdx
@@ -4,7 +4,12 @@ showTabs: true
## Description
-`Field.Option` is a part for building selection inputs with Field.Select.
+`Field.Option` is a pseudo-component that is used by parent components to configure options. It has no use on it's own. How it renders, and what extra props it accepts, are defined by the parent.
+
+### Used in:
+
+- [Field.ArraySelection](/uilib/extensions/forms/base-fields/ArraySelection/)
+- [Field.Selection](/uilib/extensions/forms/base-fields/Selection/)
```tsx
import { Field } from '@dnb/eufemia/extensions/forms'
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Option/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Option/properties.mdx
index f32b1919b91..db9c2bd863a 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Option/properties.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Option/properties.mdx
@@ -7,4 +7,6 @@ import { OptionProperties } from '@dnb/eufemia/src/extensions/forms/Field/Option
## Properties
+There might be more props available depending on the parent component. For example, `` also allows the `icon` prop, among others, for its options.
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Selection/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Selection/Examples.tsx
index 9f3614c0e20..ea22882c274 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Selection/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Selection/Examples.tsx
@@ -1,6 +1,6 @@
import * as React from 'react'
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
-import { Button, Card, Flex, Section } from '@dnb/eufemia/src'
+import { Button, Flex, Section } from '@dnb/eufemia/src'
import { Field, Form } from '@dnb/eufemia/src/extensions/forms'
// - Dropdown
@@ -149,6 +149,23 @@ export const DropdownDisabled = () => (
)
+export const DropdownDisabledOptions = () => (
+
+ {() => {
+ const Example = () => {
+ return (
+
+
+
+
+ )
+ }
+
+ return
+ }}
+
+)
+
export const DropdownError = () => (
(
)
+export const RadioDisabledOptions = () => (
+
+ {() => {
+ const Example = () => {
+ return (
+
+
+
+
+ )
+ }
+
+ return
+ }}
+
+)
+
export const RadioError = () => (
(
export const RadioNestingWithLogic = () => (
-
+
(
-
+
@@ -550,7 +584,7 @@ export const RadioNestingAdvanced = () => (
defaultData={{ mySelection: 'first', firstSelection: 'first' }}
onSubmit={console.log}
>
-
+
(
animate
compensateForGap="auto" // makes animation smooth
>
-
+
(
animate
compensateForGap="auto" // makes animation smooth
>
-
+
-
+
-
+
-
+
@@ -689,6 +723,23 @@ export const ButtonDisabled = () => (
)
+export const ButtonDisabledOptions = () => (
+
+ {() => {
+ const Example = () => {
+ return (
+
+
+
+
+ )
+ }
+
+ return
+ }}
+
+)
+
export const ButtonError = () => (
(
export const ButtonNestingWithLogic = () => (
-
+
(
-
+
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Selection/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Selection/demos.mdx
index 3bc5bff81ce..8b7dafe76d2 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Selection/demos.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Selection/demos.mdx
@@ -58,6 +58,10 @@ As there are many variants, they are split into separate sections. Here is a sum
+#### Dropdown option disabled
+
+
+
#### Dropdown error
@@ -120,6 +124,10 @@ As there are many variants, they are split into separate sections. Here is a sum
+#### Radio option disabled
+
+
+
#### Radio error
@@ -162,6 +170,10 @@ You can nest other fields and show them based on your desired logic.
+#### ToggleButton option disabled
+
+
+
#### ToggleButton error
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Selection/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Selection/info.mdx
index 67d12b390b8..a73a716a43c 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Selection/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Selection/info.mdx
@@ -8,7 +8,7 @@ showTabs: true
There is a corresponding [Value.Selection](/uilib/extensions/forms/Value/Selection) component.
-The [Field.Option](/uilib/extensions/forms/base-fields/Option/) is a related component.
+Uses the [Field.Option](/uilib/extensions/forms/base-fields/Option/) pseudo-component to define options.
```tsx
import { Field } from '@dnb/eufemia/extensions/forms'
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/String/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/String/Examples.tsx
index 44270be7695..a0859b76e88 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/String/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/String/Examples.tsx
@@ -1,5 +1,5 @@
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
-import { Card, Flex } from '@dnb/eufemia/src'
+import { Flex } from '@dnb/eufemia/src'
import {
Field,
Form,
@@ -34,7 +34,7 @@ export const LabelAndValue = () => {
export const LabelAndDescription = () => {
return (
-
+
{
labelDescription="\nDescription text with new line using \\n"
placeholder="Enter a text..."
/>
-
+
)
}
@@ -53,7 +53,7 @@ export const LabelAndDescription = () => {
export const WithStatus = () => {
return (
-
+
{
warning={['Warning message A', 'Warning message B']}
info={['Info message A', 'Info message B']}
/>
-
+
)
}
@@ -89,7 +89,7 @@ export const WithStatus = () => {
export const HorizontalLayout = () => {
return (
-
+
{
width="stretch"
/>
-
+
)
}
@@ -204,7 +204,7 @@ export const Widths = () => {
export const Icons = () => {
return (
-
+
{
rightIcon="loupe"
onChange={(value) => console.log('onChange', value)}
/>
-
+
)
}
@@ -316,13 +316,13 @@ export const ValidatePattern = () => {
)
}
-export const SynchronousExternalValidator = () => {
+export const SynchronousExternalChangeValidator = () => {
return (
+ onChangeValidator={(value) =>
value.length < 4 ? Error('At least 4 characters') : undefined
}
onChange={(value) => console.log('onChange', value)}
@@ -331,13 +331,13 @@ export const SynchronousExternalValidator = () => {
)
}
-export const AsynchronousExternalValidator = () => {
+export const AsynchronousExternalChangeValidator = () => {
return (
+ onChangeValidator={(value) =>
new Promise((resolve) =>
setTimeout(
() =>
@@ -476,7 +476,7 @@ export function TransformInAndOut() {
const MyForm = () => {
return (
-
+
Data Context
-
+
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/String/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/String/demos.mdx
index 8694985c37e..5497f8c66b1 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/String/demos.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/String/demos.mdx
@@ -74,11 +74,11 @@ This example demonstrates how the status message width adjusts according to the
### Synchronous external validator (called on every change)
-
+
### Asynchronous external validator (called on every change)
-
+
### Synchronous external validator (called on blur)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks.mdx
index 63125958ee7..0b61f2b4dc7 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks.mdx
@@ -1,7 +1,6 @@
---
title: 'Blocks'
order: 20
-status: 'beta'
breadcrumb:
- text: Forms
href: /uilib/extensions/forms/
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/info.mdx
index 85576ddfea6..aa2561e40b0 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/info.mdx
@@ -13,8 +13,6 @@ Blocks are a collection of reusable fields and values. They can also be 100% cus
Read about how to create a section (block) by using a [Form.Section](/uilib/extensions/forms/Form/Section/).
-**Note:** If you are interested in using blocks, please get in touch with us.
-
## Usage
You import a block like this:
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 8deb3b20aac..7e88677912b 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
@@ -13,6 +13,19 @@ breadcrumb:
Change log for the Eufemia Forms extension.
+## v10.57
+
+- Added possibility for disabling individual options in [Field.Selection](/uilib/extensions/forms/base-fields/Selection/) and [Field.ArraySelection](/uilib/extensions/forms/base-fields/ArraySelection/).
+- Added `labelSrOnly` to Value.\* components, to be able to provide a label that is not visible.
+- Added [Form.Card](/uilib/extensions/forms/Form/Card/) component to make it easier to use [Card](/uilib/components/card/) inside a form.
+- Added `outset` property to [Form.Card](/uilib/extensions/forms/Form/Card/) and [Card](/uilib/components/card/).
+- Deprecated `validator` property in favor of `onChangeValidator` in Field.\* components.
+- Renamed `asyncFileHandler` to `fileHandler` in [Field.Upload](/uilib/extensions/forms/feature-fields/more-fields/Upload/), to support both async and sync file handling.
+- Fixed displaying indicator with async `onBlurValidator` call when `validateInitially` is used.
+- Fixed sharing submit indicator for fields inside [Field.Composition](/uilib/extensions/forms/base-fields/Composition/).
+- Fixed so `errorMessages` won't result in infinite loops when not wrapped in `useMemo`.
+- Fixed alignment issue in [Value.SummaryList](/uilib/extensions/forms/Value/SummaryList/), when providing a field without label.
+
## v10.56
- Added inline help button (`help`) to all `Field.*` components as default (with option to open in Dialog).
@@ -57,7 +70,7 @@ Change log for the Eufemia Forms extension.
## v10.53
-- Added validation of Norwegian bankaccount numbers to [Field.BankAccountNumber](/uilib/extensions/forms/feature-fields/BankAccountNumber/).
+- Added validation of Norwegian bank account numbers to [Field.BankAccountNumber](/uilib/extensions/forms/feature-fields/BankAccountNumber/).
- Added [Form.useTranslation](/uilib/extensions/forms/Form/useTranslation/) that returns the translations for the current locale.
- Added `renderMessage` function in [Form.useTranslation](/uilib/extensions/forms/Form/useTranslation/) to render a string with line-breaks.
- Added console warning when a field path is declared more than one time.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/create-component/FieldBlock/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/create-component/FieldBlock/Examples.tsx
index 8e88a94beef..bdc7c8c9143 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/create-component/FieldBlock/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/create-component/FieldBlock/Examples.tsx
@@ -5,7 +5,7 @@ import {
TestElement,
Form,
} from '@dnb/eufemia/src/extensions/forms'
-import { Anchor, Card, Flex, Slider } from '@dnb/eufemia/src'
+import { Anchor, Flex, Slider } from '@dnb/eufemia/src'
export const Default = () => {
return (
@@ -325,7 +325,7 @@ export const InlineHelpButtonHTML = () => {
export const InlineHelpButtonVerticalLabelDescription = () => {
return (
-
+
{
content: 'Dette er hvor mye du har tenkt å låne totalt.',
}}
/>
-
+
)
}
@@ -356,7 +356,7 @@ export const InlineHelpButtonVerticalLabelDescription = () => {
export const InlineHelpButtonHorizontalLabel = () => {
return (
-
+
{
}}
info="Info message"
/>
-
+
)
}
@@ -400,7 +400,7 @@ export const InlineHelpButtonHorizontalLabel = () => {
export const InlineHelpButtonCompositionFields = () => {
return (
-
+
{
content: 'Dette er hvor mye du har tenkt å låne totalt.',
}}
/>
-
+
)
}
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/create-component/FieldBlock/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/create-component/FieldBlock/info.mdx
index 3f1bda8fe4e..1841ff70c8d 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/create-component/FieldBlock/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/create-component/FieldBlock/info.mdx
@@ -6,7 +6,7 @@ showTabs: true
`FieldBlock` is a reusable wrapper [for building](/uilib/extensions/forms/create-component/) interactive [Field](/uilib/extensions/forms/feature-fields) components.
-It shows surrounding elements through properties from `FieldProps` like `label` and `error`, and ensure that spacing between different fields work as required when put into surrounding components like [Flex.Container](/uilib/layout/flex/container/) or [Card](/uilib/components/card/).
+It shows surrounding elements through properties from `FieldProps` like `label` and `error`, and ensure that spacing between different fields work as required when put into surrounding components like [Flex.Container](/uilib/layout/flex/container/) or [Form.Card](/uilib/extensions/forms/Form/Card/).
It can also be used to group multiple inner FieldBlock components, composing status (error) messages together as one component. Check out the [Field.Composition](/uilib/extensions/forms/base-fields/Composition/) docs for that.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/create-component/useFieldProps/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/create-component/useFieldProps/info.mdx
index c96f6287005..d7991787276 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/create-component/useFieldProps/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/create-component/useFieldProps/info.mdx
@@ -80,8 +80,8 @@ All properties are optional and can be used as needed. These properties can be p
- `required` if true, it will call `validateRequired` for validation.
- `schema` or `pattern` for JSON schema validation powered by [ajv](https://ajv.js.org/).
-- `validator` your custom validation function. It will run on every keystroke. Can be an async function. Use it together with [debounceAsync](/uilib/helpers/functions/#debounce).
-- `onBlurValidator` your custom validation function. It will run on a `handleBlur()` call. Use it over `validator` for validations with side-effects. Can be an async function.
+- `onChangeValidator` your custom validation function. It will run on every keystroke. Can be an async function. Use it together with [debounceAsync](/uilib/helpers/functions/#debounce).
+- `onBlurValidator` your custom validation function. It will run on a `handleBlur()` call. Use it over `onChangeValidator` for validations with side-effects. Can be an async function.
- `validateRequired` does allow you to provide a custom logic for how the `required` property should validate. See example down below.
- `validateInitially` in order to show an error without a change and blur event. Used for rare cases.
- `validateUnchanged` in order to validate without a change and blur event. Used for rare cases.
@@ -166,7 +166,8 @@ During validation, the different APIs do have a prioritization order and will st
1. `required` property
1. `schema` property (including `pattern`)
-1. `validator` property
+1. `onChangeValidator` property
+1. `onBlurValidator` property
### Error handling
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/demo-cases/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/demo-cases/Examples.tsx
index 37fd1d1e843..3b10e9018db 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/demo-cases/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/demo-cases/Examples.tsx
@@ -1,5 +1,5 @@
import * as React from 'react'
-import { Card, Flex } from '@dnb/eufemia/src'
+import { Flex } from '@dnb/eufemia/src'
import {
Form,
Field,
@@ -35,7 +35,7 @@ export const BecomeCorporateCustomer = () => {
Bedriftsopplysninger
-
+
{
label="Etableringsland"
required
/>
-
+
-
+
{
path="/website"
label="Nettstedsadresse (valgfritt)"
/>
-
+
@@ -106,13 +106,13 @@ export const BecomeCorporateCustomer = () => {
Profile
-
+
More information
-
+
@@ -140,7 +140,7 @@ export const BecomeCorporateCustomer = () => {
Profile
-
+
@@ -149,7 +149,7 @@ export const BecomeCorporateCustomer = () => {
-
+
@@ -195,7 +195,7 @@ export function PizzaDemo() {
Which pizza do you want?
-
+
Your Pizza
-
+
-
+
Allergies
-
+
@@ -239,13 +239,13 @@ export function PizzaDemo() {
Delivery address
-
+
Your name
-
+
-
+
Your address
@@ -270,7 +270,7 @@ export function PizzaDemo() {
}}
city={{ required: true, path: '/city' }}
/>
-
+
@@ -278,7 +278,7 @@ export function PizzaDemo() {
Summary
-
+
-
+
-
+
@@ -306,7 +306,7 @@ export function PizzaDemo() {
/>
-
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/BankAccountNumber/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/BankAccountNumber/info.mdx
index 439e589a957..e285bd6e2e8 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/BankAccountNumber/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/BankAccountNumber/info.mdx
@@ -20,5 +20,5 @@ There is a corresponding [Value.BankAccountNumber](/uilib/extensions/forms/Value
### Internal validators exposed
-`Field.BankAccountNumber` expose the `bankAccountNumberValidator` validator through its `validator` and `onBlurValidator` property, take a look at [this demo](/uilib/extensions/forms/feature-fields/BankAccountNumber/demos/#extend-validation-with-custom-validation-function).
-The `bankAccountNumberValidator` validator, validates if the bankaccount number provided is a [Norwegian bankaccount number](https://no.wikipedia.org/wiki/Kontonummer) or not.
+`Field.BankAccountNumber` expose the `bankAccountNumberValidator` validator through its `onChangeValidator` and `onBlurValidator` property, take a look at [this demo](/uilib/extensions/forms/feature-fields/BankAccountNumber/demos/#extend-validation-with-custom-validation-function).
+The `bankAccountNumberValidator` validator, validates if the bank account number provided is a [Norwegian bank account number](https://no.wikipedia.org/wiki/Kontonummer) or not.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Date/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Date/Examples.tsx
index d69621c62d7..b41277a184c 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Date/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Date/Examples.tsx
@@ -90,3 +90,11 @@ export const Range = () => {
)
}
+
+export const AutoClose = () => {
+ return (
+
+
+
+ )
+}
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Date/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Date/demos.mdx
index 77f7380dc9a..058224450f0 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Date/demos.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/Date/demos.mdx
@@ -18,6 +18,14 @@ import * as Examples from './Examples'
+### Automatically close picker
+
+The calendar will be prevented from automatically closing when the submit or cancel buttons are visible, to ensure that the user is actually able to interact with them after date selection.
+
+To enable the picker to close automatically, you have to set `showCancelButton` to `false`, to override the default behavior.
+
+
+
### With help
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/demos.mdx
index 446131cc0b7..844530b2ac5 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/demos.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/demos.mdx
@@ -60,7 +60,7 @@ Below is an example of the error message displayed when there's an invalid D num
### Validation function
-You can provide your own validation function, either to `validator` or `onBlurValidator`.
+You can provide your own validation function, either to `onChangeValidator` or `onBlurValidator`.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/info.mdx
index b3beafc20cf..3af48203034 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/info.mdx
@@ -23,7 +23,7 @@ There is a corresponding [Value.NationalIdentityNumber](/uilib/extensions/forms/
### Internal validators exposed
-`Field.NationalIdentityNumber` expose the following validators through its `validator` and `onBlurValidator` property:
+`Field.NationalIdentityNumber` expose the following validators through its `onChangeValidator` and `onBlurValidator` property:
- `dnrValidator`: validates a D number.
- `fnrValidator`: validates a national identity number (fødselsnummer).
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/OrganizationNumber/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/OrganizationNumber/info.mdx
index 64cfad6512a..a6eedb8e83e 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/OrganizationNumber/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/OrganizationNumber/info.mdx
@@ -21,5 +21,5 @@ There is a corresponding [Value.OrganizationNumber](/uilib/extensions/forms/Valu
### Internal validators exposed
-`Field.OrganizationNumber` expose the `organizationNumberValidator` validator through its `validator` and `onBlurValidator` property, take a look at [this demo](/uilib/extensions/forms/feature-fields/OrganizationNumber/demos/#extend-validation-with-custom-validation-function).
+`Field.OrganizationNumber` expose the `organizationNumberValidator` validator through its `onChangeValidator` and `onBlurValidator` property, take a look at [this demo](/uilib/extensions/forms/feature-fields/OrganizationNumber/demos/#extend-validation-with-custom-validation-function).
The `organizationNumberValidator` validator, validates if the organization number provided is a [Norwegian organization number](https://www.brreg.no/om-oss/registrene-vare/om-enhetsregisteret/organisasjonsnummeret/) or not.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/Examples.tsx
index d73ce8dfb45..ec3b420116c 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/Examples.tsx
@@ -1,6 +1,5 @@
-import { Card } from '@dnb/eufemia/src'
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
-import { Field } from '@dnb/eufemia/src/extensions/forms'
+import { Field, Form } from '@dnb/eufemia/src/extensions/forms'
export const Empty = () => {
return (
@@ -168,9 +167,9 @@ export const LongLabel = () => {
export const InCard = () => {
return (
-
+
-
+
)
}
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/info.mdx
index 4f84024a570..78ffab6ce97 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/info.mdx
@@ -46,7 +46,7 @@ Countries are sorted in alphabetically order, with the following prioritized cou
## Validation
-By default it has no validation. But you can enable it by giving a `required`, `pattern`, `schema` or `validator` property with the needed validation. More about validation in the [Getting Started](/uilib/extensions/forms/getting-started/#validation) section.
+By default it has no validation. But you can enable it by giving a `required`, `pattern`, `schema`, `onChangeValidator` or `onBlurValidator` property with the needed validation. More about validation in the [Getting Started](/uilib/extensions/forms/getting-started/#validation) section.
### Norwegian mobile numbers
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PostalCodeAndCity/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PostalCodeAndCity/Examples.tsx
index 25e8361f887..d98132a2d95 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PostalCodeAndCity/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PostalCodeAndCity/Examples.tsx
@@ -1,4 +1,3 @@
-import { Card } from '@dnb/eufemia/src'
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
import { Field, Form, Iterate } from '@dnb/eufemia/src/extensions/forms'
@@ -178,10 +177,10 @@ export const SettingCountryBasedOnPath = () => {
return (
-
+
-
+
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/Examples.tsx
index 350d848539f..90baa8f6e12 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/Examples.tsx
@@ -1,4 +1,3 @@
-import { Card } from '@dnb/eufemia/src'
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
import {
Field,
@@ -114,7 +113,7 @@ export function TransformInAndOut() {
const MyForm = () => {
return (
-
+
Data Context
-
+
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Password/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Password/info.mdx
index 6cc6d4d8378..61301f8cc6b 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Password/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Password/info.mdx
@@ -13,4 +13,4 @@ render( )
## Validation
-By default it has no validation. But you can enable it by giving a `required`, `pattern`, `schema` or `validator` property with the needed validation. More about validation in the [Getting Started](/uilib/extensions/forms/getting-started/#validation) section.
+By default it has no validation. But you can enable it by giving a `required`, `pattern`, `schema`, `onChangeValidator` or `onBlurValidator` property with the needed validation. More about validation in the [Getting Started](/uilib/extensions/forms/getting-started/#validation) section.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Upload/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Upload/Examples.tsx
index 805e4d682a3..aeb1e13a394 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Upload/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Upload/Examples.tsx
@@ -1,11 +1,10 @@
import { Flex } from '@dnb/eufemia/src'
import ComponentBox from '../../../../../../../shared/tags/ComponentBox'
import { Field, Form, Tools } from '@dnb/eufemia/src/extensions/forms'
-import {
- createMockFile,
- mockAsyncFileUpload,
-} from '../../../../../../../docs/uilib/components/upload/Examples'
+import { createMockFile } from '../../../../../../../docs/uilib/components/upload/Examples'
import useUpload from '@dnb/eufemia/src/components/upload/useUpload'
+import { UploadValue } from '@dnb/eufemia/src/extensions/forms/Field/Upload'
+import { createRequest } from '../../../Form/SubmitIndicator/Examples'
export const BasicUsage = () => {
return (
@@ -85,7 +84,7 @@ export const WithPath = () => {
export const WithAsyncFileHandler = () => {
return (
-
+
{() => {
const MyForm = () => {
return (
@@ -95,7 +94,7 @@ export const WithAsyncFileHandler = () => {
id="async_upload_context_id"
path="/attachments"
labelDescription="Upload multiple files at once to see the upload error message. This demo has been set up so that every other file in a batch will fail."
- asyncFileHandler={mockAsyncFileUpload}
+ fileHandler={mockAsyncFileUpload}
required
/>
@@ -105,6 +104,47 @@ export const WithAsyncFileHandler = () => {
)
}
+ async function mockAsyncFileUpload(
+ newFiles: UploadValue,
+ ): Promise {
+ const updatedFiles: UploadValue = []
+
+ for (const [, file] of Object.entries(newFiles)) {
+ const formData = new FormData()
+ formData.append('file', file.file, file.file.name)
+
+ const request = createRequest()
+ await request(Math.floor(Math.random() * 2000) + 1000) // Simulate a request
+
+ try {
+ const mockResponse = {
+ ok: false, // Fails virus check
+ json: async () => ({
+ server_generated_id:
+ file.file.name + '_' + crypto.randomUUID(),
+ }),
+ }
+
+ if (!mockResponse.ok) {
+ throw new Error('Unable to upload this file')
+ }
+
+ const data = await mockResponse.json()
+ updatedFiles.push({
+ ...file,
+ id: data.server_generated_id,
+ })
+ } catch (error) {
+ updatedFiles.push({
+ ...file,
+ errorMessage: error.message,
+ })
+ }
+ }
+
+ return updatedFiles
+ }
+
const Output = () => {
const { files } = useUpload('async_upload_context_id')
return
@@ -115,3 +155,44 @@ export const WithAsyncFileHandler = () => {
)
}
+
+export const WithSyncFileHandler = () => {
+ return (
+
+ {() => {
+ const MyForm = () => {
+ return (
+ console.log(form)}>
+
+
+
+
+
+
+ )
+ }
+
+ function mockSyncFileUpload(newFiles: UploadValue) {
+ return newFiles.map((file) => {
+ if (file.file.name.length > 5) {
+ file.errorMessage = 'File name is too long'
+ }
+ return file
+ })
+ }
+
+ const Output = () => {
+ const { files } = useUpload('sync_upload_context_id')
+ return
+ }
+
+ return
+ }}
+
+ )
+}
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Upload/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Upload/demos.mdx
index d8cce0a083d..f7ce6eed8eb 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Upload/demos.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Upload/demos.mdx
@@ -28,4 +28,12 @@ import * as Examples from './Examples'
### With asynchronous file handler
+The `fileHandler` property supports an asynchronous function, and can be used for handling/validating files asynchronously, like to upload files to a virus checker and display errors based on the outcome:
+
+
+### With synchronous file handler
+
+The `fileHandler` property supports a synchronous function, and can be used for handling/validating files synchronously, like to check for file names that's too long:
+
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Upload/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Upload/info.mdx
index 2c5ac985757..f67dc9d6114 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Upload/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Upload/info.mdx
@@ -59,9 +59,10 @@ The `value` property represents an array with an object described above:
render( )
```
-## About the `asyncFileHandler` property
+## About the `fileHandler` property
-The `asyncFileHandler` is an asynchronous handler function that takes newly added files as a parameter and returns a promise containing the processed files. The component will automatically handle loading states during the upload process. This feature is useful for tasks like uploading files to a virus checker, which returns a new file ID if the file passes the check. To indicate a failed upload, set the `errorMessage` on the specific file object with the desired message to display next to the file in the upload list.
+The `fileHandler` is a handler function that supports both an asynchronous and synchronous function. It takes newly added files as a parameter and returns processed files (a promise when asynchronous).
+The component will automatically handle asynchronous loading states during the upload process. This feature is useful for tasks like uploading files to a virus checker, which returns a new file ID if the file passes the check. To indicate a failed upload, set the `errorMessage` on the specific file object with the desired message to display next to the file in the upload list.
```js
async function virusCheck(newFiles) {
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 ec4929c6141..8378af5c301 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
@@ -42,7 +42,7 @@ import AsyncChangeExample from './Form/Handler/parts/async-change-example.mdx'
- [required](#required)
- [pattern](#pattern)
- [schema](#schema)
- - [onBlurValidator and validator](#onblurvalidator-and-validator)
+ - [onBlurValidator and onChangeValidator](#onblurvalidator-and-onchangevalidator)
- [Connect with another field](#connect-with-another-field)
- [Async validation](#async-validation)
- [Async validator with debounce](#async-validator-with-debounce)
@@ -390,13 +390,13 @@ More info about the async change behavior in the form [Handler](/uilib/extension
#### Async field validation
-A similar indicator behavior will occur when using async functions for field validation, such as `validator` or `onBlurValidation`, your form will exhibit async behavior. This means that the validation needs to be successfully completed before the form can be submitted.
+A similar indicator behavior will occur when using async functions for field validation, such as `onChangeValidator` or `onBlurValidation`, your form will exhibit async behavior. This means that the validation needs to be successfully completed before the form can be submitted.
### Validation and error handling
Every field component has a built-in validation that is based on the type of data it handles. This validation is automatically applied to the field when the user interacts with it. The validation is also applied when the user submits the form.
-In addition, you can add your own validation to a field component. This is done by adding a `required`, `pattern`, `schema` or `validator` property.
+In addition, you can add your own validation to a field component. This is done by adding a `required`, `pattern`, `schema`, `onChangeValidator` or `onBlurValidation` property.
Fields which have the `disabled` property or the `readOnly` property, will skip validation.
@@ -487,9 +487,9 @@ const schema = {
```
-#### onBlurValidator and validator
+#### onBlurValidator and onChangeValidator
-The `onBlurValidator` and `validator` properties accepts a function that takes the current value of the field as an argument and returns an error message if the value is invalid:
+The `onBlurValidator` and `onChangeValidator` properties accepts a function that takes the current value of the field as an argument and returns an error message if the value is invalid:
```tsx
const onChangeValidator = (value) => {
@@ -498,14 +498,14 @@ const onChangeValidator = (value) => {
return new Error('Invalid value message')
}
}
-render( )
+render( )
```
You can find more info about error messages in the [error messages](/uilib/extensions/forms/Form/error-messages/) docs.
##### Connect with another field
-You can also use the `connectWithPath` function to connect the validator to another field. This allows you to rerun the validator function once the value of the connected field changes:
+You can also use the `connectWithPath` function to connect the validator (`onChangeValidator` and `onBlurValidator`) to another field. This allows you to rerun the validator function once the value of the connected field changes:
```tsx
import { Form, Field } from '@dnb/eufemia/extensions/forms'
@@ -523,15 +523,15 @@ render(
,
)
```
-By default, the validator function will only run when the "/withValidator" field is changed. When the error message is shown, it will update the message with the new value of the "/myReference" field.
+By default, the `onChangeValidator` function will only run when the "/withOnChangeValidator" field is changed. When the error message is shown, it will update the message with the new value of the "/myReference" field.
You can also change this behavior for testing purposes by using the following properties:
@@ -576,7 +576,7 @@ const onChangeValidator = debounceAsync(async function myValidator(value) {
return error
}
})
-render( )
+render( )
```
### Localization and translation
@@ -699,7 +699,7 @@ When creating [your own field](/uilib/extensions/forms/create-component/#localiz
When building your application forms, preferably use the following layout components. They seamlessly places all the fields and components of Eufemia Forms correctly into place.
- [Flex.Stack](/uilib/layout/flex/stack/) layout component for easy and consistent application forms.
-- [Card](/uilib/components/card/) with the stack property `... ` for the default card outline of forms.
+- [Form.Card](/uilib/extensions/forms/Form/Card/) with the stack property `... ` for the default card outline of forms.
- [Form.Appearance](/uilib/extensions/forms/Form/Appearance/) for changing sizes (height) of e.g. input fields.
### Best practices
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/intro-examples.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/intro-examples.mdx
index 51c9cb4c170..f5c65b7d164 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/intro-examples.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/intro-examples.mdx
@@ -35,7 +35,7 @@ properties for simplified form implementations.
## Layout components
-Wrapping inputs in [Flex.Stack](/uilib/layout/flex/stack/) and [Card](/uilib/components/card/) with the `stack` property, provides the standard design without
+Wrapping inputs in [Flex.Stack](/uilib/layout/flex/stack/) and [Form.Card](/uilib/extensions/forms/Form/Card/) with the `stack` property, provides the standard design without
the need for local styles.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/helpers/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/helpers/Examples.tsx
index 0a5cd486913..87e766b226a 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/helpers/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/helpers/Examples.tsx
@@ -100,7 +100,7 @@ export function Selection() {
return (
-
+
If you select a part of this text, you will see the selection
highlight is green.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/helpers/classes.mdx b/packages/dnb-design-system-portal/src/docs/uilib/helpers/classes.mdx
index 67473c9d006..6a355b8aacf 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/helpers/classes.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/helpers/classes.mdx
@@ -6,6 +6,8 @@ order: 1
import * as Examples from 'Docs/uilib/helpers/Examples'
import SkipLinkExample from 'Docs/uilib/usage/accessibility/examples/skip-link-example.tsx'
+# CSS classes
+
## CSS helper classes
Reusing classes in the markup instead of using SCSS extends or _mixins_ will prevent duplication in the `@dnb/eufemia`. So also your application will have good benefits from reusing these helper classes.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/helpers/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/helpers/info.mdx
index 8df645959d5..fe0d4515d1c 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/helpers/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/helpers/info.mdx
@@ -2,6 +2,8 @@
showTabs: true
---
+# Helpers
+
## Description
All the [components](/uilib/components) do share a couple of common used helpers. Your application can also use these helpers, but it's totally optional.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/layout/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/layout/Examples.tsx
index 6b972e78098..123838b4e28 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/layout/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/layout/Examples.tsx
@@ -18,7 +18,6 @@ import {
Autocomplete,
Dropdown,
Space,
- Card,
Code,
Grid,
FormSet,
@@ -48,20 +47,20 @@ export const LayoutComponents = () => {
Profile
-
+
Name
-
+
-
+
More information
-
+
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/layout/flex/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/layout/flex/Examples.tsx
index 7dc43a67f38..7f595809964 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/layout/flex/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/layout/flex/Examples.tsx
@@ -2,7 +2,7 @@ import React from 'react'
import styled from '@emotion/styled'
import ComponentBox from '../../../../shared/tags/ComponentBox'
import MediaQuery from '@dnb/eufemia/src/shared/MediaQuery'
-import { Slider, Code, Button, Card, Flex } from '@dnb/eufemia/src'
+import { Slider, Code, Button, Flex } from '@dnb/eufemia/src'
import {
TestElement,
Field,
@@ -24,20 +24,20 @@ export const LayoutComponents = () => {
Profile
-
+
Name
-
+
-
+
More information
-
+
)
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/layout/flex/container/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/layout/flex/container/info.mdx
index 01540f9f36f..215178062fb 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/layout/flex/container/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/layout/flex/container/info.mdx
@@ -2,14 +2,14 @@
showTabs: true
---
-## Description
-
## Import
```tsx
import { Flex } from '@dnb/eufemia'
```
+## Description
+
`Flex.Container` is a building block for [CSS flexbox](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flexible_box_layout) based layout of contents and components.
**NB:** For form layouts, use [Flex.Stack](/uilib/layout/flex/stack/) instead.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/layout/flex/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/layout/flex/info.mdx
index b059cc2aaea..6bc9303b5a0 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/layout/flex/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/layout/flex/info.mdx
@@ -2,6 +2,12 @@
showTabs: true
---
+## Import
+
+```tsx
+import { Flex } from '@dnb/eufemia'
+```
+
## Description
To make it easier to build application layout and [form](/uilib/extensions/forms)-views in line with defined design sketches, there are a number of components for layout.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/layout/flex/stack/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/layout/flex/stack/Examples.tsx
index fee262eda80..c1cc1721752 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/layout/flex/stack/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/layout/flex/stack/Examples.tsx
@@ -47,11 +47,11 @@ export const WithCard = () => {
return (
-
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam at felis rutrum, luctus dui at, bibendum ipsum.
-
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam at felis rutrum, luctus dui at, bibendum ipsum.
@@ -65,7 +65,7 @@ export const WithCardAndHeading = () => {
Main heading
-
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam at felis rutrum, luctus dui at, bibendum ipsum.
@@ -80,7 +80,7 @@ export const WithCardAndHeadings = () => {
Main heading
Sub heading
-
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam at felis rutrum, luctus dui at, bibendum ipsum.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/typography.mdx b/packages/dnb-design-system-portal/src/docs/uilib/typography.mdx
index 6b5decc8266..daef198fe08 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/typography.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/typography.mdx
@@ -9,6 +9,15 @@ import { TypographyVariants } from 'Docs/uilib/typography/Examples'
# Typography
+## Typography components
+
+The two main components used to set typography are:
+
+- [Span](uilib/elements/span)
+- [P](uilib/elements/paragraph)
+
+([Lead](uilib/elements/lead) and [Ingress](uilib/elements/ingress) also works in the same way)
+
## Typography in general
Fonts are handled automatically once the CSS packages **dnb-ui-core** or **dnb-ui-basis** are loaded.
@@ -55,7 +64,7 @@ Read more about the [Anchor / Text Link](/uilib/components/anchor)
DNB has its own monospace typeface (`font-family`).
-Use it either by a CSS class `.dnb-typo-mono-regular` or define your own like so:
+Use it either by a CSS class `.dnb-t__family--monospace` or define your own like so:
```css
.css-selector {
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/typography/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/typography/Examples.tsx
index 36027183e8a..88d10c27788 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/typography/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/typography/Examples.tsx
@@ -7,6 +7,8 @@ import React from 'react'
import styled from '@emotion/styled'
import ComponentBox from '../../../shared/tags/ComponentBox'
import { Code, H4, Lead, P } from '@dnb/eufemia/src'
+import { useTheme } from '@dnb/eufemia/shared'
+
import { TypographyBox } from '../../../shared/parts/TypographyBox'
const Wrapper = styled.div`
@@ -29,31 +31,62 @@ const FontUsageExample = ({ typo_class, font_family }) => (
)
-export function FontWeightExample() {
+export function FontWeightByThemeExample() {
+ const theme = useTheme()
+
+ if (theme?.name === 'sbanken') {
+ return (
+
+ {/* Regular */}
+
+
+ {/* Medium */}
+
+
+ {/* Bold */}
+
+
+ {/* Mono Regular */}
+
+
+ )
+ }
return (
{/* Regular */}
{/* Medium */}
{/* Bold */}
{/* Mono Regular */}
)
@@ -91,7 +124,7 @@ export function TypographyVariants() {
fabellas senserit inciderint vim.
Text basis (Medium)
-
+
Lorem ipsum dolor sit amet, sint quodsi concludaturque nam ei,
appetere oporteat eam te. Vel in deleniti sensibus, officiis
menandri efficiantur no cum. Per et habemus gubergren. Mundi
@@ -107,7 +140,7 @@ export function TypographyVariants() {
fabellas senserit inciderint vim.
Text small (Medium)
-
+
Lorem ipsum dolor sit amet, sint quodsi concludaturque nam ei,
appetere oporteat eam te. Vel in deleniti sensibus, officiis
menandri efficiantur no cum. Per et habemus gubergren. Mundi
@@ -119,7 +152,7 @@ export function TypographyVariants() {
Lorem ipsum dolor sit amet, sint quodsi concludaturque nam ei.
Text x-small (Medium)
-
+
Lorem ipsum dolor sit amet, sint quodsi concludaturque nam ei.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/typography/_helpers.tsx b/packages/dnb-design-system-portal/src/docs/uilib/typography/_helpers.tsx
new file mode 100644
index 00000000000..427c219979a
--- /dev/null
+++ b/packages/dnb-design-system-portal/src/docs/uilib/typography/_helpers.tsx
@@ -0,0 +1,31 @@
+import { useTheme } from '@dnb/eufemia/shared'
+
+import propertiesSbanken from '@dnb/eufemia/src/style/themes/theme-sbanken/properties'
+import propertiesUi from '@dnb/eufemia/src/style/themes/theme-ui/properties'
+import propertiesEiendom from '@dnb/eufemia/src/style/themes/theme-eiendom/properties'
+
+const properties = {
+ sbanken: propertiesSbanken,
+ ui: propertiesUi,
+ eiendom: propertiesEiendom,
+}
+
+export const GetPropValue = (prop) => {
+ const theme = useTheme()
+ const p = properties[theme.name][prop]
+ if (p && p.startsWith('var(')) {
+ return GetPropValue(p.substring(4, p.indexOf(')')))
+ }
+ return p
+}
+
+export const GetPropAsPx = (prop) => {
+ return RemToPx(GetPropValue(prop))
+}
+
+const RemToPx = (rem = '') => {
+ if (rem.endsWith('rem')) {
+ return parseFloat(rem) * 16 + 'px'
+ }
+ return rem
+}
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/typography/font-size.mdx b/packages/dnb-design-system-portal/src/docs/uilib/typography/font-size.mdx
index f1c138e0de5..30d2375fe21 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/typography/font-size.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/typography/font-size.mdx
@@ -1,23 +1,29 @@
---
title: 'Font Size'
-order: 2
+order: 3
---
-# Font Size
+import { GetPropValue, GetPropAsPx } from './_helpers.tsx'
+import ChangeStyleTheme from '../../../core/ChangeStyleTheme'
-For details about what values Typographic elements do use, have a look at the [Fonts & Typography](/quickguide-designer/fonts#typographic-elements) documentation.
+# Font Size for
+
+
+
+For details about what values Typographic elements do use, have a look at the
+[Fonts & Typography](/quickguide-designer/fonts#typographic-elements) documentation.
## Default `font-size` **rem** table
-| Pixel | Type | Rem | Custom Property | Info |
-| ----- | ---------- | ------------ | ---------------------- | ------------------------------- |
-| 14px | `x-small` | **0.875rem** | `--font-size-x-small` | Do not use for texts |
-| 16px | `small` | **1rem** | `--font-size-small` | [Fallback](#fallback-font-size) |
-| 18px | `basis` | **1.125rem** | `--font-size-basis` | Default size |
-| 20px | `medium` | **1.25rem** | `--font-size-medium` | |
-| 26px | `large` | **1.625rem** | `--font-size-large` | |
-| 34px | `x-large` | **2.125rem** | `--font-size-x-large` | |
-| 48px | `xx-large` | **3rem** | `--font-size-xx-large` | |
+| Pixel | Type | Rem | CSS variable / property | CSS Classname | Info |
+| ------------------------------------- | ---------- | ------------------------------------------ | ----------------------- | ------------------------ | ------------------------------- |
+| {GetPropAsPx('--font-size-x-small')} | `x-small` | **{GetPropValue('--font-size-x-small')}** | `--font-size-x-small` | `.dnb-t__size--x-small` | Do not use for texts |
+| {GetPropAsPx('--font-size-small')} | `small` | **{GetPropValue('--font-size-small')}** | `--font-size-small` | `.dnb-t__size--small` | [Fallback](#fallback-font-size) |
+| {GetPropAsPx('--font-size-basis')} | `basis` | **{GetPropValue('--font-size-basis')}** | `--font-size-basis` | `.dnb-t__size--basis` | Default size |
+| {GetPropAsPx('--font-size-medium')} | `medium` | **{GetPropValue('--font-size-medium')}** | `--font-size-medium` | `.dnb-t__size--medium` | |
+| {GetPropAsPx('--font-size-large')} | `large` | **{GetPropValue('--font-size-large')}** | `--font-size-large` | `.dnb-t__size--large` | |
+| {GetPropAsPx('--font-size-x-large')} | `x-large` | **{GetPropValue('--font-size-x-large')}** | `--font-size-x-large` | `.dnb-t__size--x-large` | |
+| {GetPropAsPx('--font-size-xx-large')} | `xx-large` | **{GetPropValue('--font-size-xx-large')}** | `--font-size-xx-large` | `.dnb-t__size--xx-large` | |
### Code Editor Extensions
@@ -25,16 +31,16 @@ You may be interested to install an [Eufemia code editor extension](/uilib/helpe
## Additional `font-size` **em** table
-| Pixel | Type | Em | Custom Property | Info |
-| ----- | ----------- | ------- | ----------------------- | ---- |
-| 16px | `basis--em` | **1em** | `--font-size-basis--em` | |
+| Pixel | Type | Em | Custom Property | Info |
+| ----- | ----------- | ------------------------------------------- | ----------------------- | ---- |
+| 16px | `basis--em` | **{GetPropValue('--font-size-basis--em')}** | `--font-size-basis--em` | |
## How to use the sizes (CSS)
```css
/* I have a default size */
.dnb-p {
- font-size: var(--font-size-basis); /* 1.125 = 18px */
+ font-size: var(--font-size-basis); /* 1.125rem = 18px (in Ui theme) */
}
```
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/typography/font-weight.mdx b/packages/dnb-design-system-portal/src/docs/uilib/typography/font-weight.mdx
index b06ff72263a..3843404d8eb 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/typography/font-weight.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/typography/font-weight.mdx
@@ -1,23 +1,27 @@
---
title: 'Font Weights'
-order: 1
+order: 2
redirect_from:
- /uilib/typography/font-weights
---
-import { FontWeightExample } from 'Docs/uilib/typography/Examples'
+import { FontWeightByThemeExample } from 'Docs/uilib/typography/Examples'
+import { GetPropValue, GetPropAsPx } from './_helpers.tsx'
+import ChangeStyleTheme from '../../../core/ChangeStyleTheme'
-# Font Weights
+# Font Weights for
+
+
For details about what values Typographic elements do use, have a look at the [Fonts & Typography](/quickguide-designer/fonts#typographic-elements) documentation.
## Eufemia has three (3) font-weights
-| Type | Custom Property | CSS Classname |
-| ---------------------------------------------------------- | ----------------------- | ------------------ |
-| Regular (normal) | `--font-weight-regular` | `dnb-typo-regular` |
-| Medium (500) | `--font-weight-medium` | `dnb-typo-medium` |
-| Bold (600) | `--font-weight-bold` | `dnb-typo-bold` |
+| Type | CSS variable / property | CSS Classname |
+| ------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------- |
+| Regular ({GetPropValue('--font-weight-regular')}) | `--font-weight-regular` | `.dnb-t__weight--regular` |
+| Medium ({GetPropValue('--font-weight-medium')}) | `--font-weight-medium` | `.dnb-t__weight--medium` |
+| Bold ({GetPropValue('--font-weight-bold')}) | `--font-weight-bold` | `.dnb-t__weight--bold` |
### How to use the weights (CSS)
@@ -34,7 +38,7 @@ p {
/* I am Bold */
p {
- font-weight: var(--font-weight-bold); /* 600 */
+ font-weight: var(--font-weight-bold); /* 600 (in Ui theme) */
}
/* This will result in loading the Bold Font */
@@ -47,11 +51,11 @@ p {
```html
-Heading
-Paragraph
-Third Tag
+Heading
+Paragraph
+Third Tag
```
-## Examples
+## weight examples
-
+
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/typography/helper-classes.mdx b/packages/dnb-design-system-portal/src/docs/uilib/typography/helper-classes.mdx
new file mode 100644
index 00000000000..542e182452a
--- /dev/null
+++ b/packages/dnb-design-system-portal/src/docs/uilib/typography/helper-classes.mdx
@@ -0,0 +1,70 @@
+---
+title: 'Helper classes'
+description: 'A list of typography CSS classes'
+order: 1
+---
+
+# Helpers classes
+
+This is a list of all the typography helper classes available. They are used by the [Span](uilib/elements/span) and [Paragraph](uilib/elements/paragraph) components, but can also be used on their own.
+
+For visual examples, see the [Paragraph demos](uilib/elements/paragraph/#demos).
+
+For details on sizes and weights, see the [Font weights](uilib/typography/font-weight/), [Font size](uilib/typography/font-size/) and [Line Height](uilib/typography/line-height/) documentation.
+
+## CSS classes `.dnb-t`
+
+### Font weight
+
+```
+ .dnb-t__weight--regular
+ .dnb-t__weight--medium
+ .dnb-t__weight--bold
+```
+
+### Font size
+
+```
+ .dnb-t__size--xx-large
+ .dnb-t__size--x-large
+ .dnb-t__size--large
+ .dnb-t__size--basis
+ .dnb-t__size--medium
+ .dnb-t__size--small
+ .dnb-t__size--x-small
+```
+
+### Line heights
+
+```
+ .dnb-t__line-height--xx-large
+ .dnb-t__line-height--x-large
+ .dnb-t__line-height--large
+ .dnb-t__line-height--basis
+ .dnb-t__line-height--medium
+ .dnb-t__line-height--small
+ .dnb-t__line-height--x-small
+```
+
+### Text alignment
+
+```
+ .dnb-t__align--center
+ .dnb-t__align--left
+ .dnb-t__align--right
+```
+
+### Font family
+
+```
+ .dnb-t__family--default
+ .dnb-t__family--heading
+ .dnb-t__family--monospace
+```
+
+### Underline / italic
+
+```
+ .dnb-t__decoration--underline
+ .dnb-t__slant--italic
+```
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/typography/line-height.mdx b/packages/dnb-design-system-portal/src/docs/uilib/typography/line-height.mdx
index e6547f21cff..721c439c919 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/typography/line-height.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/typography/line-height.mdx
@@ -1,23 +1,29 @@
---
title: 'Line Height'
-order: 3
+order: 4
---
-# Line Height
+import { GetPropValue, GetPropAsPx } from './_helpers.tsx'
+import ChangeStyleTheme from '../../../core/ChangeStyleTheme'
+
+# Line Height for
+
+
For details about what values Typographic elements do use, have a look at the [Fonts & Typography](/quickguide-designer/fonts#typographic-elements) documentation.
## Default `line-height` **rem** table
-| Pixel | Type | Rem | Custom Property |
-| ----- | --------- | ------------ | ----------------------- |
-| 18px | `x-small` | **1.125rem** | `--line-height-x-small` |
-| 20px | `small` | **1.25rem** | `--line-height-small` |
-| 24px | `basis` | **1.5rem** | `--line-height-basis` |
-| 28px | `lead` | **1.75rem** | `--line-height-lead` |
-| 32px | `medium` | **2rem** | `--line-height-medium` |
-| 40px | `large` | **2.5rem** | `--line-height-large` |
-| 56px | `x-large` | **3.5rem** | `--line-height-x-large` |
+| Pixel | Type | Rem | CSS variable / property | CSS Classname | Info |
+| --------------------------------------- | ---------- | -------------------------------------------- | ------------------------ | ------------------------------- | -------------------------------- |
+| {GetPropAsPx('--line-height-x-small')} | `x-small` | **{GetPropValue('--line-height-x-small')}** | `--line-height-x-small` | `.dnb-t__line-height--x-small` | |
+| {GetPropAsPx('--line-height-small')} | `small` | **{GetPropValue('--line-height-small')}** | `--line-height-small` | `.dnb-t__line-height--small` | |
+| {GetPropAsPx('--line-height-basis')} | `basis` | **{GetPropValue('--line-height-basis')}** | `--line-height-basis` | `.dnb-t__line-height--basis` | |
+| {GetPropAsPx('--line-height-lead')} | `lead` | **{GetPropValue('--line-height-lead')}** | `--line-height-lead` | `.dnb-t__line-height--lead` | Unique line-height for ``. |
+| {GetPropAsPx('--line-height-medium')} | `medium` | **{GetPropValue('--line-height-medium')}** | `--line-height-medium` | `.dnb-t__line-height--medium` | |
+| {GetPropAsPx('--line-height-large')} | `large` | **{GetPropValue('--line-height-large')}** | `--line-height-large` | `.dnb-t__line-height--large` | |
+| {GetPropAsPx('--line-height-x-large')} | `x-large` | **{GetPropValue('--line-height-x-large')}** | `--line-height-x-large` | `.dnb-t__line-height--x-large` | |
+| {GetPropAsPx('--line-height-xx-large')} | `xx-large` | **{GetPropValue('--line-height-xx-large')}** | `--line-height-xx-large` | `.dnb-t__line-height--xx-large` | Same as `x-large` |
### Code Editor Extensions
@@ -25,18 +31,18 @@ You may be interested to install an [Eufemia code editor extension](/uilib/helpe
## Additional `line-height` **em** table
-| Pixel | Type | Em | Custom Property | Info |
-| ----- | -------------- | ----------- | ---------------------------- | ------ |
-| 16px | `xx-small--em` | **1em** | `--line-height-xx-small--em` | |
-| 24px | `basis--em` | **1.333em** | `--line-height-basis--em` | **\*** |
+| Pixel | Type | Em | Custom Property | Info |
+| ----- | -------------- | ------------------------------------------------ | ---------------------------- | ------ |
+| 16px | `xx-small--em` | **{GetPropValue('--line-height-xx-small--em')}** | `--line-height-xx-small--em` | |
+| 24px | `basis--em` | **{GetPropValue('--line-height-basis--em')}** | `--line-height-basis--em` | **\*** |
-**\*** If we sum 1.33333333333\*18 we get 24. Browsers do round CSS values, so we do not need all the decimal numbers for now.
+**\*** For example: if we sum 1.33333333333\*18 we get 24. Browsers do round CSS values, so we do not need all the decimal numbers for now.
### How to use the line heights (CSS)
```css
/* I have a default height */
.dnb-p {
- line-height: var(--line-height-basis); /* 1.5rem = 24px */
+ line-height: var(--line-height-basis); /* 1.5rem = 24px (in Ui theme) */
}
```
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/usage/first-steps.mdx b/packages/dnb-design-system-portal/src/docs/uilib/usage/first-steps.mdx
index a308f3e95ea..4dbf7de225e 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/usage/first-steps.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/usage/first-steps.mdx
@@ -16,6 +16,6 @@ To give you an overall picture of the Design System, you may read [Getting Start
Check out the `@dnb/eufemia` **[Installation documentation](/uilib/usage/#installation)**.
-## Be continued
+## Continue
Continue with [the basics](/uilib/usage/first-steps/the-basics)
diff --git a/packages/dnb-design-system-portal/src/e2e/typography.spec.ts b/packages/dnb-design-system-portal/src/e2e/typography.spec.ts
index 18e9cad1018..53e3dce4e78 100644
--- a/packages/dnb-design-system-portal/src/e2e/typography.spec.ts
+++ b/packages/dnb-design-system-portal/src/e2e/typography.spec.ts
@@ -75,11 +75,11 @@ test.describe('Typography for UI', () => {
})
test('bold text should have correct font-weight', async ({ page }) => {
- await page.waitForSelector('.typography-box > .dnb-typo-bold', {
+ await page.waitForSelector('.typography-box > .dnb-t__weight--bold', {
state: 'attached',
})
const element = page
- .locator('.typography-box > .dnb-typo-bold')
+ .locator('.typography-box > .dnb-t__weight--bold')
.first()
await expect(element).toHaveCSS('font-weight', '600')
})
@@ -162,11 +162,14 @@ test.describe('Typography for Sbanken', () => {
})
test('bold text should have correct font-weight', async ({ page }) => {
- await page.waitForSelector('.typography-box > .dnb-typo-bold', {
- state: 'attached',
- })
+ await page.waitForSelector(
+ '.typography-box > .dnb-t__weight--medium',
+ {
+ state: 'attached',
+ },
+ )
const element = page
- .locator('.typography-box > .dnb-typo-bold')
+ .locator('.typography-box > .dnb-t__weight--medium')
.first()
await expect(element).toHaveCSS('font-weight', '500')
})
diff --git a/packages/dnb-design-system-portal/src/shared/menu/SidebarMenu.module.scss b/packages/dnb-design-system-portal/src/shared/menu/SidebarMenu.module.scss
index 75fc678b093..8a9c19a8994 100644
--- a/packages/dnb-design-system-portal/src/shared/menu/SidebarMenu.module.scss
+++ b/packages/dnb-design-system-portal/src/shared/menu/SidebarMenu.module.scss
@@ -215,7 +215,7 @@
&.dnb-sidebar-menu__theme-badge--sbanken {
padding: 0;
- font-family: var(--sb-font-family-headings);
+ font-family: var(--font-family-heading);
font-weight: normal;
.dnb-sidebar-menu__theme-badge__title {
diff --git a/packages/dnb-design-system-portal/src/shared/menu/SidebarMenu.tsx b/packages/dnb-design-system-portal/src/shared/menu/SidebarMenu.tsx
index 593b12791e0..4e9b32e1cc5 100644
--- a/packages/dnb-design-system-portal/src/shared/menu/SidebarMenu.tsx
+++ b/packages/dnb-design-system-portal/src/shared/menu/SidebarMenu.tsx
@@ -636,7 +636,7 @@ function groupNavItems(navItems: NavItem[], location: Location) {
// Define item object reference in hashmap
hashmap[itemId] = hashItem
- // Add all toplevel heading object references to topLevelHeadings array
+ // Add all top level heading object references to topLevelHeadings array
// so that we wont have to loop through the array a second time to sort out top level headings
if (item.level === 1) {
topLevelHeadings.push(hashmap[itemId])
diff --git a/packages/dnb-design-system-portal/src/shared/parts/PropertiesTable.tsx b/packages/dnb-design-system-portal/src/shared/parts/PropertiesTable.tsx
index 52ea1f429b8..a22cbcf03eb 100644
--- a/packages/dnb-design-system-portal/src/shared/parts/PropertiesTable.tsx
+++ b/packages/dnb-design-system-portal/src/shared/parts/PropertiesTable.tsx
@@ -179,7 +179,7 @@ function convertToCamelCase(doc: string, keys: string[]) {
}
export function formatName(name: string): React.ReactNode | string {
- if (name.includes('/')) {
+ if (name.includes('[')) {
return {name}
}
diff --git a/packages/dnb-eufemia/scripts/prebuild/tasks/__tests__/__snapshots__/makePropertiesFile.test.ts.snap b/packages/dnb-eufemia/scripts/prebuild/tasks/__tests__/__snapshots__/makePropertiesFile.test.ts.snap
index c84adae7547..5418afc2740 100644
--- a/packages/dnb-eufemia/scripts/prebuild/tasks/__tests__/__snapshots__/makePropertiesFile.test.ts.snap
+++ b/packages/dnb-eufemia/scripts/prebuild/tasks/__tests__/__snapshots__/makePropertiesFile.test.ts.snap
@@ -5,7 +5,7 @@ exports[`Properties for sbanken has to validate 1`] = `
export default {
'--sb-font-family-default': '"Roboto", "Helvetica", "Arial", sans-serif',
- '--sb-font-family-headings': '"MaisonNeueHeadings", "Roboto", "Helvetica",',
+ '--sb-font-family-heading': '"MaisonNeueHeadings", "Roboto", "Helvetica",',
'--sb-font-weight-default': 'normal',
'--sb-font-weight-basis': 'normal',
'--sb-font-weight-regular': 'normal',
@@ -97,6 +97,7 @@ export default {
'--color-emerald-green-25': '#c4d4d6',
'--color-emerald-green-10': '#e8eeef',
'--font-family-default': 'var(--sb-font-family-default)',
+ '--font-family-heading': 'var(--sb-font-family-heading)',
'--font-family-monospace': '"DNBMono", "Menlo", "Consolas", "Roboto Mono",',
'--font-weight-default': 'normal',
'--font-weight-basis': 'normal',
@@ -187,7 +188,7 @@ exports[`Properties for ui has to validate 1`] = `
export default {
'--sb-font-family-default': '"Roboto", "Helvetica", "Arial", sans-serif',
- '--sb-font-family-headings': '"MaisonNeueHeadings", "Roboto", "Helvetica",',
+ '--sb-font-family-heading': '"MaisonNeueHeadings", "Roboto", "Helvetica",',
'--sb-font-weight-default': 'normal',
'--sb-font-weight-basis': 'normal',
'--sb-font-weight-regular': 'normal',
@@ -279,6 +280,7 @@ export default {
'--color-emerald-green-25': '#c4d4d6',
'--color-emerald-green-10': '#e8eeef',
'--font-family-default': '"DNB", sans-serif',
+ '--font-family-heading': 'var(--font-family-default)',
'--font-family-monospace': '"DNBMono", "Menlo", "Consolas", "Roboto Mono",',
'--font-weight-default': 'normal',
'--font-weight-basis': 'normal',
diff --git a/packages/dnb-eufemia/src/components/autocomplete/Autocomplete.js b/packages/dnb-eufemia/src/components/autocomplete/Autocomplete.js
index 37f26b72068..22de5e1914e 100644
--- a/packages/dnb-eufemia/src/components/autocomplete/Autocomplete.js
+++ b/packages/dnb-eufemia/src/components/autocomplete/Autocomplete.js
@@ -189,6 +189,7 @@ export default class Autocomplete extends React.PureComponent {
PropTypes.string,
PropTypes.number,
]),
+ /** @deprecated use `selectedKey` */
selected_key: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
diff --git a/packages/dnb-eufemia/src/components/autocomplete/AutocompleteDocs.ts b/packages/dnb-eufemia/src/components/autocomplete/AutocompleteDocs.ts
index f3e63e18783..83d399c7553 100644
--- a/packages/dnb-eufemia/src/components/autocomplete/AutocompleteDocs.ts
+++ b/packages/dnb-eufemia/src/components/autocomplete/AutocompleteDocs.ts
@@ -270,7 +270,7 @@ export const AutocompleteEvents: PropertiesTableProps = {
status: 'optional',
},
on_select: {
- doc: 'Will be called once the users selects an item by a click or keyboard navigation. Returns an object with the new selected `data` item `{ data, event, attributes, value, active_item }` including [these methods](/uilib/components/autocomplete/events#dynamically-change-data). The "active_item" property is the currently selected item by keyboard navigation',
+ doc: 'Will be called once the users focuses or selects an item by a click or keyboard navigation. Returns an object with the new selected `data` item `{ data, event, attributes, value, active_item }` including [these methods](/uilib/components/autocomplete/events#dynamically-change-data). The "active_item" property is the currently selected item by keyboard navigation',
type: 'function',
status: 'optional',
},
diff --git a/packages/dnb-eufemia/src/components/autocomplete/__tests__/Autocomplete.screenshot.test.ts b/packages/dnb-eufemia/src/components/autocomplete/__tests__/Autocomplete.screenshot.test.ts
index 568038e1d0b..d1d4057d75b 100644
--- a/packages/dnb-eufemia/src/components/autocomplete/__tests__/Autocomplete.screenshot.test.ts
+++ b/packages/dnb-eufemia/src/components/autocomplete/__tests__/Autocomplete.screenshot.test.ts
@@ -120,6 +120,21 @@ describe.each(['ui', 'sbanken'])('Autocomplete for %s', (themeName) => {
expect(screenshot).toMatchImageSnapshot()
})
+ it('have to match disabled options', async () => {
+ const screenshot = await makeScreenshot({
+ selector: '[data-visual-test="autocomplete-disabled-options"]',
+ simulateSelector:
+ '[data-visual-test="autocomplete-disabled-options"] .dnb-autocomplete .dnb-input',
+ waitAfterSimulateSelector:
+ '[data-visual-test="autocomplete-disabled-options"] .dnb-autocomplete--opened',
+ simulate: 'click',
+ style: {
+ height: '25rem',
+ },
+ })
+ expect(screenshot).toMatchImageSnapshot()
+ })
+
it('have to match autocomplete opened list', async () => {
const screenshot = await makeScreenshot({
selector: '[data-visual-test="autocomplete-opened"]',
diff --git a/packages/dnb-eufemia/src/components/autocomplete/__tests__/Autocomplete.test.tsx b/packages/dnb-eufemia/src/components/autocomplete/__tests__/Autocomplete.test.tsx
index 738f432fbdb..63557889935 100644
--- a/packages/dnb-eufemia/src/components/autocomplete/__tests__/Autocomplete.test.tsx
+++ b/packages/dnb-eufemia/src/components/autocomplete/__tests__/Autocomplete.test.tsx
@@ -23,8 +23,8 @@ import {
} from '@testing-library/react'
import {
DrawerListData,
- DrawerListDataObject,
- DrawerListDataObjectUnion,
+ DrawerListDataArrayObject,
+ DrawerListDataArray,
} from '../../../fragments/drawer-list'
import { Provider } from '../../../shared'
@@ -42,7 +42,7 @@ const props: AutocompleteAllProps = {
skip_portal: true,
}
-const mockData: DrawerListDataObjectUnion[] = [
+const mockData: DrawerListDataArray = [
'AA c',
'BB cc zethx',
{ content: ['CC', 'cc'] },
@@ -385,7 +385,7 @@ describe('Autocomplete component', () => {
const nodes1 = document.querySelectorAll('.dnb-sr-only')
expect(nodes1[nodes1.length - 1].textContent).toBe('2 alternativer')
- const content = (mockData[2] as DrawerListDataObject).content
+ const content = (mockData[2] as DrawerListDataArrayObject).content
expect(
document.querySelectorAll('li.dnb-drawer-list__option')[0]
.textContent
@@ -763,7 +763,7 @@ describe('Autocomplete component', () => {
fireEvent.change(inputElement, {
target: { value: 'cc' },
})
- const content = (mockData[2] as DrawerListDataObject).content
+ const content = (mockData[2] as DrawerListDataArrayObject).content
expect(optionElements()[0].textContent).toBe(
(content as string[]).join('')
)
diff --git a/packages/dnb-eufemia/src/components/autocomplete/__tests__/__image_snapshots__/autocomplete-for-sbanken-have-to-match-autocomplete-opened-list.snap.png b/packages/dnb-eufemia/src/components/autocomplete/__tests__/__image_snapshots__/autocomplete-for-sbanken-have-to-match-autocomplete-opened-list.snap.png
index 98836c29dc4..8ecc091eecc 100644
Binary files a/packages/dnb-eufemia/src/components/autocomplete/__tests__/__image_snapshots__/autocomplete-for-sbanken-have-to-match-autocomplete-opened-list.snap.png and b/packages/dnb-eufemia/src/components/autocomplete/__tests__/__image_snapshots__/autocomplete-for-sbanken-have-to-match-autocomplete-opened-list.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/autocomplete/__tests__/__image_snapshots__/autocomplete-for-sbanken-have-to-match-autocomplete-with-search-result.snap.png b/packages/dnb-eufemia/src/components/autocomplete/__tests__/__image_snapshots__/autocomplete-for-sbanken-have-to-match-autocomplete-with-search-result.snap.png
index cec3435d465..818dccf5aa7 100644
Binary files a/packages/dnb-eufemia/src/components/autocomplete/__tests__/__image_snapshots__/autocomplete-for-sbanken-have-to-match-autocomplete-with-search-result.snap.png and b/packages/dnb-eufemia/src/components/autocomplete/__tests__/__image_snapshots__/autocomplete-for-sbanken-have-to-match-autocomplete-with-search-result.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/autocomplete/__tests__/__image_snapshots__/autocomplete-for-sbanken-have-to-match-autocomplete-with-suffix-value.snap.png b/packages/dnb-eufemia/src/components/autocomplete/__tests__/__image_snapshots__/autocomplete-for-sbanken-have-to-match-autocomplete-with-suffix-value.snap.png
index 60e5dc36841..72004999a82 100644
Binary files a/packages/dnb-eufemia/src/components/autocomplete/__tests__/__image_snapshots__/autocomplete-for-sbanken-have-to-match-autocomplete-with-suffix-value.snap.png and b/packages/dnb-eufemia/src/components/autocomplete/__tests__/__image_snapshots__/autocomplete-for-sbanken-have-to-match-autocomplete-with-suffix-value.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/autocomplete/__tests__/__image_snapshots__/autocomplete-for-sbanken-have-to-match-disabled-options.snap.png b/packages/dnb-eufemia/src/components/autocomplete/__tests__/__image_snapshots__/autocomplete-for-sbanken-have-to-match-disabled-options.snap.png
new file mode 100644
index 00000000000..aafbeee0cc7
Binary files /dev/null and b/packages/dnb-eufemia/src/components/autocomplete/__tests__/__image_snapshots__/autocomplete-for-sbanken-have-to-match-disabled-options.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/autocomplete/__tests__/__image_snapshots__/autocomplete-for-ui-have-to-match-disabled-options.snap.png b/packages/dnb-eufemia/src/components/autocomplete/__tests__/__image_snapshots__/autocomplete-for-ui-have-to-match-disabled-options.snap.png
new file mode 100644
index 00000000000..ea77bf49488
Binary files /dev/null and b/packages/dnb-eufemia/src/components/autocomplete/__tests__/__image_snapshots__/autocomplete-for-ui-have-to-match-disabled-options.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/autocomplete/__tests__/__snapshots__/Autocomplete.test.tsx.snap b/packages/dnb-eufemia/src/components/autocomplete/__tests__/__snapshots__/Autocomplete.test.tsx.snap
index 8cb57beb81a..4e8f7ff629b 100644
--- a/packages/dnb-eufemia/src/components/autocomplete/__tests__/__snapshots__/Autocomplete.test.tsx.snap
+++ b/packages/dnb-eufemia/src/components/autocomplete/__tests__/__snapshots__/Autocomplete.test.tsx.snap
@@ -2936,14 +2936,14 @@ exports[`Autocomplete scss have to match default theme snapshot 1`] = `
.dnb-autocomplete__root .dnb-drawer-list__option__item:nth-of-type(1) {
font-weight: var(--font-weight-basis);
}
-.dnb-autocomplete__root .dnb-drawer-list__option__item:nth-of-type(n + 2) {
+.dnb-autocomplete__root .dnb-drawer-list__option:not([disabled]) .dnb-drawer-list__option__item:nth-of-type(n + 2) {
color: var(--color-black-55);
}
.dnb-autocomplete__root .dnb-drawer-list__option__item--highlight {
font-weight: var(--font-weight-bold);
}
-.dnb-autocomplete__root .dnb-drawer-list__option--selected .dnb-drawer-list__option__item:nth-of-type(n + 2) {
+.dnb-autocomplete__root .dnb-drawer-list__option--selected:not([disabled]) .dnb-drawer-list__option__item:nth-of-type(n + 2) {
color: var(--color-white);
}
diff --git a/packages/dnb-eufemia/src/components/autocomplete/stories/Autocomplete.stories.tsx b/packages/dnb-eufemia/src/components/autocomplete/stories/Autocomplete.stories.tsx
index 3eb1e9b8dc5..1c4e09e0971 100644
--- a/packages/dnb-eufemia/src/components/autocomplete/stories/Autocomplete.stories.tsx
+++ b/packages/dnb-eufemia/src/components/autocomplete/stories/Autocomplete.stories.tsx
@@ -20,7 +20,7 @@ import { SubmitButton } from '../../input/Input'
import { format } from '../../number-format/NumberUtils'
import {
DrawerListData,
- DrawerListDataObjectUnion,
+ DrawerListDataArray,
} from '../../../fragments/DrawerList'
export default {
@@ -856,7 +856,7 @@ const WideStyle = styled.div`
export function DataSuffix() {
const { locale } = React.useContext(Context)
const ban = format(21001234567, { ban: true, locale }) as string
- const numbers: DrawerListDataObjectUnion[] = [
+ const numbers: DrawerListDataArray = [
{
selected_value: `Brukskonto (${ban})`,
suffix_value: (
diff --git a/packages/dnb-eufemia/src/components/autocomplete/style/themes/dnb-autocomplete-theme-ui.scss b/packages/dnb-eufemia/src/components/autocomplete/style/themes/dnb-autocomplete-theme-ui.scss
index 034441bc243..3662e9d5646 100644
--- a/packages/dnb-eufemia/src/components/autocomplete/style/themes/dnb-autocomplete-theme-ui.scss
+++ b/packages/dnb-eufemia/src/components/autocomplete/style/themes/dnb-autocomplete-theme-ui.scss
@@ -53,7 +53,7 @@
font-weight: var(--font-weight-basis);
}
- &__item:nth-of-type(n + 2) {
+ &:not([disabled]) .dnb-drawer-list__option__item:nth-of-type(n + 2) {
color: var(--color-black-55);
}
@@ -63,7 +63,7 @@
}
.dnb-autocomplete__root
- .dnb-drawer-list__option--selected
+ .dnb-drawer-list__option--selected:not([disabled])
.dnb-drawer-list__option__item:nth-of-type(n + 2) {
color: var(--color-white);
}
diff --git a/packages/dnb-eufemia/src/components/avatar/style/themes/dnb-avatar-theme-sbanken.scss b/packages/dnb-eufemia/src/components/avatar/style/themes/dnb-avatar-theme-sbanken.scss
index abbfdb5a453..b1aafe6452f 100644
--- a/packages/dnb-eufemia/src/components/avatar/style/themes/dnb-avatar-theme-sbanken.scss
+++ b/packages/dnb-eufemia/src/components/avatar/style/themes/dnb-avatar-theme-sbanken.scss
@@ -26,12 +26,12 @@
&--size-large {
font-weight: var(--sb-font-weight-basis);
- font-family: var(--sb-font-family-headings);
+ font-family: var(--font-family-heading);
}
&--size-x-large {
font-weight: var(--sb-font-weight-basis);
- font-family: var(--sb-font-family-headings);
+ font-family: var(--font-family-heading);
}
&__group {
diff --git a/packages/dnb-eufemia/src/components/breadcrumb/__tests__/__snapshots__/Breadcrumb.test.tsx.snap b/packages/dnb-eufemia/src/components/breadcrumb/__tests__/__snapshots__/Breadcrumb.test.tsx.snap
index e91825894cb..d0fcc560488 100644
--- a/packages/dnb-eufemia/src/components/breadcrumb/__tests__/__snapshots__/Breadcrumb.test.tsx.snap
+++ b/packages/dnb-eufemia/src/components/breadcrumb/__tests__/__snapshots__/Breadcrumb.test.tsx.snap
@@ -624,9 +624,19 @@ button.dnb-button::-moz-focus-inner {
.dnb-section:not([style*="--breakout"]) {
--breakout: var(--breakout--on);
}
+.dnb-section[style*="--outset"].dnb-space[style]:not(.dnb-card) {
+ padding-left: calc(var(--padding-left) * (1 - var(--outset)));
+ padding-right: calc(var(--padding-right) * (1 - var(--outset)));
+}
+.dnb-section[style*="--outset"]::before {
+ margin-left: calc(var(--outset-left, var(--padding-left)) * -1 * var(--outset));
+ margin-right: calc(var(--outset-right, var(--padding-right)) * -1 * var(--outset));
+ background-color: inherit;
+}
@media screen and (max-width: 60em) {
.dnb-section {
--breakout: var(--breakout--small, var(--breakout--fallback));
+ --outset: var(--outset--small, var(--outset--fallback));
--background-color--value: var(--background-color--small);
--text-color--value: var(--text-color--small);
--outline-color: var(--outline-color--small);
@@ -640,6 +650,7 @@ button.dnb-button::-moz-focus-inner {
@media screen and (max-width: 60em) and (min-width: 40.00625em) {
.dnb-section {
--breakout: var(--breakout--medium, var(--breakout--fallback));
+ --outset: var(--outset--medium, var(--outset--fallback));
--background-color--value: var(--background-color--medium);
--text-color--value: var(--text-color--medium);
--outline-color: var(--outline-color--medium);
@@ -653,6 +664,7 @@ button.dnb-button::-moz-focus-inner {
@media screen and (min-width: 60.00625em) {
.dnb-section {
--breakout: var(--breakout--large, var(--breakout--fallback));
+ --outset: var(--outset--large, var(--outset--fallback));
--background-color--value: var(--background-color--large);
--text-color--value: var(--text-color--large);
--outline-color: var(--outline-color--large);
diff --git a/packages/dnb-eufemia/src/components/button/__tests__/__image_snapshots__/button-for-ui-primary-have-to-match-a-stretched-button.snap.png b/packages/dnb-eufemia/src/components/button/__tests__/__image_snapshots__/button-for-ui-primary-have-to-match-a-stretched-button.snap.png
index c487a1d9a7b..0b5a780b97a 100644
Binary files a/packages/dnb-eufemia/src/components/button/__tests__/__image_snapshots__/button-for-ui-primary-have-to-match-a-stretched-button.snap.png and b/packages/dnb-eufemia/src/components/button/__tests__/__image_snapshots__/button-for-ui-primary-have-to-match-a-stretched-button.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/card/Card.tsx b/packages/dnb-eufemia/src/components/card/Card.tsx
index c4698e7cb35..5cbf9e1530a 100644
--- a/packages/dnb-eufemia/src/components/card/Card.tsx
+++ b/packages/dnb-eufemia/src/components/card/Card.tsx
@@ -28,6 +28,7 @@ export type Props = {
*/
filled?: boolean
} & FlexContainerProps &
+ Pick &
FlexItemProps & {
stack?: boolean
} & SpaceProps &
@@ -49,6 +50,7 @@ function Card(props: Props) {
rowGap,
responsive = !nestedContext?.isNested,
filled,
+ outset,
title,
children,
...rest
@@ -79,6 +81,11 @@ function Card(props: Props) {
filled && 'dnb-card--filled'
),
breakout: responsive ? trueWhenSmall : false,
+ outset: nestedContext?.isNested
+ ? false
+ : outset === true
+ ? falseWhenSmall
+ : outset,
roundedCorner: responsive ? falseWhenSmall : true,
outline: '--outline-card-color',
innerSpace:
diff --git a/packages/dnb-eufemia/src/components/card/CardDocs.ts b/packages/dnb-eufemia/src/components/card/CardDocs.ts
index 32e85eba4dd..418be0af9e4 100644
--- a/packages/dnb-eufemia/src/components/card/CardDocs.ts
+++ b/packages/dnb-eufemia/src/components/card/CardDocs.ts
@@ -1,6 +1,11 @@
import { PropertiesTableProps } from '../../shared/types'
export const CardProperties: PropertiesTableProps = {
+ outset: {
+ doc: 'True to break out negatively on larger screens. Defaults to `false`.',
+ type: 'boolean',
+ status: 'optional',
+ },
stack: {
doc: 'True to stack the sub components with lines between. The `spacing` will default to `medium`.',
type: 'boolean',
diff --git a/packages/dnb-eufemia/src/components/card/__tests__/Card.screenshot.test.ts b/packages/dnb-eufemia/src/components/card/__tests__/Card.screenshot.test.ts
index b0cce62c30c..fc11951fd78 100644
--- a/packages/dnb-eufemia/src/components/card/__tests__/Card.screenshot.test.ts
+++ b/packages/dnb-eufemia/src/components/card/__tests__/Card.screenshot.test.ts
@@ -64,6 +64,16 @@ describe.each(['ui', 'sbanken'])('Card for %s', (themeName) => {
})
expect(screenshot).toMatchImageSnapshot()
})
+
+ it('have to match outset', async () => {
+ const screenshot = await makeScreenshot({
+ selector: '[data-visual-test="layout-card-outset"]',
+ wrapperStyle: {
+ padding: '2rem',
+ },
+ })
+ expect(screenshot).toMatchImageSnapshot()
+ })
})
describe.each(['ui', 'sbanken'])(
@@ -114,9 +124,18 @@ describe.each(['ui', 'sbanken'])(
it('have to match nested cards', async () => {
const screenshot = await makeScreenshot({
+ ...params,
selector: '[data-visual-test="layout-card-nested"]',
})
expect(screenshot).toMatchImageSnapshot()
})
+
+ it('have to match outset', async () => {
+ const screenshot = await makeScreenshot({
+ ...params,
+ selector: '[data-visual-test="layout-card-outset"]',
+ })
+ expect(screenshot).toMatchImageSnapshot()
+ })
}
)
diff --git a/packages/dnb-eufemia/src/components/card/__tests__/Card.test.tsx b/packages/dnb-eufemia/src/components/card/__tests__/Card.test.tsx
index b511b182080..b49ac267f67 100644
--- a/packages/dnb-eufemia/src/components/card/__tests__/Card.test.tsx
+++ b/packages/dnb-eufemia/src/components/card/__tests__/Card.test.tsx
@@ -131,6 +131,7 @@ describe('Card', () => {
expect(element).toHaveClass('dnb-flex-item--align-self-stretch')
expect(container).toHaveClass('dnb-flex-container--align-stretch')
expect(container).toHaveClass('dnb-flex-container--align-self-stretch')
+ expect(container).toHaveClass('dnb-flex-container--spacing-medium')
})
it('should set align="stretch" classes', () => {
@@ -331,6 +332,55 @@ describe('Card', () => {
expect(element.getAttribute('style')).not.toContain('--space-')
})
+ it('should support "outset"', () => {
+ const { rerender } = render( )
+
+ const element = document.querySelector('.dnb-card')
+
+ expect(element).toHaveStyle('--outset--small: 0')
+ expect(element).toHaveStyle('--outset--medium: 1')
+ expect(element).toHaveStyle('--outset--large: 1')
+
+ rerender(
+
+ )
+
+ expect(element).toHaveStyle('--outset--small: 1')
+ expect(element).toHaveStyle('--outset--medium: 0')
+ expect(element).toHaveStyle('--outset--large: 0')
+
+ rerender( )
+
+ expect(element).toHaveStyle('--outset--small: 0')
+ expect(element).toHaveStyle('--outset--medium: 0')
+ expect(element).toHaveStyle('--outset--large: 0')
+ })
+
+ it('should not allow "outset" on nested cards', () => {
+ render(
+
+
+
+ )
+
+ const firstCard = document.querySelector('.dnb-card')
+ const secondCard = firstCard.querySelector('.dnb-card')
+
+ expect(firstCard).toHaveStyle('--outset--small: 0')
+ expect(firstCard).toHaveStyle('--outset--medium: 1')
+ expect(firstCard).toHaveStyle('--outset--large: 1')
+
+ expect(secondCard).toHaveStyle('--outset--small: 0')
+ expect(secondCard).toHaveStyle('--outset--medium: 0')
+ expect(secondCard).toHaveStyle('--outset--large: 0')
+ })
+
it('should support "responsive" of false', () => {
const { rerender } = render(
diff --git a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-border.snap.png b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-border.snap.png
index 1341fae6d4a..ec3b90a7fd9 100644
Binary files a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-border.snap.png and b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-border.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-flex.snap.png b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-flex.snap.png
index 659c3a692d3..ba3adaffda8 100644
Binary files a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-flex.snap.png and b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-flex.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-grid.snap.png b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-grid.snap.png
index 49f2bb926b6..436df89e238 100644
Binary files a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-grid.snap.png and b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-grid.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-nested-cards.snap.png b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-nested-cards.snap.png
index ff04ac4c618..fc01ca68321 100644
Binary files a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-nested-cards.snap.png and b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-nested-cards.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-outset.snap.png b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-outset.snap.png
new file mode 100644
index 00000000000..6861084414c
Binary files /dev/null and b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-outset.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-stack.snap.png b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-stack.snap.png
index e996ea8b2d2..3dd8a8093ab 100644
Binary files a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-stack.snap.png and b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-sbanken-have-to-match-stack.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-border.snap.png b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-border.snap.png
index 7834c550818..52f48e64e6d 100644
Binary files a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-border.snap.png and b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-border.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-flex.snap.png b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-flex.snap.png
index 6557a5da975..af019fe8ae0 100644
Binary files a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-flex.snap.png and b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-flex.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-grid.snap.png b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-grid.snap.png
index f98836aaf34..eb7b65197e4 100644
Binary files a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-grid.snap.png and b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-grid.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-nested-cards.snap.png b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-nested-cards.snap.png
index 9259467f394..ac6854001b2 100644
Binary files a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-nested-cards.snap.png and b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-nested-cards.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-outset.snap.png b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-outset.snap.png
new file mode 100644
index 00000000000..3ac7fbdb711
Binary files /dev/null and b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-outset.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-stack.snap.png b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-stack.snap.png
index 42dc1863332..9db4e1400f5 100644
Binary files a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-stack.snap.png and b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-stack.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-sbanken-have-to-match-border.snap.png b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-sbanken-have-to-match-border.snap.png
index d1b615f50d3..bf00eb3413a 100644
Binary files a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-sbanken-have-to-match-border.snap.png and b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-sbanken-have-to-match-border.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-sbanken-have-to-match-nested-cards.snap.png b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-sbanken-have-to-match-nested-cards.snap.png
index b790f320a06..5c013d0b3d6 100644
Binary files a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-sbanken-have-to-match-nested-cards.snap.png and b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-sbanken-have-to-match-nested-cards.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-sbanken-have-to-match-outset.snap.png b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-sbanken-have-to-match-outset.snap.png
new file mode 100644
index 00000000000..6ad046db986
Binary files /dev/null and b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-sbanken-have-to-match-outset.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-ui-have-to-match-border.snap.png b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-ui-have-to-match-border.snap.png
index 0a9f5c4cf39..2bb50499308 100644
Binary files a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-ui-have-to-match-border.snap.png and b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-ui-have-to-match-border.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-ui-have-to-match-nested-cards.snap.png b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-ui-have-to-match-nested-cards.snap.png
index cde267ddd31..4b640080bd3 100644
Binary files a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-ui-have-to-match-nested-cards.snap.png and b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-ui-have-to-match-nested-cards.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-ui-have-to-match-outset.snap.png b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-ui-have-to-match-outset.snap.png
new file mode 100644
index 00000000000..f966fc9cad2
Binary files /dev/null and b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-ui-have-to-match-outset.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/card/stories/Card.stories.tsx b/packages/dnb-eufemia/src/components/card/stories/Card.stories.tsx
index ef2ae33b361..9bbca63b7ec 100644
--- a/packages/dnb-eufemia/src/components/card/stories/Card.stories.tsx
+++ b/packages/dnb-eufemia/src/components/card/stories/Card.stories.tsx
@@ -6,7 +6,7 @@
import React from 'react'
import { Field, Form } from '../../../extensions/forms'
-import { Card, Flex, Grid } from '../../'
+import { Card, Flex, Grid, Section, Space } from '../../'
import { Wrapper, Box } from 'storybook-utils/helpers'
import { H2, P } from '../../../elements'
@@ -190,3 +190,31 @@ export const WithGrid = () => {
)
}
+
+export const WithOutset = () => {
+ return (
+
+
+ Main heading
+
+ Card content
+
+ Nested card
+
+
+
+
+
+
+
+ )
+}
diff --git a/packages/dnb-eufemia/src/components/card/style/dnb-card.scss b/packages/dnb-eufemia/src/components/card/style/dnb-card.scss
index 5627d916408..75806e919d6 100644
--- a/packages/dnb-eufemia/src/components/card/style/dnb-card.scss
+++ b/packages/dnb-eufemia/src/components/card/style/dnb-card.scss
@@ -114,9 +114,19 @@
align-self: flex-start;
}
- // Nested Cards
- & .dnb-card {
- --outline-width: 0.125rem;
+ &[style*='--outset'] {
+ &.dnb-space[style]:not(.dnb-card) {
+ padding-left: calc(var(--padding-left) * calc(1 - var(--outset)));
+ padding-right: calc(var(--padding-right) * calc(1 - var(--outset)));
+ }
+ &.dnb-card > .dnb-flex-container {
+ margin-left: calc(
+ var(--padding-left, var(--spacing-medium)) * -1 * var(--outset)
+ );
+ margin-right: calc(
+ var(--padding-right, var(--spacing-medium)) * -1 * var(--outset)
+ );
+ }
}
}
@@ -125,14 +135,22 @@
+ * + .dnb-card,/* e.g. one paragraph */
+ * + * + .dnb-card,/* e.g. two paragraphs */
+ .dnb-help-button__content + .dnb-section + .dnb-card
+) {
+ &:not([class*='space__bottom']) {
+ margin-bottom: var(--spacing-small);
+ }
+}
+
+// Deprecated – can be removed in v11 (its handled by the outset prop)
+.dnb-card--auto-indent:has(
+ + .dnb-card:not([style*='--outset']),
+ + * + .dnb-card:not([style*='--outset']),/* e.g. one paragraph */
+ + * + * + .dnb-card:not([style*='--outset']),/* e.g. two paragraphs */
+ + .dnb-help-button__content + .dnb-section + .dnb-card:not([style*='--outset'])
) {
&:not([class*='space__left']) {
@include allAbove(small) {
margin-left: var(--spacing-medium);
}
}
-
- &:not([class*='space__bottom']) {
- margin-bottom: var(--spacing-small);
- }
}
diff --git a/packages/dnb-eufemia/src/components/dialog/__tests__/__image_snapshots__/dialog-for-sbanken-have-to-match-a-top-aligned-dialog.snap.png b/packages/dnb-eufemia/src/components/dialog/__tests__/__image_snapshots__/dialog-for-sbanken-have-to-match-a-top-aligned-dialog.snap.png
index 22a5a6e4738..973ad460813 100644
Binary files a/packages/dnb-eufemia/src/components/dialog/__tests__/__image_snapshots__/dialog-for-sbanken-have-to-match-a-top-aligned-dialog.snap.png and b/packages/dnb-eufemia/src/components/dialog/__tests__/__image_snapshots__/dialog-for-sbanken-have-to-match-a-top-aligned-dialog.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/dialog/__tests__/__image_snapshots__/dialog-for-ui-have-to-match-a-top-aligned-dialog.snap.png b/packages/dnb-eufemia/src/components/dialog/__tests__/__image_snapshots__/dialog-for-ui-have-to-match-a-top-aligned-dialog.snap.png
index 6c7de005b85..ec4fe41e01c 100644
Binary files a/packages/dnb-eufemia/src/components/dialog/__tests__/__image_snapshots__/dialog-for-ui-have-to-match-a-top-aligned-dialog.snap.png and b/packages/dnb-eufemia/src/components/dialog/__tests__/__image_snapshots__/dialog-for-ui-have-to-match-a-top-aligned-dialog.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/dialog/__tests__/__snapshots__/Dialog.test.tsx.snap b/packages/dnb-eufemia/src/components/dialog/__tests__/__snapshots__/Dialog.test.tsx.snap
index 7bd9ba47689..62b3becfd47 100644
--- a/packages/dnb-eufemia/src/components/dialog/__tests__/__snapshots__/Dialog.test.tsx.snap
+++ b/packages/dnb-eufemia/src/components/dialog/__tests__/__snapshots__/Dialog.test.tsx.snap
@@ -677,11 +677,12 @@ button.dnb-button::-moz-focus-inner {
transform: translate3d(0, -0.5rem, 0);
}
:not(.dnb-card) .dnb-help-button__content .dnb-section {
- margin-left: calc(var(--help-button-indent-width) * -1);
- margin-right: calc(var(--help-button-indent-width) * -1);
- border: var(--help-button-indent-width) solid var(--help-button-content-background);
- border-top: none;
- border-bottom: none;
+ --outset-left: calc(
+ var(--spacing-medium) + var(--help-button-indent-width)
+ );
+ --outset-right: calc(
+ var(--spacing-medium) + var(--help-button-indent-width)
+ );
}
.dnb-help-button__content.dnb-height-animation--parallax .dnb-section .dnb-p {
transform: translate3d(0, 0, 0);
diff --git a/packages/dnb-eufemia/src/components/drawer/__tests__/__snapshots__/Drawer.test.tsx.snap b/packages/dnb-eufemia/src/components/drawer/__tests__/__snapshots__/Drawer.test.tsx.snap
index 6f087ae70f5..c74fc3f8533 100644
--- a/packages/dnb-eufemia/src/components/drawer/__tests__/__snapshots__/Drawer.test.tsx.snap
+++ b/packages/dnb-eufemia/src/components/drawer/__tests__/__snapshots__/Drawer.test.tsx.snap
@@ -678,11 +678,12 @@ button.dnb-button::-moz-focus-inner {
transform: translate3d(0, -0.5rem, 0);
}
:not(.dnb-card) .dnb-help-button__content .dnb-section {
- margin-left: calc(var(--help-button-indent-width) * -1);
- margin-right: calc(var(--help-button-indent-width) * -1);
- border: var(--help-button-indent-width) solid var(--help-button-content-background);
- border-top: none;
- border-bottom: none;
+ --outset-left: calc(
+ var(--spacing-medium) + var(--help-button-indent-width)
+ );
+ --outset-right: calc(
+ var(--spacing-medium) + var(--help-button-indent-width)
+ );
}
.dnb-help-button__content.dnb-height-animation--parallax .dnb-section .dnb-p {
transform: translate3d(0, 0, 0);
diff --git a/packages/dnb-eufemia/src/components/dropdown/Dropdown.js b/packages/dnb-eufemia/src/components/dropdown/Dropdown.js
index 3a203a3a185..5f2a44109de 100644
--- a/packages/dnb-eufemia/src/components/dropdown/Dropdown.js
+++ b/packages/dnb-eufemia/src/components/dropdown/Dropdown.js
@@ -127,6 +127,7 @@ export default class Dropdown extends React.PureComponent {
PropTypes.string,
PropTypes.number,
]),
+ /** @deprecated use `selectedKey` */
selected_key: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
diff --git a/packages/dnb-eufemia/src/components/dropdown/__tests__/Dropdown.screenshot.test.ts b/packages/dnb-eufemia/src/components/dropdown/__tests__/Dropdown.screenshot.test.ts
index fd198cc1749..a7229155f37 100644
--- a/packages/dnb-eufemia/src/components/dropdown/__tests__/Dropdown.screenshot.test.ts
+++ b/packages/dnb-eufemia/src/components/dropdown/__tests__/Dropdown.screenshot.test.ts
@@ -36,6 +36,20 @@ describe.each(['ui', 'sbanken'])('Dropdown for %s', (themeName) => {
expect(screenshot).toMatchImageSnapshot()
})
+ it('have to match disabled options', async () => {
+ const screenshot = await makeScreenshot({
+ style: {
+ 'padding-bottom': '14rem',
+ },
+ selector: '[data-visual-test="dropdown-disabled-options"]',
+ simulate: 'click',
+ simulateSelector:
+ '[data-visual-test="dropdown-disabled-options"] .dnb-dropdown__trigger',
+ simulateAfter: { keypress: 'Escape' },
+ })
+ expect(screenshot).toMatchImageSnapshot()
+ })
+
it('have to match tertiary variant disabled state', async () => {
const screenshot = await makeScreenshot({
selector: '[data-visual-test="dropdown-disabled-tertiary"]',
diff --git a/packages/dnb-eufemia/src/components/dropdown/__tests__/Dropdown.test.tsx b/packages/dnb-eufemia/src/components/dropdown/__tests__/Dropdown.test.tsx
index f0cb5ae620d..4a6375e97a4 100644
--- a/packages/dnb-eufemia/src/components/dropdown/__tests__/Dropdown.test.tsx
+++ b/packages/dnb-eufemia/src/components/dropdown/__tests__/Dropdown.test.tsx
@@ -13,8 +13,8 @@ import {
testDirectionObserver,
} from '../../../fragments/drawer-list/__tests__/DrawerListTestMocks'
import {
- DrawerListDataObject,
- DrawerListDataObjectUnion,
+ DrawerListDataArrayObject,
+ DrawerListDataArray,
} from '../../../fragments/drawer-list'
// use no_animation so we don't need to wait
@@ -28,7 +28,7 @@ const props: DropdownAllProps = {
no_animation: true,
}
-const mockData: DrawerListDataObjectUnion[] = [
+const mockData: DrawerListDataArray = [
{
selected_value: 'Brukskonto - Kari Nordmann',
content: ['1234 56 78901', 'Brukskonto - Kari Nordmann'],
@@ -72,7 +72,9 @@ describe('Dropdown component', () => {
expect(
document.querySelector('.dnb-dropdown__text__inner').textContent
- ).toBe((mockData[props.value] as DrawerListDataObject).selected_value)
+ ).toBe(
+ (mockData[props.value] as DrawerListDataArrayObject).selected_value
+ )
keydown(32) // space
@@ -96,7 +98,7 @@ describe('Dropdown component', () => {
expect(
document.querySelector('.dnb-dropdown__text__inner').textContent
).toBe(
- (mockData[(props.value as number) + 1] as DrawerListDataObject)
+ (mockData[(props.value as number) + 1] as DrawerListDataArrayObject)
.selected_value
)
})
@@ -507,7 +509,7 @@ describe('Dropdown component', () => {
rerender( )
expect(document.querySelector('.dnb-dropdown__text').textContent).toBe(
- (mockData[value] as DrawerListDataObject).selected_value
+ (mockData[value] as DrawerListDataArrayObject).selected_value
)
rerender( )
@@ -520,7 +522,7 @@ describe('Dropdown component', () => {
rerender( )
expect(document.querySelector('.dnb-dropdown__text').textContent).toBe(
- (mockData[value] as DrawerListDataObject).selected_value
+ (mockData[value] as DrawerListDataArrayObject).selected_value
)
rerender( )
@@ -732,6 +734,7 @@ describe('Dropdown component', () => {
data: {
__id: 0,
content: 'English',
+ selectedKey: 'en-GB',
selected_key: 'en-GB',
type: 'object',
value: 'en-GB',
@@ -750,6 +753,7 @@ describe('Dropdown component', () => {
isTrusted: false,
data: {
content: 'Norsk',
+ selectedKey: 'nb-NO',
selected_key: 'nb-NO',
type: 'object',
value: 'nb-NO',
@@ -1071,7 +1075,7 @@ describe('Dropdown component', () => {
expect(
document.querySelector('.dnb-dropdown__text__inner').textContent
).toBe(
- (mockData[(props.value as number) + 1] as DrawerListDataObject)
+ (mockData[(props.value as number) + 1] as DrawerListDataArrayObject)
.selected_value
)
})
@@ -1080,7 +1084,9 @@ describe('Dropdown component', () => {
render( )
expect(
document.querySelector('.dnb-dropdown__text__inner').textContent
- ).toBe((mockData[props.value] as DrawerListDataObject).selected_value)
+ ).toBe(
+ (mockData[props.value] as DrawerListDataArrayObject).selected_value
+ )
})
it('has correct selected value after new selection', () => {
@@ -1092,7 +1098,9 @@ describe('Dropdown component', () => {
expect(
document.querySelector('.dnb-dropdown__text__inner').textContent
- ).toBe((mockData[props.value] as DrawerListDataObject).selected_value)
+ ).toBe(
+ (mockData[props.value] as DrawerListDataArrayObject).selected_value
+ )
})
it('has correct value after useEffect value state change', () => {
@@ -1111,7 +1119,9 @@ describe('Dropdown component', () => {
expect(
document.querySelector('.dnb-dropdown__text__inner').textContent
- ).toBe((mockData[newValue] as DrawerListDataObject).selected_value)
+ ).toBe(
+ (mockData[newValue] as DrawerListDataArrayObject).selected_value
+ )
open()
diff --git a/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-different-item-directions.snap.png b/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-different-item-directions.snap.png
index eaead75948e..cc62ec8745e 100644
Binary files a/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-different-item-directions.snap.png and b/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-different-item-directions.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-disabled-options.snap.png b/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-disabled-options.snap.png
new file mode 100644
index 00000000000..0e6da3f6953
Binary files /dev/null and b/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-disabled-options.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-dropdown-action-menu-in-mobile-view.snap.png b/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-dropdown-action-menu-in-mobile-view.snap.png
index 5ba5ce5c70e..331d702686a 100644
Binary files a/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-dropdown-action-menu-in-mobile-view.snap.png and b/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-dropdown-action-menu-in-mobile-view.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-dropdown-action-menu-with-custom-items.snap.png b/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-dropdown-action-menu-with-custom-items.snap.png
index a07f65b4717..2487fb300cc 100644
Binary files a/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-dropdown-action-menu-with-custom-items.snap.png and b/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-dropdown-action-menu-with-custom-items.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-dropdown-as-more-menu-opened-on-left-side.snap.png b/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-dropdown-as-more-menu-opened-on-left-side.snap.png
index b6c6a11535a..f01a9690d4a 100644
Binary files a/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-dropdown-as-more-menu-opened-on-left-side.snap.png and b/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-dropdown-as-more-menu-opened-on-left-side.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-dropdown-as-more-menu-opened-on-right-side.snap.png b/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-dropdown-as-more-menu-opened-on-right-side.snap.png
index 1751fcffa0c..c1aced1d071 100644
Binary files a/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-dropdown-as-more-menu-opened-on-right-side.snap.png and b/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-dropdown-as-more-menu-opened-on-right-side.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-dropdown-items.snap.png b/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-dropdown-items.snap.png
index 28831c65b8f..63083bbe130 100644
Binary files a/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-dropdown-items.snap.png and b/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-dropdown-items.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-tertiary-variant-opened-on-left-side.snap.png b/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-tertiary-variant-opened-on-left-side.snap.png
index 2fb167961e4..e89b1847501 100644
Binary files a/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-tertiary-variant-opened-on-left-side.snap.png and b/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-tertiary-variant-opened-on-left-side.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-tertiary-variant-opened-on-right-side.snap.png b/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-tertiary-variant-opened-on-right-side.snap.png
index ee87421f9f3..5530e7253e2 100644
Binary files a/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-tertiary-variant-opened-on-right-side.snap.png and b/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-sbanken-have-to-match-the-tertiary-variant-opened-on-right-side.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-ui-have-to-match-disabled-options.snap.png b/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-ui-have-to-match-disabled-options.snap.png
new file mode 100644
index 00000000000..e0d428ebbfb
Binary files /dev/null and b/packages/dnb-eufemia/src/components/dropdown/__tests__/__image_snapshots__/dropdown-for-ui-have-to-match-disabled-options.snap.png differ
diff --git a/packages/dnb-eufemia/src/components/dropdown/stories/Dropdown.stories.tsx b/packages/dnb-eufemia/src/components/dropdown/stories/Dropdown.stories.tsx
index 69e0644343b..e2ff853fd2e 100644
--- a/packages/dnb-eufemia/src/components/dropdown/stories/Dropdown.stories.tsx
+++ b/packages/dnb-eufemia/src/components/dropdown/stories/Dropdown.stories.tsx
@@ -20,7 +20,7 @@ import {
GlobalStatus,
} from '../..'
import { Flex, Link } from '../../..'
-import { DrawerListDataObjectUnion } from '../../../fragments/DrawerList'
+import { DrawerListDataArray } from '../../../fragments/DrawerList'
import { Provider } from '../../../shared'
export default {
@@ -628,7 +628,7 @@ export const DropdownSandbox = () => (
)
-let dropdownData: DrawerListDataObjectUnion[] = [
+let dropdownData: DrawerListDataArray = [
{
selected_value: 'Brukskonto - Kari Nordmann',
content: (
diff --git a/packages/dnb-eufemia/src/components/form-status/__tests__/FormStatus.test.tsx b/packages/dnb-eufemia/src/components/form-status/__tests__/FormStatus.test.tsx
index 5c55607b3a2..b308a81cbd2 100644
--- a/packages/dnb-eufemia/src/components/form-status/__tests__/FormStatus.test.tsx
+++ b/packages/dnb-eufemia/src/components/form-status/__tests__/FormStatus.test.tsx
@@ -41,6 +41,16 @@ describe('FormStatus component', () => {
).toContain('max-width: 30rem;')
})
+ it('should have correct icon label', () => {
+ render( )
+ expect(
+ document.querySelector('[role="presentation"]')
+ ).toHaveAttribute('data-testid', 'ErrorIcon icon')
+ expect(
+ document.querySelector('.dnb-form-status__text')
+ ).toHaveTextContent('Error message')
+ })
+
it('should re-calculate max-width', () => {
const { rerender } = render(
diff --git a/packages/dnb-eufemia/src/components/form-status/stories/FormStatus.stories.tsx b/packages/dnb-eufemia/src/components/form-status/stories/FormStatus.stories.tsx
index b64100525a0..266c1fa5a5e 100644
--- a/packages/dnb-eufemia/src/components/form-status/stories/FormStatus.stories.tsx
+++ b/packages/dnb-eufemia/src/components/form-status/stories/FormStatus.stories.tsx
@@ -25,7 +25,7 @@ import {
} from '../..'
import { Link } from '../../..'
import { format } from '../../number-format/NumberUtils'
-import { DrawerListDataObject } from '../../../fragments/DrawerList'
+import { DrawerListDataArray } from '../../../fragments/DrawerList'
export default {
title: 'Eufemia/Components/FormStatus',
@@ -228,7 +228,7 @@ export const GlobalStatusExample = () => {
export const SuffixAndStretchedStatus = () => {
const ban = format(21001234567, { ban: true }) as string
- const numbers: DrawerListDataObject[] = [
+ const numbers: DrawerListDataArray = [
{
selected_value: `Brukskonto (${ban})`,
suffix_value: (
diff --git a/packages/dnb-eufemia/src/components/global-status/__tests__/__snapshots__/GlobalStatus.test.tsx.snap b/packages/dnb-eufemia/src/components/global-status/__tests__/__snapshots__/GlobalStatus.test.tsx.snap
index da959a36d6b..0d289889976 100644
--- a/packages/dnb-eufemia/src/components/global-status/__tests__/__snapshots__/GlobalStatus.test.tsx.snap
+++ b/packages/dnb-eufemia/src/components/global-status/__tests__/__snapshots__/GlobalStatus.test.tsx.snap
@@ -624,9 +624,19 @@ button.dnb-button::-moz-focus-inner {
.dnb-section:not([style*="--breakout"]) {
--breakout: var(--breakout--on);
}
+.dnb-section[style*="--outset"].dnb-space[style]:not(.dnb-card) {
+ padding-left: calc(var(--padding-left) * (1 - var(--outset)));
+ padding-right: calc(var(--padding-right) * (1 - var(--outset)));
+}
+.dnb-section[style*="--outset"]::before {
+ margin-left: calc(var(--outset-left, var(--padding-left)) * -1 * var(--outset));
+ margin-right: calc(var(--outset-right, var(--padding-right)) * -1 * var(--outset));
+ background-color: inherit;
+}
@media screen and (max-width: 60em) {
.dnb-section {
--breakout: var(--breakout--small, var(--breakout--fallback));
+ --outset: var(--outset--small, var(--outset--fallback));
--background-color--value: var(--background-color--small);
--text-color--value: var(--text-color--small);
--outline-color: var(--outline-color--small);
@@ -640,6 +650,7 @@ button.dnb-button::-moz-focus-inner {
@media screen and (max-width: 60em) and (min-width: 40.00625em) {
.dnb-section {
--breakout: var(--breakout--medium, var(--breakout--fallback));
+ --outset: var(--outset--medium, var(--outset--fallback));
--background-color--value: var(--background-color--medium);
--text-color--value: var(--text-color--medium);
--outline-color: var(--outline-color--medium);
@@ -653,6 +664,7 @@ button.dnb-button::-moz-focus-inner {
@media screen and (min-width: 60.00625em) {
.dnb-section {
--breakout: var(--breakout--large, var(--breakout--fallback));
+ --outset: var(--outset--large, var(--outset--fallback));
--background-color--value: var(--background-color--large);
--text-color--value: var(--text-color--large);
--outline-color: var(--outline-color--large);
diff --git a/packages/dnb-eufemia/src/components/help-button/HelpButtonInline.tsx b/packages/dnb-eufemia/src/components/help-button/HelpButtonInline.tsx
index cfdebcdcd73..fa3b5c0d3c7 100644
--- a/packages/dnb-eufemia/src/components/help-button/HelpButtonInline.tsx
+++ b/packages/dnb-eufemia/src/components/help-button/HelpButtonInline.tsx
@@ -22,6 +22,8 @@ export type HelpProps = {
open?: boolean
/** Only for the "inline" variant */
breakout?: boolean
+ /** Only for the "inline" variant */
+ outset?: boolean
}
export type HelpButtonInlineProps = HelpButtonProps & {
@@ -97,6 +99,7 @@ export type HelpButtonInlineContentProps = SpacingProps & {
children?: React.ReactNode
help?: HelpProps
breakout?: boolean
+ outset?: boolean
}
export function HelpButtonInlineContent(
@@ -108,6 +111,7 @@ export function HelpButtonInlineContent(
children,
help: helpProp,
breakout = true,
+ outset = true,
...rest
} = props
const { data, update } =
@@ -119,12 +123,14 @@ export function HelpButtonInlineContent(
content,
renderAs,
breakout: breakoutProp = true,
+ outset: outsetProp = true,
} = helpProp || {}
const innerRef = useRef(null)
const cardContext = useContext(CardContext)
const breakoutFromLayout =
Boolean(cardContext) && breakout && breakoutProp
+ const outsetFromLayout = outset && outsetProp
useEffect(() => {
if (isOpen && isUserIntent) {
@@ -187,6 +193,7 @@ export function HelpButtonInlineContent(
tabIndex={-1}
innerRef={innerRef}
onKeyDown={onKeyDown}
+ outset={outsetFromLayout}
breakout={breakoutFromLayout}
roundedCorner={!breakoutFromLayout}
innerSpace={
@@ -203,7 +210,7 @@ export function HelpButtonInlineContent(
{...rest}
>
- {title && {title}
}
+ {title && {title}
}
{content && {content}
}
{children}
diff --git a/packages/dnb-eufemia/src/components/help-button/__tests__/__snapshots__/HelpButton.test.tsx.snap b/packages/dnb-eufemia/src/components/help-button/__tests__/__snapshots__/HelpButton.test.tsx.snap
index 787af0cfc6e..27a9013f20a 100644
--- a/packages/dnb-eufemia/src/components/help-button/__tests__/__snapshots__/HelpButton.test.tsx.snap
+++ b/packages/dnb-eufemia/src/components/help-button/__tests__/__snapshots__/HelpButton.test.tsx.snap
@@ -663,11 +663,12 @@ button.dnb-button::-moz-focus-inner {
transform: translate3d(0, -0.5rem, 0);
}
:not(.dnb-card) .dnb-help-button__content .dnb-section {
- margin-left: calc(var(--help-button-indent-width) * -1);
- margin-right: calc(var(--help-button-indent-width) * -1);
- border: var(--help-button-indent-width) solid var(--help-button-content-background);
- border-top: none;
- border-bottom: none;
+ --outset-left: calc(
+ var(--spacing-medium) + var(--help-button-indent-width)
+ );
+ --outset-right: calc(
+ var(--spacing-medium) + var(--help-button-indent-width)
+ );
}
.dnb-help-button__content.dnb-height-animation--parallax .dnb-section .dnb-p {
transform: translate3d(0, 0, 0);
diff --git a/packages/dnb-eufemia/src/components/help-button/style/dnb-help-button-inline.scss b/packages/dnb-eufemia/src/components/help-button/style/dnb-help-button-inline.scss
index 89353661fc5..b0e8ef6e1de 100644
--- a/packages/dnb-eufemia/src/components/help-button/style/dnb-help-button-inline.scss
+++ b/packages/dnb-eufemia/src/components/help-button/style/dnb-help-button-inline.scss
@@ -109,17 +109,12 @@
--help-button-indent-width: var(--card-outline-width);
:not(.dnb-card) & .dnb-section {
- // Because no outline, we need to stretch the content to the left and right,
- // so it is aligned on how the Card is displayed.
- margin-left: calc(var(--help-button-indent-width) * -1);
- margin-right: calc(var(--help-button-indent-width) * -1);
-
- // Use a border instead of the original outline,
- // because is has no animation artifacts.
- border: var(--help-button-indent-width) solid
- var(--help-button-content-background);
- border-top: none;
- border-bottom: none;
+ --outset-left: calc(
+ var(--spacing-medium) + var(--help-button-indent-width)
+ );
+ --outset-right: calc(
+ var(--spacing-medium) + var(--help-button-indent-width)
+ );
}
&.dnb-height-animation--parallax .dnb-section .dnb-p {
diff --git a/packages/dnb-eufemia/src/components/icon/Icon.tsx b/packages/dnb-eufemia/src/components/icon/Icon.tsx
index b8fd1b1ff52..054d5e786ea 100644
--- a/packages/dnb-eufemia/src/components/icon/Icon.tsx
+++ b/packages/dnb-eufemia/src/components/icon/Icon.tsx
@@ -157,6 +157,9 @@ export default function Icon(localProps: IconAllProps) {
}
export function getIconNameFromComponent(icon: IconProps['icon']): string {
+ if (React.isValidElement(icon) && icon?.type) {
+ icon = icon?.type as IconType
+ }
const name = typeof icon === 'function' ? icon.name : String(icon)
if (/^data:image\//.test(name)) {
return null
diff --git a/packages/dnb-eufemia/src/components/modal/__tests__/__snapshots__/Modal.test.tsx.snap b/packages/dnb-eufemia/src/components/modal/__tests__/__snapshots__/Modal.test.tsx.snap
index 3472c02b451..168de6ea3ba 100644
--- a/packages/dnb-eufemia/src/components/modal/__tests__/__snapshots__/Modal.test.tsx.snap
+++ b/packages/dnb-eufemia/src/components/modal/__tests__/__snapshots__/Modal.test.tsx.snap
@@ -670,11 +670,12 @@ button.dnb-button::-moz-focus-inner {
transform: translate3d(0, -0.5rem, 0);
}
:not(.dnb-card) .dnb-help-button__content .dnb-section {
- margin-left: calc(var(--help-button-indent-width) * -1);
- margin-right: calc(var(--help-button-indent-width) * -1);
- border: var(--help-button-indent-width) solid var(--help-button-content-background);
- border-top: none;
- border-bottom: none;
+ --outset-left: calc(
+ var(--spacing-medium) + var(--help-button-indent-width)
+ );
+ --outset-right: calc(
+ var(--spacing-medium) + var(--help-button-indent-width)
+ );
}
.dnb-help-button__content.dnb-height-animation--parallax .dnb-section .dnb-p {
transform: translate3d(0, 0, 0);
diff --git a/packages/dnb-eufemia/src/components/modal/parts/ModalHeader.tsx b/packages/dnb-eufemia/src/components/modal/parts/ModalHeader.tsx
index 85515d2eb9b..4c89c573a2b 100644
--- a/packages/dnb-eufemia/src/components/modal/parts/ModalHeader.tsx
+++ b/packages/dnb-eufemia/src/components/modal/parts/ModalHeader.tsx
@@ -33,7 +33,8 @@ export interface ModalHeaderProps extends Omit {
title_class?: string
/**
- * Font size of the title (maps to `dnb-p--`)
+ * Font size of the title (maps to `dnb-h--`)
+ * Default is `large`
*/
size?: 'medium' | 'large' | 'x-large' | 'xx-large'
}
diff --git a/packages/dnb-eufemia/src/components/section/Section.tsx b/packages/dnb-eufemia/src/components/section/Section.tsx
index f1956f4866a..b4a690148ae 100644
--- a/packages/dnb-eufemia/src/components/section/Section.tsx
+++ b/packages/dnb-eufemia/src/components/section/Section.tsx
@@ -70,6 +70,12 @@ export type SectionProps = {
*/
breakout?: boolean | ResponsiveProp
+ /**
+ * Define if the Card should break out negatively on larger screens. You can not use `breakout` and `outset` together.
+ * Defaults to `false`
+ */
+ outset?: boolean | ResponsiveProp
+
/**
* Define if the section should have rounded corners. Defaults to `false`.
*/
@@ -152,7 +158,8 @@ export function SectionParams(
const {
variant,
- breakout = true,
+ breakout = !props.outset,
+ outset,
roundedCorner,
textColor,
backgroundColor,
@@ -188,6 +195,7 @@ export function SectionParams(
'breakout',
(value) => `var(--breakout--${value ? 'on' : 'off'})`
),
+ ...computeStyle(outset, 'outset', (value) => (value ? '1' : '0')),
...computeStyle(
roundedCorner,
'rounded-corner',
diff --git a/packages/dnb-eufemia/src/components/section/SectionDocs.ts b/packages/dnb-eufemia/src/components/section/SectionDocs.ts
new file mode 100644
index 00000000000..be479426af2
--- /dev/null
+++ b/packages/dnb-eufemia/src/components/section/SectionDocs.ts
@@ -0,0 +1,59 @@
+import { PropertiesTableProps } from '../../shared/types'
+
+export const SectionProperties: PropertiesTableProps = {
+ variant: {
+ doc: 'Defines the semantic purpose and subsequently the style of the visual helper. Will take precedence over the style_type property.',
+ type: 'string',
+ status: 'optional',
+ },
+ breakout: {
+ doc: 'Use `true` to enable a fullscreen breakout look. Supports also media query breakpoints like `{ small: boolean }`. Defaults to `true`.',
+ type: 'boolean',
+ status: 'optional',
+ },
+ outset: {
+ doc: 'Define if the Card should break out negatively on larger screens. You can not use `breakout` and `outset` together. Defaults to `false`.',
+ type: 'boolean',
+ status: 'optional',
+ },
+ outline: {
+ doc: "Define a custom border color. If `true` is given, `color-black-8` is used. Use a Eufemia color. Supports also media query breakpoints like `{ small: 'black-8' }`.",
+ type: 'string',
+ status: 'optional',
+ },
+ roundedCorner: {
+ doc: 'Use `true` to enable rounded corners (border-radius). Supports also media query breakpoints like `{ small: boolean }`. Defaults to `false`.',
+ type: 'boolean',
+ status: 'optional',
+ },
+ backgroundColor: {
+ doc: "Define a custom background color, instead of a variant. Use a Eufemia color. Supports also media query breakpoints like `{ small: 'white' }`.",
+ type: 'string',
+ status: 'optional',
+ },
+ dropShadow: {
+ doc: 'Use `true` to show the default Eufemia DropShadow. Supports also media query breakpoints like `{ small: true }`.',
+ type: 'boolean',
+ status: 'optional',
+ },
+ textColor: {
+ doc: "Define a custom text color to compliment the backgroundColor. Use a Eufemia color. Supports also media query breakpoints like `{ small: 'black-80' }`.",
+ type: 'string',
+ status: 'optional',
+ },
+ innerSpace: {
+ doc: "Will add a padding around the content. Supports also media query breakpoints like `{small: { top: 'medium' }}`.",
+ type: 'string',
+ status: 'optional',
+ },
+ innerRef: {
+ doc: 'By providing a React Ref we can get the internally used element (DOM). E.g. `inner_ref={myRef}` by using `React.createRef()` or `React.useRef()`.',
+ type: 'React.Ref',
+ status: 'optional',
+ },
+ '[Space](/uilib/layout/space/properties)': {
+ doc: 'Spacing properties like `top` or `bottom` are supported.',
+ type: ['string', 'object'],
+ status: 'optional',
+ },
+}
diff --git a/packages/dnb-eufemia/src/components/section/__tests__/__snapshots__/Section.test.tsx.snap b/packages/dnb-eufemia/src/components/section/__tests__/__snapshots__/Section.test.tsx.snap
index 19b393353c0..f1cb2dd754e 100644
--- a/packages/dnb-eufemia/src/components/section/__tests__/__snapshots__/Section.test.tsx.snap
+++ b/packages/dnb-eufemia/src/components/section/__tests__/__snapshots__/Section.test.tsx.snap
@@ -61,9 +61,19 @@ exports[`Section scss has to match style dependencies css 1`] = `
.dnb-section:not([style*="--breakout"]) {
--breakout: var(--breakout--on);
}
+.dnb-section[style*="--outset"].dnb-space[style]:not(.dnb-card) {
+ padding-left: calc(var(--padding-left) * (1 - var(--outset)));
+ padding-right: calc(var(--padding-right) * (1 - var(--outset)));
+}
+.dnb-section[style*="--outset"]::before {
+ margin-left: calc(var(--outset-left, var(--padding-left)) * -1 * var(--outset));
+ margin-right: calc(var(--outset-right, var(--padding-right)) * -1 * var(--outset));
+ background-color: inherit;
+}
@media screen and (max-width: 60em) {
.dnb-section {
--breakout: var(--breakout--small, var(--breakout--fallback));
+ --outset: var(--outset--small, var(--outset--fallback));
--background-color--value: var(--background-color--small);
--text-color--value: var(--text-color--small);
--outline-color: var(--outline-color--small);
@@ -77,6 +87,7 @@ exports[`Section scss has to match style dependencies css 1`] = `
@media screen and (max-width: 60em) and (min-width: 40.00625em) {
.dnb-section {
--breakout: var(--breakout--medium, var(--breakout--fallback));
+ --outset: var(--outset--medium, var(--outset--fallback));
--background-color--value: var(--background-color--medium);
--text-color--value: var(--text-color--medium);
--outline-color: var(--outline-color--medium);
@@ -90,6 +101,7 @@ exports[`Section scss has to match style dependencies css 1`] = `
@media screen and (min-width: 60.00625em) {
.dnb-section {
--breakout: var(--breakout--large, var(--breakout--fallback));
+ --outset: var(--outset--large, var(--outset--fallback));
--background-color--value: var(--background-color--large);
--text-color--value: var(--text-color--large);
--outline-color: var(--outline-color--large);
diff --git a/packages/dnb-eufemia/src/components/section/style/dnb-section.scss b/packages/dnb-eufemia/src/components/section/style/dnb-section.scss
index 13d6e0f7d03..ef75d829e5c 100644
--- a/packages/dnb-eufemia/src/components/section/style/dnb-section.scss
+++ b/packages/dnb-eufemia/src/components/section/style/dnb-section.scss
@@ -67,8 +67,28 @@
--breakout: var(--breakout--on);
}
+ &[style*='--outset'] {
+ &.dnb-space[style]:not(.dnb-card) {
+ padding-left: calc(var(--padding-left) * calc(1 - var(--outset)));
+ padding-right: calc(var(--padding-right) * calc(1 - var(--outset)));
+ }
+
+ &::before {
+ margin-left: calc(
+ var(--outset-left, var(--padding-left)) * -1 * var(--outset)
+ );
+ margin-right: calc(
+ var(--outset-right, var(--padding-right)) * -1 * var(--outset)
+ );
+
+ // Because of the margin usage, we need to inherit the background color.
+ background-color: inherit;
+ }
+ }
+
@include allBelow(medium) {
--breakout: var(--breakout--small, var(--breakout--fallback));
+ --outset: var(--outset--small, var(--outset--fallback));
--background-color--value: var(--background-color--small);
--text-color--value: var(--text-color--small);
--outline-color: var(--outline-color--small);
@@ -80,6 +100,7 @@
}
@include allBetween(small, medium) {
--breakout: var(--breakout--medium, var(--breakout--fallback));
+ --outset: var(--outset--medium, var(--outset--fallback));
--background-color--value: var(--background-color--medium);
--text-color--value: var(--text-color--medium);
--outline-color: var(--outline-color--medium);
@@ -91,6 +112,7 @@
}
@include allAbove(medium) {
--breakout: var(--breakout--large, var(--breakout--fallback));
+ --outset: var(--outset--large, var(--outset--fallback));
--background-color--value: var(--background-color--large);
--text-color--value: var(--text-color--large);
--outline-color: var(--outline-color--large);
diff --git a/packages/dnb-eufemia/src/components/skip-content/__tests__/__snapshots__/SkipContent.test.tsx.snap b/packages/dnb-eufemia/src/components/skip-content/__tests__/__snapshots__/SkipContent.test.tsx.snap
index df8d760e814..2b8b3f653b1 100644
--- a/packages/dnb-eufemia/src/components/skip-content/__tests__/__snapshots__/SkipContent.test.tsx.snap
+++ b/packages/dnb-eufemia/src/components/skip-content/__tests__/__snapshots__/SkipContent.test.tsx.snap
@@ -624,9 +624,19 @@ button.dnb-button::-moz-focus-inner {
.dnb-section:not([style*="--breakout"]) {
--breakout: var(--breakout--on);
}
+.dnb-section[style*="--outset"].dnb-space[style]:not(.dnb-card) {
+ padding-left: calc(var(--padding-left) * (1 - var(--outset)));
+ padding-right: calc(var(--padding-right) * (1 - var(--outset)));
+}
+.dnb-section[style*="--outset"]::before {
+ margin-left: calc(var(--outset-left, var(--padding-left)) * -1 * var(--outset));
+ margin-right: calc(var(--outset-right, var(--padding-right)) * -1 * var(--outset));
+ background-color: inherit;
+}
@media screen and (max-width: 60em) {
.dnb-section {
--breakout: var(--breakout--small, var(--breakout--fallback));
+ --outset: var(--outset--small, var(--outset--fallback));
--background-color--value: var(--background-color--small);
--text-color--value: var(--text-color--small);
--outline-color: var(--outline-color--small);
@@ -640,6 +650,7 @@ button.dnb-button::-moz-focus-inner {
@media screen and (max-width: 60em) and (min-width: 40.00625em) {
.dnb-section {
--breakout: var(--breakout--medium, var(--breakout--fallback));
+ --outset: var(--outset--medium, var(--outset--fallback));
--background-color--value: var(--background-color--medium);
--text-color--value: var(--text-color--medium);
--outline-color: var(--outline-color--medium);
@@ -653,6 +664,7 @@ button.dnb-button::-moz-focus-inner {
@media screen and (min-width: 60.00625em) {
.dnb-section {
--breakout: var(--breakout--large, var(--breakout--fallback));
+ --outset: var(--outset--large, var(--outset--fallback));
--background-color--value: var(--background-color--large);
--text-color--value: var(--text-color--large);
--outline-color: var(--outline-color--large);
diff --git a/packages/dnb-eufemia/src/core/jest/jestSetupScreenshots.css b/packages/dnb-eufemia/src/core/jest/jestSetupScreenshots.css
index c2aeee189ba..56a623dcfba 100644
--- a/packages/dnb-eufemia/src/core/jest/jestSetupScreenshots.css
+++ b/packages/dnb-eufemia/src/core/jest/jestSetupScreenshots.css
@@ -35,9 +35,19 @@ html {
background: #fff;
}
+[data-visual-test]:has(
+ .dnb-section[style*='--outset--medium: 1'],
+ .dnb-section[style*='--outset--large: 1']
+ ) {
+ @media (min-width: 40em) {
+ padding-left: 1.5rem;
+ padding-right: 1.5rem;
+ }
+}
+
/**
* Hide "Preparing requested page" popup
*/
gatsby-qod {
opacity: 0;
-}
\ No newline at end of file
+}
diff --git a/packages/dnb-eufemia/src/elements/span/Span.tsx b/packages/dnb-eufemia/src/elements/span/Span.tsx
index 4ad41011aea..a5fec218e1b 100644
--- a/packages/dnb-eufemia/src/elements/span/Span.tsx
+++ b/packages/dnb-eufemia/src/elements/span/Span.tsx
@@ -4,13 +4,12 @@
*/
import React from 'react'
-import { SpacingProps } from '../../components/space/types'
-import E from '../Element'
+import Typography, { TypographyProps } from '../typography/Typography'
-type SpanProps = SpacingProps & React.HTMLAttributes
+type SpanProps = TypographyProps
const Span = React.forwardRef((props: SpanProps, ref) => (
-
+
))
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
diff --git a/packages/dnb-eufemia/src/elements/span/SpanDocs.ts b/packages/dnb-eufemia/src/elements/span/SpanDocs.ts
new file mode 100644
index 00000000000..428cfb3358a
--- /dev/null
+++ b/packages/dnb-eufemia/src/elements/span/SpanDocs.ts
@@ -0,0 +1,6 @@
+import { PropertiesTableProps } from '../../shared/types'
+import { TypographyProperties } from '../typography/TypographyDocs'
+
+export const SpanProperties: PropertiesTableProps = {
+ ...TypographyProperties,
+}
diff --git a/packages/dnb-eufemia/src/elements/span/__tests__/Span.screenshot.test.ts b/packages/dnb-eufemia/src/elements/span/__tests__/Span.screenshot.test.ts
new file mode 100644
index 00000000000..8c216ae26a4
--- /dev/null
+++ b/packages/dnb-eufemia/src/elements/span/__tests__/Span.screenshot.test.ts
@@ -0,0 +1,37 @@
+/**
+ * Screenshot Test
+ * This file will not run on "test:staged" because we don't require any related files
+ */
+
+import {
+ makeScreenshot,
+ setupPageScreenshot,
+} from '../../../core/jest/jestSetupScreenshots'
+
+describe.each(['ui', 'sbanken'])('Span for %s', (themeName) => {
+ setupPageScreenshot({
+ themeName,
+ url: '/uilib/elements/span',
+ })
+
+ it('basics', async () => {
+ const screenshot = await makeScreenshot({
+ selector: '[data-visual-test="span-basic"]',
+ })
+ expect(screenshot).toMatchImageSnapshot()
+ })
+
+ it('with modifiers', async () => {
+ const screenshot = await makeScreenshot({
+ selector: '[data-visual-test="span-modifiers"]',
+ })
+ expect(screenshot).toMatchImageSnapshot()
+ })
+
+ it('all sizes and weights', async () => {
+ const screenshot = await makeScreenshot({
+ selector: '[data-visual-test="span-sizes"]',
+ })
+ expect(screenshot).toMatchImageSnapshot()
+ })
+})
diff --git a/packages/dnb-eufemia/src/elements/span/__tests__/Span.test.tsx b/packages/dnb-eufemia/src/elements/span/__tests__/Span.test.tsx
new file mode 100644
index 00000000000..efe4ec62544
--- /dev/null
+++ b/packages/dnb-eufemia/src/elements/span/__tests__/Span.test.tsx
@@ -0,0 +1,66 @@
+/**
+ * Element Test
+ *
+ */
+
+import React from 'react'
+import { axeComponent } from '../../../core/jest/jestSetup'
+import Span from '../Span'
+import { render } from '@testing-library/react'
+
+describe('Span element', () => {
+ it('size also sets line-height when not defined', () => {
+ render( )
+ const element = document.querySelector('.dnb-t__size--large')
+
+ expect(Array.from(element.classList)).toEqual([
+ 'dnb-t__line-height--large',
+ 'dnb-t__size--large',
+ 'dnb-span',
+ ])
+ })
+ it('sets only line-height when size is not defined', () => {
+ render( )
+ const element = document.querySelector('.dnb-t__line-height--large')
+
+ expect(Array.from(element.classList)).toEqual([
+ 'dnb-t__line-height--large',
+ 'dnb-span',
+ ])
+ })
+ it('has correct style when several modifiers are defined', () => {
+ render(
+
+ )
+ const element = document.querySelector('.dnb-t__size--small')
+
+ expect(Array.from(element.classList)).toEqual([
+ 'dnb-t__line-height--xx-large',
+ 'dnb-t__size--small',
+ 'dnb-t__align--center',
+ 'dnb-t__family--monospace',
+ 'dnb-t__weight--medium',
+ 'dnb-t__decoration--underline',
+ 'dnb-span',
+ ])
+ })
+ it('has correct style when medium is set to true', () => {
+ render( )
+ const element = document.querySelector('.dnb-t__weight--bold')
+ expect(Array.from(element.classList)).toEqual([
+ 'dnb-t__weight--bold',
+ 'dnb-span',
+ ])
+ })
+ it('should validate with ARIA rules as a span element', async () => {
+ const Comp = render( )
+ expect(await axeComponent(Comp)).toHaveNoViolations()
+ })
+})
diff --git a/packages/dnb-eufemia/src/elements/span/__tests__/__image_snapshots__/span-for-sbanken-all-sizes-and-weights.snap.png b/packages/dnb-eufemia/src/elements/span/__tests__/__image_snapshots__/span-for-sbanken-all-sizes-and-weights.snap.png
new file mode 100644
index 00000000000..6efdd4f0d94
Binary files /dev/null and b/packages/dnb-eufemia/src/elements/span/__tests__/__image_snapshots__/span-for-sbanken-all-sizes-and-weights.snap.png differ
diff --git a/packages/dnb-eufemia/src/elements/span/__tests__/__image_snapshots__/span-for-sbanken-basics.snap.png b/packages/dnb-eufemia/src/elements/span/__tests__/__image_snapshots__/span-for-sbanken-basics.snap.png
new file mode 100644
index 00000000000..a1e093ad1dc
Binary files /dev/null and b/packages/dnb-eufemia/src/elements/span/__tests__/__image_snapshots__/span-for-sbanken-basics.snap.png differ
diff --git a/packages/dnb-eufemia/src/elements/span/__tests__/__image_snapshots__/span-for-sbanken-with-modifiers.snap.png b/packages/dnb-eufemia/src/elements/span/__tests__/__image_snapshots__/span-for-sbanken-with-modifiers.snap.png
new file mode 100644
index 00000000000..de5d835369a
Binary files /dev/null and b/packages/dnb-eufemia/src/elements/span/__tests__/__image_snapshots__/span-for-sbanken-with-modifiers.snap.png differ
diff --git a/packages/dnb-eufemia/src/elements/span/__tests__/__image_snapshots__/span-for-ui-all-sizes-and-weights.snap.png b/packages/dnb-eufemia/src/elements/span/__tests__/__image_snapshots__/span-for-ui-all-sizes-and-weights.snap.png
new file mode 100644
index 00000000000..a68410918a1
Binary files /dev/null and b/packages/dnb-eufemia/src/elements/span/__tests__/__image_snapshots__/span-for-ui-all-sizes-and-weights.snap.png differ
diff --git a/packages/dnb-eufemia/src/elements/span/__tests__/__image_snapshots__/span-for-ui-basics.snap.png b/packages/dnb-eufemia/src/elements/span/__tests__/__image_snapshots__/span-for-ui-basics.snap.png
new file mode 100644
index 00000000000..dbe71beac53
Binary files /dev/null and b/packages/dnb-eufemia/src/elements/span/__tests__/__image_snapshots__/span-for-ui-basics.snap.png differ
diff --git a/packages/dnb-eufemia/src/elements/span/__tests__/__image_snapshots__/span-for-ui-with-modifiers.snap.png b/packages/dnb-eufemia/src/elements/span/__tests__/__image_snapshots__/span-for-ui-with-modifiers.snap.png
new file mode 100644
index 00000000000..10037147b5e
Binary files /dev/null and b/packages/dnb-eufemia/src/elements/span/__tests__/__image_snapshots__/span-for-ui-with-modifiers.snap.png differ
diff --git a/packages/dnb-eufemia/src/elements/typography/Ingress.tsx b/packages/dnb-eufemia/src/elements/typography/Ingress.tsx
index 60980b3ef38..35fe9d48c17 100644
--- a/packages/dnb-eufemia/src/elements/typography/Ingress.tsx
+++ b/packages/dnb-eufemia/src/elements/typography/Ingress.tsx
@@ -5,7 +5,7 @@
import React from 'react'
import P, { PProps } from './P'
-const Ingress = (props: PProps) =>
+const Ingress = (props: PProps) =>
Ingress._supportsSpacingProps = true
diff --git a/packages/dnb-eufemia/src/elements/typography/P.tsx b/packages/dnb-eufemia/src/elements/typography/P.tsx
index f530acc891f..f5cb7897ca0 100644
--- a/packages/dnb-eufemia/src/elements/typography/P.tsx
+++ b/packages/dnb-eufemia/src/elements/typography/P.tsx
@@ -5,97 +5,112 @@
import React, { createContext, useContext } from 'react'
import classnames from 'classnames'
-import { SpacingProps } from '../../components/space/types'
-import type { DynamicElement } from '../../shared/types'
-import E from '../Element'
+import Typography, { TypographySize, TypographyProps } from './Typography'
-export type PSize =
- | 'x-small'
- | 'small'
- | 'basis'
- | 'medium'
- | 'large'
- | 'x-large'
- | 'xx-large'
+/** @deprecated use TypographySize instead */
+export type PSize = TypographySize
-export type PProps = SpacingProps &
- React.HTMLAttributes & {
- /**
- * Defines the Element Type, like "p"
- * Default: p
- */
- element?: DynamicElement | 'p'
- /**
- * Tells the component to use the medium font-weight styling dnb-p--medium defined in paragraphStyle - typography-mixins.scss. Find more details here https://eufemia.dnb.no/uilib/typography/font-weight/
- */
- medium?: boolean
- /**
- * Tells the component to use the bold font-weight styling dnb-p--bold defined in paragraphStyle - typography-mixins.scss. Find more details here https://eufemia.dnb.no/uilib/typography/font-weight/
- */
- bold?: boolean
- /**
- * Sets the font size based on size classes defined in paragraphStyle - typography-mixins.scss. For more detailed information go here: https://eufemia.dnb.no/uilib/typography/font-size/
- */
- size?: PSize
- /**
- * A string containing a combination of modifiers, used to set both font-size and weight in one property. e.g. "x-small bold" would make the paragraph extra small and bold.
- * Works as a flexible alternative to setting the medium, small, bold and size props.
- * List of modifiers can be found at https://eufemia.dnb.no/uilib/typography/font-size/ and https://eufemia.dnb.no/uilib/typography/font-weight/
- */
- modifier?: string
- }
+export type PProps = TypographyProps & {
+ /**
+ * Tells the component to use the medium font-weight styling dnb-t__weight--medium defined in paragraphStyle - typography-mixins.scss. Find more details here https://eufemia.dnb.no/uilib/typography/font-weight/
+ * @deprecated use the `weight` prop instead
+ */
+ medium?: boolean
+ /**
+ * Tells the component to use the bold font-weight styling dnb-t__weight--bold defined in paragraphStyle - typography-mixins.scss. Find more details here https://eufemia.dnb.no/uilib/typography/font-weight/
+ * @deprecated use the `weight` prop instead
+ */
+ bold?: boolean
+ /**
+ * A string containing a combination of modifiers, used to set both font-size and weight in one property. e.g. "x-small bold" would make the paragraph extra small and bold.
+ * Works as a flexible alternative to setting the medium, bold and size props.
+ * List of modifiers can be found at https://eufemia.dnb.no/uilib/typography/font-size/ and https://eufemia.dnb.no/uilib/typography/font-weight/
+ * @deprecated only font weights "bold" and "medium" and sizes "x-small" and "small" are supported. Use the `size` and `weight` props instead.
+ */
+ modifier?: string
+}
function P(props: PProps) {
const {
- modifier,
+ remainingModifiers,
element = 'p',
className,
- medium,
- bold,
- size,
- children,
...rest
- } = props
+ } = handleDeprecatedProps(props)
- const allModifiers = [medium && 'medium', bold && 'bold']
const paragraphContext = useContext(ParagraphContext)
- if (modifier) {
- modifier
- .split(/\s/g)
- .forEach((modifier) => allModifiers.push(modifier))
- }
-
- const modifierString = allModifiers
- .filter(Boolean)
- .reduce((acc, cur) => {
- if (['x-small', 'small'].includes(cur)) {
- return `${acc} dnb-p__size--${cur}`
- }
-
+ const deprecatedModifierString = remainingModifiers.reduce(
+ (acc, cur) => {
+ // This entire string could possibly be deprecated. There are no remaining modifiers
+ // that should be supported, but technically this allows for any class "dnb-p--[modifier]".
+ // But "dnb-p--lead" is the only class that we have, and it's not supposed to be added here.
return `${acc} dnb-p--${cur}`
- }, '')
+ },
+ ''
+ )
return (
-
- {children}
-
+ {...rest}
+ />
)
}
+const handleDeprecatedProps = ({
+ weight,
+ size,
+ modifier,
+ bold,
+ medium,
+ ...rest
+}: PProps): TypographyProps & {
+ remainingModifiers?: string[]
+} => {
+ let oldWeight
+ let oldSize
+
+ const allModifiers = [bold && 'bold', medium && 'medium']
+ if (modifier) {
+ modifier
+ .split(/\s/g)
+ .forEach((modifier) => allModifiers.push(modifier))
+ }
+
+ const remainingModifiers = allModifiers.filter(Boolean).filter((cur) => {
+ if (['x-small'].includes(cur)) {
+ oldSize = 'x-small'
+ } else if (['small'].includes(cur)) {
+ oldSize = oldSize || 'small'
+ } else if (['medium'].includes(cur)) {
+ oldWeight = oldWeight || 'medium'
+ } else if (['bold'].includes(cur)) {
+ oldWeight = 'bold'
+ } else {
+ // There should never be anything here unless it's a custom modifier.
+ return true
+ }
+ return false
+ }, [])
+
+ return {
+ weight: weight || oldWeight,
+ size: oldSize && size !== 'x-small' ? oldSize : size,
+ remainingModifiers,
+ ...rest,
+ }
+}
+
P._supportsSpacingProps = true
export default P
diff --git a/packages/dnb-eufemia/src/elements/typography/PDocs.ts b/packages/dnb-eufemia/src/elements/typography/PDocs.ts
index 8b97070e069..e5d7b74539d 100644
--- a/packages/dnb-eufemia/src/elements/typography/PDocs.ts
+++ b/packages/dnb-eufemia/src/elements/typography/PDocs.ts
@@ -1,34 +1,21 @@
import { PropertiesTableProps } from '../../shared/types'
+import { TypographyProperties } from './TypographyDocs'
export const ParagraphProperties: PropertiesTableProps = {
- element: {
- doc: 'Defines the Element Type, like `p`.',
- type: ['HTMLElement', 'string'],
- status: 'optional',
- },
+ ...TypographyProperties,
medium: {
- doc: 'Tells the component to use the medium font-weight styling `dnb-p--medium`. More details [here](/uilib/typography/font-weight).',
+ doc: 'Tells the component to use the medium font-weight styling `dnb-t__weight--medium`. More details [here](/uilib/typography/font-weight).',
type: 'boolean',
- status: 'optional',
+ status: 'deprecated',
},
bold: {
- doc: 'Tells the component to use the bold font-weight styling class `dnb-p--bold`. More details [here](/uilib/typography/font-weight).',
+ doc: 'Tells the component to use the bold font-weight styling class `dnb-t__weight--bold`. More details [here](/uilib/typography/font-weight).',
type: 'boolean',
- status: 'optional',
- },
- size: {
- doc: 'Sets the font size based on the following sizes: `x-small`, `small`, `basis`, `medium`, `large`, `x-large` or `xx-large`.',
- type: 'string',
- status: 'optional',
+ status: 'deprecated',
},
modifier: {
- doc: 'String containing a combination of modifiers, used to set both font-size and weight in one property. e.g. `x-small bold` would make the paragraph extra small and bold.',
+ doc: 'String containing a combination of modifiers, used to set both font-size and weight in one property. e.g. `x-small medium` would make the paragraph extra small and medium.',
type: 'string',
- status: 'optional',
- },
- '[Space](/uilib/layout/space/properties)': {
- doc: 'Spacing properties like `top` or `bottom` are supported.',
- type: ['string', 'object'],
- status: 'optional',
+ status: 'deprecated',
},
}
diff --git a/packages/dnb-eufemia/src/elements/typography/Typography.tsx b/packages/dnb-eufemia/src/elements/typography/Typography.tsx
new file mode 100644
index 00000000000..6178cbc0a29
--- /dev/null
+++ b/packages/dnb-eufemia/src/elements/typography/Typography.tsx
@@ -0,0 +1,101 @@
+/**
+ * HTML Element
+ *
+ */
+
+import React from 'react'
+import classnames from 'classnames'
+import { SpacingProps } from '../../components/space/types'
+import type { DynamicElement } from '../../shared/types'
+import E from '../Element'
+
+export type TypographySize =
+ | 'x-small'
+ | 'small'
+ | 'basis'
+ | 'medium'
+ | 'large'
+ | 'x-large'
+ | 'xx-large'
+
+export type TypographyAlign = 'center' | 'left' | 'right'
+export type TypographyFamily = 'basis' | 'heading' | 'monospace'
+export type TypographyWeight = 'regular' | 'medium' | 'bold'
+export type TypographyDecoration = 'underline'
+export type TypographySlant = 'italic'
+
+export type TypographyProps<
+ ElementType extends HTMLElement = HTMLElement,
+> = SpacingProps &
+ React.HTMLAttributes & {
+ /**
+ * Defines the Element Type, like "p".
+ */
+ element?: DynamicElement
+ /**
+ * Sets the font size, also sets the line-height if `line` prop is not set
+ */
+ size?: TypographySize
+ /**
+ * Sets the line height, will use same value as `size` if not set.
+ */
+ lineHeight?: TypographySize
+ /**
+ * Sets the text alignment
+ */
+ align?: TypographyAlign
+ /**
+ * Sets the font family
+ */
+ family?: TypographyFamily
+ /**
+ * Sets the font weight
+ */
+ weight?: TypographyWeight
+ /**
+ * Sets the font decoration
+ */
+ decoration?: TypographyDecoration
+ /**
+ * Sets the font style
+ */
+ slant?: TypographySlant
+ }
+
+type TypographyInternalProps = {
+ innerRef?: React.RefObject | React.ForwardedRef
+}
+
+const Typography = ({
+ element = 'p',
+ className,
+ size,
+ lineHeight,
+ align,
+ family,
+ weight,
+ decoration,
+ slant,
+ ...props
+}: TypographyProps & TypographyInternalProps) => {
+ return (
+
+ )
+}
+
+Typography._supportsSpacingProps = true
+
+export default Typography
diff --git a/packages/dnb-eufemia/src/elements/typography/TypographyDocs.ts b/packages/dnb-eufemia/src/elements/typography/TypographyDocs.ts
new file mode 100644
index 00000000000..b4318a926f7
--- /dev/null
+++ b/packages/dnb-eufemia/src/elements/typography/TypographyDocs.ts
@@ -0,0 +1,65 @@
+import { PropertiesTableProps } from '../../shared/types'
+
+export const TypographyProperties: PropertiesTableProps = {
+ element: {
+ doc: 'Defines the Element Type, like `p`.',
+ type: ['HTMLElement', 'string'],
+ status: 'optional',
+ },
+ size: {
+ doc: 'Sets the font size, also sets the line-height if `lineHeight` prop is not set.',
+ type: [
+ `'x-small'`,
+ `'small'`,
+ `'basis'`,
+ `'medium'`,
+ `'large'`,
+ `'x-large'`,
+ `'xx-large'`,
+ ],
+ status: 'optional',
+ },
+ lineHeight: {
+ doc: 'Sets the line height, will use same value as `size` if not set.',
+ type: [
+ `'x-small'`,
+ `'small'`,
+ `'basis'`,
+ `'medium'`,
+ `'large'`,
+ `'x-large'`,
+ `'xx-large'`,
+ ],
+ status: 'optional',
+ },
+ align: {
+ doc: 'Sets the text alignment.',
+ type: [`'center'`, `'left'`, `'right'`],
+ status: 'optional',
+ },
+ family: {
+ doc: 'Sets the font family.',
+ type: [`'basis'`, `'heading'`, `'monospace'`],
+ status: 'optional',
+ },
+ weight: {
+ doc: 'Sets the font weight.',
+ type: [`'regular'`, `'medium'`],
+ status: 'optional',
+ },
+ decoration: {
+ doc: 'Sets the font decoration.',
+ type: `'underline'`,
+ status: 'optional',
+ },
+ slant: {
+ doc: 'Sets the font style.',
+ type: `'italic'`,
+ status: 'optional',
+ },
+ '[Space](/uilib/layout/space/properties)': {
+ doc: 'Spacing properties like `top` or `bottom` are supported.',
+ type: ['string', 'object'],
+ status: 'optional',
+ },
+}
diff --git a/packages/dnb-eufemia/src/elements/typography/__tests__/P.test.tsx b/packages/dnb-eufemia/src/elements/typography/__tests__/P.test.tsx
index b5bcb2a4a22..2c7a6ae13c8 100644
--- a/packages/dnb-eufemia/src/elements/typography/__tests__/P.test.tsx
+++ b/packages/dnb-eufemia/src/elements/typography/__tests__/P.test.tsx
@@ -45,56 +45,128 @@ describe('P element', () => {
expect(element.tagName).toBe('STRONG')
})
- it('has correct size when size is defined', () => {
- render(
)
- const element = document.querySelector('.dnb-p__size--large')
+ it('can set className', () => {
+ render(
)
+ const element = document.querySelector('.dnb-p')
expect(Array.from(element.classList)).toEqual([
'dnb-p',
- 'dnb-p__size--large',
+ 'my-class',
+ 'dnb-t__weight--regular',
])
})
- it('has correct style when size and a modifier is defined', () => {
- render(
)
- const element = document.querySelector('.dnb-p__size--medium')
+ it('has correct size and line height when size is defined', () => {
+ render(
)
+ const element = document.querySelector('.dnb-t__size--large')
expect(Array.from(element.classList)).toEqual([
'dnb-p',
- 'dnb-p--medium',
- 'dnb-p__size--medium',
+ 'dnb-t__line-height--large',
+ 'dnb-t__size--large',
])
})
- it('has correct style when several modifiers are defined', () => {
- render(
)
- const element = document.querySelector('.dnb-p__size--small')
+ it('has correct style when bold is set to true', () => {
+ render(
)
+ const element = document.querySelector('.dnb-t__weight--bold')
expect(Array.from(element.classList)).toEqual([
'dnb-p',
- 'dnb-p--medium',
- 'dnb-p__size--small',
+ 'dnb-t__weight--bold',
])
})
- it('has correct style when medium is set to true', () => {
- render(
)
- const element = document.querySelector('.dnb-p--medium')
+ it('has correct style when several modifiers are defined', () => {
+ render(
+
+ )
+ const element = document.querySelector('.dnb-p')
+
expect(Array.from(element.classList)).toEqual([
'dnb-p',
- 'dnb-p--medium',
+ 'dnb-t__line-height--xx-large',
+ 'dnb-t__size--small',
+ 'dnb-t__align--center',
+ 'dnb-t__family--monospace',
+ 'dnb-t__weight--medium',
+ 'dnb-t__decoration--underline',
])
})
- it('has correct style when bold is set to true', () => {
- render(
)
- const element = document.querySelector('.dnb-p--bold')
-
- expect(Array.from(element.classList)).toEqual(['dnb-p', 'dnb-p--bold'])
- })
-
it('should validate with ARIA rules as a p element', async () => {
const Comp = render(
)
expect(await axeComponent(Comp)).toHaveNoViolations()
})
+
+ describe('deprecated behaviour', () => {
+ it('can set className and modifier', () => {
+ render(
)
+ const element = document.querySelector('.dnb-p')
+
+ expect(Array.from(element.classList)).toEqual([
+ 'dnb-p',
+ 'dnb-p--my-modifier',
+ 'my-class',
+ ])
+ })
+ it('has correct style when size and a modifier is defined', () => {
+ render(
)
+ const element = document.querySelector('.dnb-t__size--medium')
+
+ expect(Array.from(element.classList)).toEqual([
+ 'dnb-p',
+ 'dnb-t__line-height--medium',
+ 'dnb-t__size--medium',
+ 'dnb-t__weight--medium',
+ ])
+ })
+ it('has correct style when several modifiers are defined', () => {
+ render(
)
+ const element = document.querySelector('.dnb-t__size--small')
+
+ expect(Array.from(element.classList)).toEqual([
+ 'dnb-p',
+ 'dnb-t__line-height--small',
+ 'dnb-t__size--small',
+ 'dnb-t__weight--medium',
+ ])
+ })
+ it('has correct style when several modifiers conflict', () => {
+ render(
)
+ const element = document.querySelector('.dnb-t__size--x-small')
+
+ expect(Array.from(element.classList)).toEqual([
+ 'dnb-p',
+ 'dnb-t__line-height--x-small',
+ 'dnb-t__size--x-small',
+ 'dnb-t__weight--bold',
+ ])
+ })
+ it('has correct style when medium is set to true', () => {
+ render(
)
+ const element = document.querySelector('.dnb-t__weight--medium')
+ expect(Array.from(element.classList)).toEqual([
+ 'dnb-p',
+ 'dnb-t__weight--medium',
+ ])
+ })
+
+ it('has correct style when bold is set to true', () => {
+ render(
)
+ const element = document.querySelector('.dnb-t__weight--bold')
+
+ expect(Array.from(element.classList)).toEqual([
+ 'dnb-p',
+ 'dnb-t__weight--bold',
+ ])
+ })
+ })
})
diff --git a/packages/dnb-eufemia/src/elements/typography/__tests__/Paragraph.screenshot.test.ts b/packages/dnb-eufemia/src/elements/typography/__tests__/Paragraph.screenshot.test.ts
index dde88c9ed42..72d15a759a7 100644
--- a/packages/dnb-eufemia/src/elements/typography/__tests__/Paragraph.screenshot.test.ts
+++ b/packages/dnb-eufemia/src/elements/typography/__tests__/Paragraph.screenshot.test.ts
@@ -14,23 +14,59 @@ describe.each(['ui', 'sbanken'])('Paragraph for %s', (themeName) => {
url: '/uilib/elements/paragraph',
})
- it('have to match the paragraph example', async () => {
+ it('have to match the paragraph with weight modifiers', async () => {
const screenshot = await makeScreenshot({
- selector: '[data-visual-test="paragraph-default"]',
+ selector: '[data-visual-test="paragraph-modifiers-weight"]',
})
expect(screenshot).toMatchImageSnapshot()
})
- it('have to match the paragraph with small text', async () => {
+ it('have to match the paragraph with size modifiers', async () => {
const screenshot = await makeScreenshot({
- selector: '[data-visual-test="paragraph-small"]',
+ selector: '[data-visual-test="paragraph-modifiers-size"]',
+ })
+ expect(screenshot).toMatchImageSnapshot()
+ })
+
+ it('have to match the paragraph with align modifiers', async () => {
+ const screenshot = await makeScreenshot({
+ style: { width: '30rem' },
+ selector: '[data-visual-test="paragraph-modifiers-align"]',
})
expect(screenshot).toMatchImageSnapshot()
})
- it('have to match the paragraph with modifiers', async () => {
+ it('have to match the paragraph with family modifiers', async () => {
const screenshot = await makeScreenshot({
- selector: '[data-visual-test="paragraph-modifiers"]',
+ selector: '[data-visual-test="paragraph-modifiers-family"]',
+ })
+ expect(screenshot).toMatchImageSnapshot()
+ })
+
+ it('have to match the paragraph with line modifiers', async () => {
+ const screenshot = await makeScreenshot({
+ selector: '[data-visual-test="paragraph-modifiers-line"]',
+ })
+ expect(screenshot).toMatchImageSnapshot()
+ })
+
+ it('have to match the paragraph with other modifiers', async () => {
+ const screenshot = await makeScreenshot({
+ selector: '[data-visual-test="paragraph-modifiers-other"]',
+ })
+ expect(screenshot).toMatchImageSnapshot()
+ })
+
+ it('have to match the paragraph example', async () => {
+ const screenshot = await makeScreenshot({
+ selector: '[data-visual-test="paragraph-default"]',
+ })
+ expect(screenshot).toMatchImageSnapshot()
+ })
+
+ it('have to match the paragraph with small text', async () => {
+ const screenshot = await makeScreenshot({
+ selector: '[data-visual-test="paragraph-small"]',
})
expect(screenshot).toMatchImageSnapshot()
})
diff --git a/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-additional-elements.snap.png b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-additional-elements.snap.png
index 9cc37d5f375..93c1f8984d0 100644
Binary files a/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-additional-elements.snap.png and b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-additional-elements.snap.png differ
diff --git a/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-align-modifiers.snap.png b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-align-modifiers.snap.png
new file mode 100644
index 00000000000..06f36931f7b
Binary files /dev/null and b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-align-modifiers.snap.png differ
diff --git a/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-family-modifiers.snap.png b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-family-modifiers.snap.png
new file mode 100644
index 00000000000..bb8d7320973
Binary files /dev/null and b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-family-modifiers.snap.png differ
diff --git a/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-line-modifiers.snap.png b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-line-modifiers.snap.png
new file mode 100644
index 00000000000..2c55f6c9bd0
Binary files /dev/null and b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-line-modifiers.snap.png differ
diff --git a/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-modifiers.snap.png b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-modifiers.snap.png
deleted file mode 100644
index 07c58427cc7..00000000000
Binary files a/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-modifiers.snap.png and /dev/null differ
diff --git a/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-other-modifiers.snap.png b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-other-modifiers.snap.png
new file mode 100644
index 00000000000..6cec9501b17
Binary files /dev/null and b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-other-modifiers.snap.png differ
diff --git a/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-size-modifiers.snap.png b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-size-modifiers.snap.png
new file mode 100644
index 00000000000..3cc42bd5995
Binary files /dev/null and b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-size-modifiers.snap.png differ
diff --git a/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-weight-modifiers.snap.png b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-weight-modifiers.snap.png
new file mode 100644
index 00000000000..c3ec48554cb
Binary files /dev/null and b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-sbanken-have-to-match-the-paragraph-with-weight-modifiers.snap.png differ
diff --git a/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-align-modifiers.snap.png b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-align-modifiers.snap.png
new file mode 100644
index 00000000000..5f3022cb912
Binary files /dev/null and b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-align-modifiers.snap.png differ
diff --git a/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-family-modifiers.snap.png b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-family-modifiers.snap.png
new file mode 100644
index 00000000000..98f546c66b0
Binary files /dev/null and b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-family-modifiers.snap.png differ
diff --git a/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-line-modifiers.snap.png b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-line-modifiers.snap.png
new file mode 100644
index 00000000000..a359955777f
Binary files /dev/null and b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-line-modifiers.snap.png differ
diff --git a/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-modifiers.snap.png b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-modifiers.snap.png
deleted file mode 100644
index 6a286fd4bf2..00000000000
Binary files a/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-modifiers.snap.png and /dev/null differ
diff --git a/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-other-modifiers.snap.png b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-other-modifiers.snap.png
new file mode 100644
index 00000000000..9245dae4eff
Binary files /dev/null and b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-other-modifiers.snap.png differ
diff --git a/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-size-modifiers.snap.png b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-size-modifiers.snap.png
new file mode 100644
index 00000000000..5031d192b5a
Binary files /dev/null and b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-size-modifiers.snap.png differ
diff --git a/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-weight-modifiers.snap.png b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-weight-modifiers.snap.png
new file mode 100644
index 00000000000..979a5faee26
Binary files /dev/null and b/packages/dnb-eufemia/src/elements/typography/__tests__/__image_snapshots__/paragraph-for-ui-have-to-match-the-paragraph-with-weight-modifiers.snap.png differ
diff --git a/packages/dnb-eufemia/src/elements/typography/style/_dnb-t.scss b/packages/dnb-eufemia/src/elements/typography/style/_dnb-t.scss
new file mode 100644
index 00000000000..5c63b49aeb8
--- /dev/null
+++ b/packages/dnb-eufemia/src/elements/typography/style/_dnb-t.scss
@@ -0,0 +1,93 @@
+/*
+ * Typography
+ * Universal set of helper classes that do not have a specific element.
+ * The class ".dnb-t" does nothing, only it's modifiers ".dnb-t__[***]" do.
+ */
+.dnb-t {
+ // size
+ &__size--xx-large {
+ font-size: var(--font-size-xx-large);
+ }
+ &__size--x-large {
+ font-size: var(--font-size-x-large);
+ }
+ &__size--large {
+ font-size: var(--font-size-large);
+ }
+ &__size--medium {
+ font-size: var(--font-size-medium);
+ }
+ &__size--basis {
+ font-size: var(--font-size-basis);
+ }
+ &__size--small {
+ font-size: var(--font-size-small);
+ }
+ &__size--x-small {
+ font-size: var(--font-size-x-small);
+ }
+
+ // line height
+ &__line-height--xx-large {
+ line-height: var(--line-height-xx-large);
+ }
+ &__line-height--x-large {
+ line-height: var(--line-height-x-large);
+ }
+ &__line-height--large {
+ line-height: var(--line-height-large);
+ }
+ &__line-height--medium {
+ line-height: var(--line-height-medium);
+ }
+ &__line-height--basis {
+ line-height: var(--line-height-basis);
+ }
+ &__line-height--small {
+ line-height: var(--line-height-small);
+ }
+ &__line-height--x-small {
+ line-height: var(--line-height-x-small);
+ }
+
+ // weight
+ &__weight--regular {
+ font-weight: var(--font-weight-regular);
+ }
+ &__weight--medium {
+ font-weight: var(--font-weight-medium);
+ }
+ &__weight--bold {
+ font-weight: var(--font-weight-bold);
+ }
+
+ // alignement
+ &__align--center {
+ text-align: center;
+ }
+ &__align--left {
+ text-align: left;
+ }
+ &__align--right {
+ text-align: right;
+ }
+
+ // family
+ &__family--default {
+ font-family: var(--font-family-default);
+ }
+ &__family--heading {
+ font-family: var(--font-family-heading);
+ }
+ &__family--monospace {
+ font-family: var(--font-family-monospace);
+ }
+
+ // underline / italic
+ &__decoration--underline {
+ text-decoration: underline;
+ }
+ &__slant--italic {
+ font-style: italic;
+ }
+}
diff --git a/packages/dnb-eufemia/src/elements/typography/style/dnb-typography.scss b/packages/dnb-eufemia/src/elements/typography/style/dnb-typography.scss
index 5fba5f66c3c..63c8ff1b07a 100644
--- a/packages/dnb-eufemia/src/elements/typography/style/dnb-typography.scss
+++ b/packages/dnb-eufemia/src/elements/typography/style/dnb-typography.scss
@@ -7,7 +7,6 @@
@import './typography-mixins.scss';
@include typographySelectors() {
- --typography-h-default-font-family: var(--font-family-default);
--typography-h-default-font-weight: var(--font-weight-medium);
// Heading xx-large
@@ -94,6 +93,8 @@ sub {
@include paragraphStyle();
}
+@import './dnb-t.scss';
+
// Tables
.dnb-table {
b,
diff --git a/packages/dnb-eufemia/src/elements/typography/style/themes/dnb-typography-theme-sbanken.scss b/packages/dnb-eufemia/src/elements/typography/style/themes/dnb-typography-theme-sbanken.scss
index 50064e93b27..1d13f27f1d1 100644
--- a/packages/dnb-eufemia/src/elements/typography/style/themes/dnb-typography-theme-sbanken.scss
+++ b/packages/dnb-eufemia/src/elements/typography/style/themes/dnb-typography-theme-sbanken.scss
@@ -18,7 +18,6 @@
}
@include typography.typographySelectors() {
- --typography-h-default-font-family: var(--sb-font-family-headings);
--typography-h-xx-large-weight: var(--font-weight-regular);
--typography-h-x-large-weight: var(--font-weight-regular);
--typography-h-large-small-font-size: var(--sb-font-size-medium--plus);
diff --git a/packages/dnb-eufemia/src/elements/typography/style/typography-mixins.scss b/packages/dnb-eufemia/src/elements/typography/style/typography-mixins.scss
index 34399faf355..04fadd65786 100644
--- a/packages/dnb-eufemia/src/elements/typography/style/typography-mixins.scss
+++ b/packages/dnb-eufemia/src/elements/typography/style/typography-mixins.scss
@@ -96,7 +96,7 @@
margin: 0;
}
- font-family: var(--typography-h-default-font-family);
+ font-family: var(--font-family-heading);
// make icons inside heading responsive to the heading size
.dnb-icon--default {
@@ -240,13 +240,29 @@
&--lead {
@include typography_lead();
}
+ b,
+ strong {
+ font-weight: var(--font-weight-medium);
+ }
+ // is still needed for backwards compatibility when ".dnp-p" was used for all typography
+ @include paragraphDeprecated();
- &--medium {
+ & > small {
+ font-size: var(--font-size-small);
+ line-height: var(--line-height-small);
+ }
+
+ & > cite {
font-weight: var(--font-weight-medium);
+ line-height: var(--line-height-basis);
+ font-style: italic;
}
+}
- b,
- strong {
+// should use the .dnb-t classes instead
+@mixin paragraphDeprecated() {
+ // weights
+ &--medium {
font-weight: var(--font-weight-medium);
}
@@ -254,6 +270,7 @@
font-weight: var(--font-weight-bold);
}
+ // sizes and line-heights
&__size--xx-large {
font-size: var(--font-size-xx-large);
line-height: var(--line-height-xx-large);
@@ -279,24 +296,17 @@
line-height: var(--line-height-medium);
}
- &--small ,// backwards compatibility
- &__size--small,
- & > small {
+ &--small, // backwards compatibility
+ &__size--small {
font-size: var(--font-size-small);
line-height: var(--line-height-small);
}
- &--x-small ,// backwards compatibility
- &__size--x-small {
+ &--x-small, // backwards compatibility
+ &__size--x-small {
font-size: var(--font-size-x-small);
line-height: var(--line-height-x-small);
}
-
- & > cite {
- font-weight: var(--font-weight-medium);
- line-height: var(--line-height-basis);
- font-style: italic;
- }
}
@mixin headingSpacing_xx-large() {
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 ae0e7f08457..8d7660d0a9a 100644
--- a/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/Provider.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/Provider.tsx
@@ -603,7 +603,10 @@ export default function Provider(
for (const path in fieldPropsRef.current) {
if (mountedFieldsRef.current[path]?.isMounted) {
const props = fieldPropsRef.current[path]
- if (isAsync(props.validator) || isAsync(props.onBlurValidator)) {
+ if (
+ isAsync(props.onChangeValidator) ||
+ isAsync(props.onBlurValidator)
+ ) {
return true
}
}
diff --git a/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/__tests__/Provider.test.tsx b/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/__tests__/Provider.test.tsx
index eebcf937713..dbb5210eb6b 100644
--- a/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/__tests__/Provider.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/__tests__/Provider.test.tsx
@@ -1082,7 +1082,7 @@ describe('DataContext.Provider', () => {
expect(onSubmit).toHaveBeenCalledTimes(1)
})
- describe('should evaluate long validator and onBlurValidator before continue with async onSubmit', () => {
+ describe('should evaluate long onChangeValidator and onBlurValidator before continue with async onSubmit', () => {
let eventsStart = []
let eventsEnd = []
@@ -1110,12 +1110,12 @@ describe('DataContext.Provider', () => {
eventsEnd.push('onChangeField')
}
- const validator = async () => {
- eventsStart.push('validator')
+ const onChangeValidator = async () => {
+ eventsStart.push('onChangeValidator')
await wait(10)
- eventsEnd.push('validator')
+ eventsEnd.push('onChangeValidator')
}
const onBlurValidator = async () => {
@@ -1135,7 +1135,7 @@ describe('DataContext.Provider', () => {
@@ -1152,7 +1152,7 @@ describe('DataContext.Provider', () => {
await waitFor(() => {
expect(eventsStart).toEqual([
- 'validator',
+ 'onChangeValidator',
'onChangeForm',
'onChangeField',
])
@@ -1168,7 +1168,7 @@ describe('DataContext.Provider', () => {
@@ -1186,12 +1186,12 @@ describe('DataContext.Provider', () => {
await wait(100)
expect(eventsStart).toEqual([
- 'validator',
+ 'onChangeValidator',
'onBlurValidator',
'onSubmit',
])
expect(eventsEnd).toEqual([
- 'validator',
+ 'onChangeValidator',
'onBlurValidator',
'onSubmit',
])
@@ -1203,10 +1203,10 @@ describe('DataContext.Provider', () => {
await wait(10)
return { info: 'Info message' } as const
})
- const validator = jest.fn(async (value) => {
+ const onChangeValidator = jest.fn(async (value) => {
await wait(10)
- if (value === 'validator-error') {
- return new Error('validator-error')
+ if (value === 'onChangeValidator-error') {
+ return new Error('onChangeValidator-error')
}
})
const onBlurValidator = jest.fn(async (value) => {
@@ -1220,7 +1220,7 @@ describe('DataContext.Provider', () => {
@@ -1250,12 +1250,12 @@ describe('DataContext.Provider', () => {
await waitFor(() => {
expect(onSubmit).toHaveBeenCalledTimes(0)
expect(onBlurValidator).toHaveBeenCalledTimes(0)
- expect(validator).toHaveBeenCalledTimes(0)
+ expect(onChangeValidator).toHaveBeenCalledTimes(0)
})
// Use fireEvent over userEvent, because of its sync nature
fireEvent.change(input, {
- target: { value: 'validator-error' },
+ target: { value: 'onChangeValidator-error' },
})
await waitFor(() => {
@@ -1266,13 +1266,13 @@ describe('DataContext.Provider', () => {
const status = document.querySelector(
'.dnb-forms-field-block .dnb-form-status'
)
- expect(status).toHaveTextContent('validator-error')
+ expect(status).toHaveTextContent('onChangeValidator-error')
})
await waitFor(() => {
expect(onSubmit).toHaveBeenCalledTimes(0)
expect(onBlurValidator).toHaveBeenCalledTimes(0)
- expect(validator).toHaveBeenCalledTimes(1)
+ expect(onChangeValidator).toHaveBeenCalledTimes(1)
})
// Use fireEvent over userEvent, because of its sync nature
@@ -1294,7 +1294,7 @@ describe('DataContext.Provider', () => {
await waitFor(() => {
expect(onSubmit).toHaveBeenCalledTimes(0)
expect(onBlurValidator).toHaveBeenCalledTimes(0)
- expect(validator).toHaveBeenCalledTimes(2)
+ expect(onChangeValidator).toHaveBeenCalledTimes(2)
})
// Use fireEvent over userEvent, because of its sync nature
@@ -1309,7 +1309,7 @@ describe('DataContext.Provider', () => {
await waitFor(() => {
expect(onSubmit).toHaveBeenCalledTimes(0)
expect(onBlurValidator).toHaveBeenCalledTimes(1)
- expect(validator).toHaveBeenCalledTimes(2)
+ expect(onChangeValidator).toHaveBeenCalledTimes(2)
})
await userEvent.click(submitButton)
@@ -1317,7 +1317,7 @@ describe('DataContext.Provider', () => {
await waitFor(() => {
expect(onSubmit).toHaveBeenCalledTimes(0)
expect(onBlurValidator).toHaveBeenCalledTimes(1)
- expect(validator).toHaveBeenCalledTimes(2)
+ expect(onChangeValidator).toHaveBeenCalledTimes(2)
})
await waitFor(() => {
@@ -1345,20 +1345,20 @@ describe('DataContext.Provider', () => {
await waitFor(() => {
expect(onSubmit).toHaveBeenCalledTimes(1)
expect(onBlurValidator).toHaveBeenCalledTimes(3)
- expect(validator).toHaveBeenCalledTimes(12)
+ expect(onChangeValidator).toHaveBeenCalledTimes(12)
})
})
- it('should set "formState" to "pending" when "validator" is async', async () => {
+ it('should set "formState" to "pending" when "onChangeValidator" is async', async () => {
const result = createRef()
- const validator = async () => {
+ const onChangeValidator = async () => {
return new Error('My error')
}
const { rerender } = render(
-
+
)
@@ -1375,14 +1375,14 @@ describe('DataContext.Provider', () => {
expect(result.current.formState).toBeUndefined()
})
- const syncValidator = () => {
+ const syncOnChangeValidator = () => {
return new Error('My error')
}
rerender(
-
+
)
@@ -1496,7 +1496,7 @@ describe('DataContext.Provider', () => {
.fn()
.mockImplementation(async () => null)
- const validator = debounceAsync(async (value) => {
+ const onChangeValidator = debounceAsync(async (value) => {
await wait(400)
if (value === 'invalid') {
return Error('My error')
@@ -1508,7 +1508,7 @@ describe('DataContext.Provider', () => {
@@ -1593,7 +1593,7 @@ describe('DataContext.Provider', () => {
})
})
- it('should emit onChange only when validator is evaluated successfully', async () => {
+ it('should emit onChange only when onChangeValidator is evaluated successfully', async () => {
const onChangeContext = jest.fn().mockImplementation(async () => {
await wait(10)
})
@@ -1601,25 +1601,27 @@ describe('DataContext.Provider', () => {
await wait(10)
})
- const validator = jest.fn().mockImplementation(async (value) => {
- /**
- * It seems that this test on CI fails during way slower performance.
- * The higher timeout is to ensure the typed value will be handle by the async revalidation, even the value was valid when continue typing.
- *
- * The slower the performance, the higher the timeout needs to be.
- */
- await wait(60)
- if (value !== 'valid') {
- return Error(`value: ${value}`)
- }
- })
+ const onChangeValidator = jest
+ .fn()
+ .mockImplementation(async (value) => {
+ /**
+ * It seems that this test on CI fails during way slower performance.
+ * The higher timeout is to ensure the typed value will be handle by the async revalidation, even the value was valid when continue typing.
+ *
+ * The slower the performance, the higher the timeout needs to be.
+ */
+ await wait(60)
+ if (value !== 'valid') {
+ return Error(`value: ${value}`)
+ }
+ })
render(
@@ -1986,7 +1988,7 @@ describe('DataContext.Provider', () => {
})
})
- it('should fulfill async validator before the form and field event', async () => {
+ it('should fulfill async onChangeValidator before the form and field event', async () => {
const onChangeForm: OnChange = async ({ myField }) => {
return {
info: 'onChangeForm-info',
@@ -2003,7 +2005,7 @@ describe('DataContext.Provider', () => {
Error('onChangeField-error'),
}
}
- const validator = debounceAsync(async (value) => {
+ const onChangeValidator = debounceAsync(async (value) => {
if (value === 'invalid') {
return Error('My error')
}
@@ -2015,7 +2017,7 @@ describe('DataContext.Provider', () => {
label="My label"
path="/myField"
onChange={onChangeField}
- validator={validator}
+ onChangeValidator={onChangeValidator}
/>
@@ -2056,9 +2058,9 @@ describe('DataContext.Provider', () => {
it('should show indicator during all async operations', async () => {
const events = []
- const validator = debounceAsync(async () => {
+ const onChangeValidator = debounceAsync(async () => {
await wait(101)
- events.push('validator')
+ events.push('onChangeValidator')
})
const onChangeForm: OnChange = async () => {
await wait(102)
@@ -2075,7 +2077,7 @@ describe('DataContext.Provider', () => {
label="My label"
path="/myField"
onChange={onChangeField}
- validator={validator}
+ onChangeValidator={onChangeValidator}
/>
)
@@ -2088,14 +2090,14 @@ describe('DataContext.Provider', () => {
await userEvent.type(input, '123')
await waitFor(() => {
- expect(events).toEqual(['validator'])
+ expect(events).toEqual(['onChangeValidator'])
expect(indicator).toHaveClass(
'dnb-forms-submit-indicator--state-pending'
)
})
await waitFor(() => {
- expect(events).toEqual(['validator', 'onChangeForm'])
+ expect(events).toEqual(['onChangeValidator', 'onChangeForm'])
expect(indicator).toHaveClass(
'dnb-forms-submit-indicator--state-pending'
)
@@ -2103,7 +2105,7 @@ describe('DataContext.Provider', () => {
await waitFor(() => {
expect(events).toEqual([
- 'validator',
+ 'onChangeValidator',
'onChangeForm',
'onChangeField',
])
@@ -3976,7 +3978,7 @@ describe('DataContext.Provider', () => {
await wait(10)
})
- const validator = jest.fn(async (value) => {
+ const onChangeValidator = jest.fn(async (value) => {
await wait(10)
if (value !== 123) {
return new Error('Invalid')
@@ -3994,7 +3996,7 @@ describe('DataContext.Provider', () => {
path="/count"
label={data?.count}
onChange={onChange}
- validator={validator}
+ onChangeValidator={onChangeValidator}
/>
{JSON.stringify(data)}
@@ -4020,8 +4022,11 @@ describe('DataContext.Provider', () => {
await waitFor(() => {
expect(onChange).toHaveBeenCalledTimes(0)
- expect(validator).toHaveBeenCalledTimes(1)
- expect(validator).toHaveBeenLastCalledWith(12, expect.anything())
+ expect(onChangeValidator).toHaveBeenCalledTimes(1)
+ expect(onChangeValidator).toHaveBeenLastCalledWith(
+ 12,
+ expect.anything()
+ )
})
fireEvent.change(input, {
@@ -4032,8 +4037,11 @@ describe('DataContext.Provider', () => {
expect(onChange).toHaveBeenCalledTimes(0)
expect(onChange).toHaveBeenCalledTimes(0)
- expect(validator).toHaveBeenCalledTimes(2)
- expect(validator).toHaveBeenLastCalledWith(123, expect.anything())
+ expect(onChangeValidator).toHaveBeenCalledTimes(2)
+ expect(onChangeValidator).toHaveBeenLastCalledWith(
+ 123,
+ expect.anything()
+ )
// executed in sync and unvalidated
expect(onDataChange).toHaveBeenCalledTimes(4)
@@ -4043,8 +4051,11 @@ describe('DataContext.Provider', () => {
expect(onChange).toHaveBeenCalledTimes(1)
expect(onChange).toHaveBeenLastCalledWith(123)
- expect(validator).toHaveBeenCalledTimes(2)
- expect(validator).toHaveBeenLastCalledWith(123, expect.anything())
+ expect(onChangeValidator).toHaveBeenCalledTimes(2)
+ expect(onChangeValidator).toHaveBeenLastCalledWith(
+ 123,
+ expect.anything()
+ )
})
})
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/ArraySelection.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/ArraySelection.test.tsx
index 4e2161383b1..1b2d652162b 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/ArraySelection.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/ArraySelection.test.tsx
@@ -39,6 +39,22 @@ describe('ArraySelection', () => {
).toBe(document.querySelector('.dnb-tooltip__content').id)
})
+ it('precede option title over children', async () => {
+ render(
+
+
+ child a
+
+
+ child b
+
+
+ )
+ const options = document.querySelectorAll('.dnb-checkbox')
+ expect(options[0].textContent).toBe('title a')
+ expect(options[1].textContent).toBe('title b')
+ })
+
it('handles selection correctly', () => {
const handleChange = jest.fn()
render(
@@ -470,6 +486,23 @@ describe('ArraySelection', () => {
describe.each(['button', 'checkbox-button'])(
'%s',
(testVariant: 'button' | 'checkbox-button') => {
+ it('precede option title over children', async () => {
+ render(
+
+
+ child a
+
+
+ child b
+
+
+ )
+
+ const options = document.querySelectorAll('.dnb-button__text')
+ expect(options[0].textContent).toBe('title a')
+ expect(options[1].textContent).toBe('title b')
+ })
+
it(`has correct elements when "${testVariant}" is provided provided`, () => {
render(
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-checkbox-button-have-to-match-checkbox-button-options-horizontal.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-checkbox-button-have-to-match-checkbox-button-options-horizontal.snap.png
index 33772925a1c..de27d822958 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-checkbox-button-have-to-match-checkbox-button-options-horizontal.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-checkbox-button-have-to-match-checkbox-button-options-horizontal.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-checkbox-have-to-match-checkbox-nesting-logic.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-checkbox-have-to-match-checkbox-nesting-logic.snap.png
index c3b150c7f26..6f832134d67 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-checkbox-have-to-match-checkbox-nesting-logic.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-checkbox-have-to-match-checkbox-nesting-logic.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-checkbox-button-have-to-match-checkbox-button-options-horizontal.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-checkbox-button-have-to-match-checkbox-button-options-horizontal.snap.png
index ec254db3572..aac49c7f2d6 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-checkbox-button-have-to-match-checkbox-button-options-horizontal.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-checkbox-button-have-to-match-checkbox-button-options-horizontal.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-checkbox-have-to-match-checkbox-nesting-logic.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-checkbox-have-to-match-checkbox-nesting-logic.snap.png
index 95b8e842f45..acf0cd54d04 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-checkbox-have-to-match-checkbox-nesting-logic.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-checkbox-have-to-match-checkbox-nesting-logic.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/stories/ArraySelection.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/stories/ArraySelection.stories.tsx
index f6f8ec241b9..cc8796fc62f 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/stories/ArraySelection.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/stories/ArraySelection.stories.tsx
@@ -1,4 +1,4 @@
-import { Card, Section } from '../../../../../components'
+import { Section } from '../../../../../components'
import { Field, Form } from '../../..'
export default {
@@ -8,7 +8,7 @@ export default {
export function NestingWithLogic() {
return (
-
+
-
+
)
}
@@ -93,7 +93,7 @@ export function SelectUpToThree() {
]
return (
-
+
@@ -118,6 +118,6 @@ export function SelectUpToThree() {
maxItems: 'You can only select up to three',
}}
/>
-
+
)
}
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/BankAccountNumber/BankAccountNumber.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/BankAccountNumber/BankAccountNumber.tsx
index 89abcdae5e5..fafb91f0d2f 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/BankAccountNumber/BankAccountNumber.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/BankAccountNumber/BankAccountNumber.tsx
@@ -46,7 +46,9 @@ function BankAccountNumber(props: Props) {
const {
validate = true,
omitMask,
+ // Deprecated – can be removed in v11
validator,
+ onChangeValidator = validator,
onBlurValidator = bankAccountNumberValidator,
label: labelProp,
width,
@@ -97,7 +99,7 @@ function BankAccountNumber(props: Props) {
mask,
width: width ?? 'medium',
inputMode: 'numeric',
- validator: validate ? validator : undefined,
+ onChangeValidator: validate ? onChangeValidator : undefined,
onBlurValidator: validate ? onBlurValidatorToUse : undefined,
exportValidators: { bankAccountNumberValidator },
}
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/BankAccountNumber/__tests__/BankAccountNumber.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/BankAccountNumber/__tests__/BankAccountNumber.test.tsx
index 9a89d99d702..02d87441d89 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/BankAccountNumber/__tests__/BankAccountNumber.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/BankAccountNumber/__tests__/BankAccountNumber.test.tsx
@@ -28,6 +28,57 @@ describe('Field.BankAccountNumber', () => {
expect(input).toHaveAttribute('inputmode', 'numeric')
})
+ // Deprecated – can be removed in v11
+ it('should validate given function as validator', async () => {
+ const text = 'Custom Error message'
+ const validator = jest.fn((value) => {
+ return value.length < 4 ? new Error(text) : undefined
+ })
+
+ render(
+
+ )
+
+ await waitFor(() => {
+ expect(validator).toHaveBeenCalledTimes(1)
+ })
+
+ const element = document.querySelector('.dnb-form-status')
+
+ expect(element).toBeInTheDocument()
+ expect(element.textContent).toBe(text)
+ })
+
+ it('should validate given function as onChangeValidator', async () => {
+ const text = 'Custom Error message'
+ const onChangeValidator = jest.fn((value) => {
+ return value.length < 4 ? new Error(text) : undefined
+ })
+
+ render(
+
+ )
+
+ await waitFor(() => {
+ expect(onChangeValidator).toHaveBeenCalledTimes(1)
+ })
+
+ const element = document.querySelector('.dnb-form-status')
+
+ expect(element).toBeInTheDocument()
+ expect(element.textContent).toBe(text)
+ })
+
describe('should validate Norwegian bank account numbers', () => {
const validBankAccountNumbers = [
'52440407897',
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Indeterminate/stories/Indeterminate.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/Indeterminate/stories/Indeterminate.stories.tsx
index 6b7d24ba52d..dd0cb8a4f8f 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/Indeterminate/stories/Indeterminate.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/Indeterminate/stories/Indeterminate.stories.tsx
@@ -1,6 +1,5 @@
import React from 'react'
import Field, { Form } from '../../../Forms'
-import { Card } from '../../../../../components'
export default {
title: 'Eufemia/Extensions/Forms/Indeterminate',
@@ -9,7 +8,7 @@ export default {
export function WithToggle() {
return (
-
+
-
+
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx
index 650876cfdd7..96b9b5d8abc 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx
@@ -92,8 +92,10 @@ function NationalIdentityNumber(props: Props) {
const {
validate = true,
omitMask,
- onBlurValidator = dnrAndFnrValidator,
+ // Deprecated – can be removed in v11
validator,
+ onChangeValidator = validator,
+ onBlurValidator = dnrAndFnrValidator,
width,
label: labelProp,
} = props
@@ -129,7 +131,7 @@ function NationalIdentityNumber(props: Props) {
mask,
width: width ?? 'medium',
inputMode: 'numeric',
- validator: validate ? validator : undefined,
+ onChangeValidator: validate ? onChangeValidator : undefined,
onBlurValidator: validate ? onBlurValidatorToUse : undefined,
exportValidators: {
dnrValidator,
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumber.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumber.test.tsx
index 72b3e73d4e8..096901fc34e 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumber.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumber.test.tsx
@@ -134,7 +134,8 @@ describe('Field.NationalIdentityNumber', () => {
expect(dummyValidator).toHaveBeenCalledWith('6', expect.anything())
})
- it('should validate given function', async () => {
+ // Deprecated – can be removed in v11
+ it('should validate given function as validator', async () => {
const text = 'Custom Error message'
const validator = jest.fn((value) => {
return value.length < 4 ? new Error(text) : undefined
@@ -159,20 +160,45 @@ describe('Field.NationalIdentityNumber', () => {
expect(element.textContent).toBe(text)
})
+ it('should validate given function as onChangeValidator', async () => {
+ const text = 'Custom Error message'
+ const onChangeValidator = jest.fn((value) => {
+ return value.length < 4 ? new Error(text) : undefined
+ })
+
+ render(
+
+ )
+
+ await waitFor(() => {
+ expect(onChangeValidator).toHaveBeenCalledTimes(1)
+ })
+
+ const element = document.querySelector('.dnb-form-status')
+
+ expect(element).toBeInTheDocument()
+ expect(element.textContent).toBe(text)
+ })
+
it('should contain errorMessages as second parameter', () => {
- const validator = jest.fn()
+ const onChangeValidator = jest.fn()
render(
)
- expect(validator).toHaveBeenCalledTimes(1)
- expect(validator).toHaveBeenCalledWith(
+ expect(onChangeValidator).toHaveBeenCalledTimes(1)
+ expect(onChangeValidator).toHaveBeenCalledWith(
'123',
expect.objectContaining({
errorMessages: expect.objectContaining({
@@ -313,7 +339,7 @@ describe('Field.NationalIdentityNumber', () => {
expect(screen.queryByRole('alert')).toBeNull()
})
- it('should not validate custom validator when validate false', async () => {
+ it('should not validate custom onChangeValidator when validate false', async () => {
const customValidator: Validator = (value) => {
if (value?.length < 4) {
return new Error('My error')
@@ -324,7 +350,7 @@ describe('Field.NationalIdentityNumber', () => {
@@ -333,7 +359,7 @@ describe('Field.NationalIdentityNumber', () => {
expect(screen.queryByRole('alert')).toBeNull()
})
- it('should not validate extended validator when validate false', async () => {
+ it('should not validate extended onChangeValidator when validate false', async () => {
const invalidFnrBornInApril = '29040112345'
const bornInApril = (value: string) => value.substring(2, 4) === '04'
@@ -352,7 +378,7 @@ describe('Field.NationalIdentityNumber', () => {
value={invalidFnrBornInApril}
validateInitially
validate={false}
- validator={customValidator}
+ onChangeValidator={customValidator}
/>
)
@@ -507,7 +533,7 @@ describe('Field.NationalIdentityNumber', () => {
)
})
- describe('should extend validation using custom validator', () => {
+ describe('should extend validation using custom onChangeValidator', () => {
const validFnrNumApril = ['14046512368', '10042223293']
const validDNumApril = ['51041678171']
@@ -542,7 +568,7 @@ describe('Field.NationalIdentityNumber', () => {
it.each(validIds)('Valid identity number: %s', async (fnrNum) => {
render(
@@ -554,7 +580,7 @@ describe('Field.NationalIdentityNumber', () => {
it.each(invalidIds)('Invalid identity number: %s', async (id) => {
render(
@@ -571,7 +597,7 @@ describe('Field.NationalIdentityNumber', () => {
it.each(invalidDNumApril)('Invalid D number: %s', async (dNum) => {
render(
@@ -588,7 +614,7 @@ describe('Field.NationalIdentityNumber', () => {
it.each(invalidDNumTooShort)('Invalid D number: %s', async (dNum) => {
render(
@@ -607,7 +633,7 @@ describe('Field.NationalIdentityNumber', () => {
async (fnr) => {
render(
@@ -627,7 +653,7 @@ describe('Field.NationalIdentityNumber', () => {
async (fnr) => {
render(
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberMinimumAgeValidator.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberMinimumAgeValidator.test.tsx
index 30e958f7212..d8c0f56b462 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberMinimumAgeValidator.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberMinimumAgeValidator.test.tsx
@@ -186,13 +186,13 @@ describe('Field.NationalIdentityNumber with minimumAgeValidator', () => {
const invalidDnums = ['69020112345', '690']
const invalidFnrs = ['29020112345', '290']
- describe('when provided as the only validator validation function', () => {
+ describe('when provided as the onChangeValidator function', () => {
it.each(validIds)(
'Identity number is 18 years or older : %s',
async (validId) => {
render(
{
async (invalidId) => {
render(
{
)
})
- describe('when extending the dnrAndFnrValidator as validator', () => {
+ describe('when extending the dnrAndFnrValidator as onChangeValidator', () => {
it.each(validIds)(
'Identity number is 18 years or older : %s',
async (validId) => {
render(
@@ -289,7 +291,9 @@ describe('Field.NationalIdentityNumber with minimumAgeValidator', () => {
render(
@@ -309,7 +313,9 @@ describe('Field.NationalIdentityNumber with minimumAgeValidator', () => {
render(
@@ -329,7 +335,9 @@ describe('Field.NationalIdentityNumber with minimumAgeValidator', () => {
render(
@@ -428,14 +436,14 @@ describe('Field.NationalIdentityNumber with minimumAgeValidator', () => {
)
})
- describe('when extending the dnrValidator as validator', () => {
+ describe('when extending the dnrValidator as onChangeValidator', () => {
it.each(dnr18YearsOldAndOlder)(
'D number is 18 years or older : %s',
async (validDnum) => {
render(
@@ -453,7 +461,7 @@ describe('Field.NationalIdentityNumber with minimumAgeValidator', () => {
render(
@@ -473,7 +481,7 @@ describe('Field.NationalIdentityNumber with minimumAgeValidator', () => {
render(
@@ -547,14 +555,14 @@ describe('Field.NationalIdentityNumber with minimumAgeValidator', () => {
})
})
- describe('when extending the fnrValidator as validator', () => {
+ describe('when extending the fnrValidator as onChangeValidator', () => {
it.each(fnr18YearsOldAndOlder)(
'Identity number(fnr) is 18 years or older : %s',
async (validFnr) => {
render(
@@ -572,7 +580,7 @@ describe('Field.NationalIdentityNumber with minimumAgeValidator', () => {
render(
@@ -592,7 +600,7 @@ describe('Field.NationalIdentityNumber with minimumAgeValidator', () => {
render(
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/stories/NationalIdentityNumber.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/stories/NationalIdentityNumber.stories.tsx
index 02b55500e24..3df8b1737ca 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/stories/NationalIdentityNumber.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/stories/NationalIdentityNumber.stories.tsx
@@ -51,34 +51,34 @@ export function ValidatorsUndefinedFalse() {
return (
Validate Initially:
@@ -175,34 +175,34 @@ export function NationalIdentityNumberValidator() {
Validate Initially:
@@ -245,34 +245,34 @@ export function DNumberValidator() {
Validate Initially:
@@ -342,67 +342,67 @@ export function AdultValidator() {
Validate Initially:
@@ -464,56 +464,56 @@ export function AdultValidatorAndDefaultValidator() {
return (
Validate Initially:
@@ -525,56 +525,56 @@ export function CustomValidatorFunction() {
return (
Validate Initially:
@@ -643,56 +643,56 @@ export function CustomValidatorFunctionReturnArray() {
return (
Validate Initially:
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Number/__tests__/Number.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/Number/__tests__/Number.test.tsx
index 4815ed4a3da..4cbb1b16de4 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/Number/__tests__/Number.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/Number/__tests__/Number.test.tsx
@@ -548,29 +548,32 @@ describe('Field.Number', () => {
expect(screen.queryByRole('alert')).toBeInTheDocument()
})
- it('should call validator with validateInitially', async () => {
- const validator = jest.fn(() => {
+ it('should call onChangeValidator with validateInitially', async () => {
+ const onChangeValidator = jest.fn(() => {
return new Error('Validator message')
})
render(
)
- expect(validator).toHaveBeenCalledTimes(1)
- expect(validator).toHaveBeenCalledWith(123, expect.anything())
+ expect(onChangeValidator).toHaveBeenCalledTimes(1)
+ expect(onChangeValidator).toHaveBeenCalledWith(
+ 123,
+ expect.anything()
+ )
await waitFor(() => {
expect(screen.queryByRole('alert')).toBeInTheDocument()
})
})
- it('should call validator on form submit', async () => {
- const validator = jest.fn(() => {
+ it('should call onChangeValidator on form submit', async () => {
+ const onChangeValidator = jest.fn(() => {
return new Error('Validator message')
})
@@ -578,7 +581,7 @@ describe('Field.Number', () => {
@@ -586,8 +589,11 @@ describe('Field.Number', () => {
fireEvent.submit(document.querySelector('form'))
- expect(validator).toHaveBeenCalledTimes(1)
- expect(validator).toHaveBeenCalledWith(123, expect.anything())
+ expect(onChangeValidator).toHaveBeenCalledTimes(1)
+ expect(onChangeValidator).toHaveBeenCalledWith(
+ 123,
+ expect.anything()
+ )
await waitFor(() => {
expect(screen.queryByRole('alert')).toBeInTheDocument()
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Number/stories/Number.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/Number/stories/Number.stories.tsx
index 87b383f3810..d74f85e5e5f 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/Number/stories/Number.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/Number/stories/Number.stories.tsx
@@ -45,8 +45,8 @@ export const Number = () => {
}
export const WithFreshValidator = () => {
- const validator: UseFieldProps['validator'] = useCallback(
- (num, { connectWithPath }) => {
+ const validator: UseFieldProps['onChangeValidator'] =
+ useCallback((num, { connectWithPath }) => {
const { getValue } = connectWithPath('/refValue')
const amount = getValue()
// console.log('amount', amount, amount >= num)
@@ -56,9 +56,7 @@ export const WithFreshValidator = () => {
if (num === undefined) {
return new Error(`No amount was given`)
}
- },
- []
- )
+ }, [])
return (
{
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Option/Option.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/Option/Option.tsx
index 6ed84586d87..f499fb390e1 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/Option/Option.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/Option/Option.tsx
@@ -20,7 +20,7 @@ export default function Option({
// eslint-disable-next-line jsx-a11y/role-has-required-aria-props
role="option"
>
- {children ?? title}
+ {title ?? children}
{text}
)
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Option/OptionDocs.ts b/packages/dnb-eufemia/src/extensions/forms/Field/Option/OptionDocs.ts
index 2671ebd960c..c078982357e 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/Option/OptionDocs.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/Option/OptionDocs.ts
@@ -8,18 +8,23 @@ export const OptionProperties: PropertiesTableProps = {
status: 'optional',
},
title: {
- doc: 'Text title for the option.',
- type: 'string',
+ doc: 'Title for the option. Overrides `children`.',
+ type: ['string', 'React.Node'],
status: 'optional',
},
text: {
doc: 'Secondary text.',
- type: 'string',
+ type: ['string', 'React.Node'],
+ status: 'optional',
+ },
+ disabled: {
+ doc: 'Will disable the option.',
+ type: 'boolean',
status: 'optional',
},
help: FieldProperties.help,
children: {
- doc: 'Optional way to provide `title`.',
+ doc: 'Optional way to provide `title`. Will be ignored if `title` is used.',
type: 'React.Node',
status: 'optional',
},
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/OrganizationNumber/OrganizationNumber.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/OrganizationNumber/OrganizationNumber.tsx
index 79dabac77ee..2b6afda008d 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/OrganizationNumber/OrganizationNumber.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/OrganizationNumber/OrganizationNumber.tsx
@@ -42,7 +42,9 @@ function OrganizationNumber(props: Props) {
const {
validate = true,
omitMask,
+ // Deprecated – can be removed in v11
validator,
+ onChangeValidator = validator,
onBlurValidator = organizationNumberValidator,
label: labelProp,
width,
@@ -67,7 +69,7 @@ function OrganizationNumber(props: Props) {
mask,
width: width ?? 'medium',
inputMode: 'numeric',
- validator: validate ? validator : undefined,
+ onChangeValidator: validate ? onChangeValidator : undefined,
onBlurValidator: validate ? onBlurValidatorToUse : undefined,
exportValidators: { organizationNumberValidator },
}
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/OrganizationNumber/__tests__/OrganizationNumber.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/OrganizationNumber/__tests__/OrganizationNumber.test.tsx
index 30e402dcec3..e4843eb9229 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/OrganizationNumber/__tests__/OrganizationNumber.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/OrganizationNumber/__tests__/OrganizationNumber.test.tsx
@@ -95,6 +95,57 @@ describe('Field.OrganizationNumber', () => {
})
})
+ // Deprecated – can be removed in v11
+ it('should validate given function as validator', async () => {
+ const text = 'Custom Error message'
+ const validator = jest.fn((value) => {
+ return value.length < 4 ? new Error(text) : undefined
+ })
+
+ render(
+
+ )
+
+ await waitFor(() => {
+ expect(validator).toHaveBeenCalledTimes(1)
+ })
+
+ const element = document.querySelector('.dnb-form-status')
+
+ expect(element).toBeInTheDocument()
+ expect(element.textContent).toBe(text)
+ })
+
+ it('should validate given function as onChangeValidator', async () => {
+ const text = 'Custom Error message'
+ const onChangeValidator = jest.fn((value) => {
+ return value.length < 4 ? new Error(text) : undefined
+ })
+
+ render(
+
+ )
+
+ await waitFor(() => {
+ expect(onChangeValidator).toHaveBeenCalledTimes(1)
+ })
+
+ const element = document.querySelector('.dnb-form-status')
+
+ expect(element).toBeInTheDocument()
+ expect(element.textContent).toBe(text)
+ })
+
it('should display error if required and validateInitially', async () => {
render( )
@@ -289,7 +340,7 @@ describe('Field.OrganizationNumber', () => {
value={invalidOrgNo}
validateInitially
validate={false}
- validator={customValidator}
+ onChangeValidator={customValidator}
onBlurValidator={false}
/>
@@ -326,7 +377,7 @@ describe('Field.OrganizationNumber', () => {
value={invalidOrgNo}
validateInitially
validate={false}
- validator={customValidator}
+ onChangeValidator={customValidator}
onBlurValidator={false}
/>
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/OrganizationNumber/stories/OrganizationNumber.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/OrganizationNumber/stories/OrganizationNumber.stories.tsx
index 4af93be8b42..51db385f261 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/OrganizationNumber/stories/OrganizationNumber.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/OrganizationNumber/stories/OrganizationNumber.stories.tsx
@@ -98,34 +98,34 @@ export function OrganizationNumberValidator() {
Validate Initially:
@@ -141,34 +141,34 @@ export function CustomValidator() {
Validate Initially:
@@ -223,34 +223,34 @@ export function CustomValidatorReturnArray() {
Validate Initially:
@@ -307,19 +307,22 @@ export function StringValidatorSimple() {
return (
-
-
-
+
+
+
Validate Initially:
-
+
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/PhoneNumber.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/PhoneNumber.tsx
index e44d9df3a7f..44c490d08a4 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/PhoneNumber.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/PhoneNumber.tsx
@@ -27,7 +27,7 @@ import {
getCountryData,
} from '../SelectCountry'
import useTranslation from '../../hooks/useTranslation'
-import { DrawerListDataObject } from '../../../../fragments/DrawerList'
+import { DrawerListDataArrayItem } from '../../../../fragments/DrawerList'
export type Props = Omit<
FieldPropsWithExtraValue<
@@ -95,7 +95,7 @@ function PhoneNumber(props: Props) {
const countryCodeRef = useRef(props?.emptyValue)
const numberRef = useRef(props?.emptyValue)
- const dataRef = useRef>(null)
+ const dataRef = useRef>(null)
const langRef = useRef(lang)
const wasFilled = useRef(false)
@@ -433,7 +433,7 @@ function PhoneNumber(props: Props) {
width={
omitCountryCodeField ? 'medium' : props.width ?? 'stretch'
}
- help={{ ...help, breakout: false }}
+ help={{ ...help, breakout: false, outset: false }}
required={required}
errorMessages={errorMessages}
validateInitially={validateInitially}
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/__tests__/PhoneNumber.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/__tests__/PhoneNumber.test.tsx
index 29efd59316e..4e605f8fd71 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/__tests__/PhoneNumber.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/__tests__/PhoneNumber.test.tsx
@@ -669,23 +669,23 @@ describe('Field.PhoneNumber', () => {
expect(document.querySelector('[role="alert"]')).toBeInTheDocument()
})
- it('should handle "validator" property with country code', async () => {
- const validator = jest.fn(() => {
+ it('should handle "onChangeValidator" property with country code', async () => {
+ const onChangeValidator = jest.fn(() => {
return new Error('some error')
})
render(
)
- expect(validator).toHaveBeenCalledTimes(1)
- expect(validator).toHaveBeenCalledWith(
+ expect(onChangeValidator).toHaveBeenCalledTimes(1)
+ expect(onChangeValidator).toHaveBeenCalledWith(
'+41 9999',
expect.objectContaining({
errorMessages: expect.objectContaining({
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/stories/PhoneNumber.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/stories/PhoneNumber.stories.tsx
index 202ab3d8e48..b1bf2fbf5a9 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/stories/PhoneNumber.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/stories/PhoneNumber.stories.tsx
@@ -16,7 +16,7 @@ const makeRequest = async (value) => {
})
}
-const validator = async (value) => {
+const onChangeValidator = async (value) => {
// Delay the response
const isValid = await makeRequest(value)
if (!isValid) {
@@ -36,7 +36,7 @@ export function PhoneNumber() {
({
+ 'Field.errorRequired': translations.PostalCode.errorRequired,
+ 'Field.errorPattern': translations.PostalCode.errorPattern,
+ ...postalCodeErrorMessages,
+ }),
+ [
+ postalCodeErrorMessages,
+ translations.PostalCode.errorPattern,
+ translations.PostalCode.errorRequired,
+ ]
+ )}
width={postalCodeWidth ?? false}
inputClassName="dnb-forms-field-postal-code-and-city__postal-code-input"
inputMode="numeric"
@@ -121,11 +124,18 @@ function PostalCodeAndCity(props: Props) {
cityClassName
)}
label={cityLabel ?? translations.City.label}
- errorMessages={{
- 'Field.errorRequired': translations.City.errorRequired,
- 'Field.errorPattern': translations.City.errorPattern,
- ...cityErrorMessages,
- }}
+ errorMessages={useMemo(
+ () => ({
+ 'Field.errorRequired': translations.City.errorRequired,
+ 'Field.errorPattern': translations.City.errorPattern,
+ ...cityErrorMessages,
+ }),
+ [
+ cityErrorMessages,
+ translations.City.errorPattern,
+ translations.City.errorRequired,
+ ]
+ )}
pattern={cityPattern ?? '^[A-Za-zÆØÅæøå -]+$'}
trim
width={cityWidth ?? 'stretch'}
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Selection/Selection.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/Selection/Selection.tsx
index ab6e73776b3..86aa5186f96 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/Selection/Selection.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/Selection/Selection.tsx
@@ -41,6 +41,7 @@ export type Data = Array<{
value: string
title: React.ReactNode
text?: React.ReactNode
+ disabled?: boolean
}>
export type Props = FieldProps & {
@@ -364,7 +365,12 @@ function renderRadioItems({
].filter(Boolean)
}
-export function mapOptions(children: React.ReactNode, { createOption }) {
+export function mapOptions(
+ children: React.ReactNode,
+ {
+ createOption,
+ }: { createOption: (props: OptionProps, i: number) => React.ReactNode }
+) {
return React.Children.map(
children,
(child: React.ReactElement, i) => {
@@ -396,11 +402,12 @@ export function makeOptions(
if (React.isValidElement(child) && child.type === OptionField) {
const props = child.props as OptionFieldProps
- const title = props.children ?? props.title ?? Untitled
+ const title = props.title ?? props.children ?? Untitled
const content = props.text ? [title, props.text] : title
const selectedKey = String(props.value ?? '')
+ const disabled = props.disabled
- return { selectedKey, content }
+ return { selectedKey, content, disabled }
}
// For other children, just show them as content
@@ -414,10 +421,11 @@ export function makeOptions(
function renderDropdownItems(data: Data) {
return (
- data?.map(({ value, title, text }) => {
+ data?.map(({ value, title, text, disabled }) => {
return {
selectedKey: value,
content: (text ? [title, text] : title) || Untitled ,
+ disabled,
}
}) || []
)
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Selection/__tests__/Selection.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/Selection/__tests__/Selection.test.tsx
index 5da99492a49..19bf5d9f214 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/Selection/__tests__/Selection.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/Selection/__tests__/Selection.test.tsx
@@ -99,26 +99,6 @@ describe('Selection', () => {
// getByText instead of getByPlaceholderText since eufemia adds placeholder as tag, not placeholder-attribute
expect(screen.getByText('Select something')).toBeInTheDocument()
})
-
- it('precede option children over title', async () => {
- render(
-
-
- child a
-
-
- child b
-
-
- )
-
- const selectionButton = document.querySelector('button')
- await userEvent.click(selectionButton)
- const options = document.querySelectorAll('.dnb-drawer-list__option')
-
- expect(options[0].textContent).toBe('child a')
- expect(options[1].textContent).toBe('child b')
- })
})
describe('variants', () => {
@@ -136,6 +116,24 @@ describe('variants', () => {
expect(radioButtons[1]).toBeChecked()
})
+ it('precede option title over children', async () => {
+ render(
+
+
+ child a
+
+
+ child b
+
+
+ )
+
+ const options = document.querySelectorAll('.dnb-radio')
+
+ expect(options[0].textContent).toBe('title a')
+ expect(options[1].textContent).toBe('title b')
+ })
+
it('renders help', () => {
render(
@@ -860,6 +858,25 @@ describe('variants', () => {
expect(options[1].getAttribute('aria-selected')).toBe('false')
})
+ it('precede option title over children', async () => {
+ render(
+
+
+ child a
+
+
+ child b
+
+
+ )
+
+ open()
+ const options = document.querySelectorAll('[role="option"]')
+
+ expect(options[0].textContent).toBe('title a')
+ expect(options[1].textContent).toBe('title b')
+ })
+
it('renders help', () => {
render(
+
-
+
)
}
export function Autocomplete() {
return (
-
+
-
+
)
}
export function HelpButton() {
return (
-
+
-
+
)
}
export function NestingWithLogic() {
return (
-
+
-
+
)
}
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/String/__tests__/String.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/String/__tests__/String.test.tsx
index fd73076f7bf..2e36e267f57 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/String/__tests__/String.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/String/__tests__/String.test.tsx
@@ -917,20 +917,20 @@ describe('Field.String', () => {
})
})
- describe('validation using a synchronous external validator function', () => {
- it('should show error returned by validator', async () => {
- const validator = jest.fn(syncValidatorReturningError)
+ describe('validation using a synchronous external onChangeValidator function', () => {
+ it('should show error returned by onChangeValidator', async () => {
+ const onChangeValidator = jest.fn(syncValidatorReturningError)
render(
)
await waitFor(() => {
// Wait for since external validators are processed asynchronously
- expect(validator).toHaveBeenCalledTimes(1)
- expect(validator).toHaveBeenNthCalledWith(
+ expect(onChangeValidator).toHaveBeenCalledTimes(1)
+ expect(onChangeValidator).toHaveBeenNthCalledWith(
1,
'abc',
expect.anything()
@@ -945,18 +945,18 @@ describe('Field.String', () => {
fireEvent.blur(input)
await waitFor(() => {
- expect(validator).toHaveBeenCalledTimes(4)
- expect(validator).toHaveBeenNthCalledWith(
+ expect(onChangeValidator).toHaveBeenCalledTimes(4)
+ expect(onChangeValidator).toHaveBeenNthCalledWith(
2,
'abcd',
expect.anything()
)
- expect(validator).toHaveBeenNthCalledWith(
+ expect(onChangeValidator).toHaveBeenNthCalledWith(
3,
'abcde',
expect.anything()
)
- expect(validator).toHaveBeenNthCalledWith(
+ expect(onChangeValidator).toHaveBeenNthCalledWith(
4,
'abcdef',
expect.anything()
@@ -967,12 +967,12 @@ describe('Field.String', () => {
})
})
- it('should not show error when validator returns undefined', async () => {
- const validator = jest.fn(syncValidatorReturningUndefined)
+ it('should not show error when onChangeValidator returns undefined', async () => {
+ const onChangeValidator = jest.fn(syncValidatorReturningUndefined)
render(
)
@@ -982,20 +982,20 @@ describe('Field.String', () => {
})
})
- describe('validation using an asynchronous external validator function', () => {
- it('should show error returned by validator', async () => {
- const validator = jest.fn(asyncValidatorResolvingWithError)
+ describe('validation using an asynchronous external onChangeValidator function', () => {
+ it('should show error returned by onChangeValidator', async () => {
+ const onChangeValidator = jest.fn(asyncValidatorResolvingWithError)
render(
)
await waitFor(() => {
// Wait for since external validators are processed asynchronously
- expect(validator).toHaveBeenCalledTimes(1)
- expect(validator).toHaveBeenNthCalledWith(
+ expect(onChangeValidator).toHaveBeenCalledTimes(1)
+ expect(onChangeValidator).toHaveBeenNthCalledWith(
1,
'abc',
expect.anything()
@@ -1012,18 +1012,18 @@ describe('Field.String', () => {
fireEvent.blur(input)
})
- expect(validator).toHaveBeenCalledTimes(4)
- expect(validator).toHaveBeenNthCalledWith(
+ expect(onChangeValidator).toHaveBeenCalledTimes(4)
+ expect(onChangeValidator).toHaveBeenNthCalledWith(
2,
'abcd',
expect.anything()
)
- expect(validator).toHaveBeenNthCalledWith(
+ expect(onChangeValidator).toHaveBeenNthCalledWith(
3,
'abcde',
expect.anything()
)
- expect(validator).toHaveBeenNthCalledWith(
+ expect(onChangeValidator).toHaveBeenNthCalledWith(
4,
'abcdef',
expect.anything()
@@ -1033,12 +1033,14 @@ describe('Field.String', () => {
).toBeInTheDocument()
})
- it('should not show error when validator returns undefined', async () => {
- const validator = jest.fn(asyncValidatorResolvingWithUndefined)
+ it('should not show error when onChangeValidator returns undefined', async () => {
+ const onChangeValidator = jest.fn(
+ asyncValidatorResolvingWithUndefined
+ )
render(
)
@@ -1050,19 +1052,19 @@ describe('Field.String', () => {
})
describe('validation using a synchronous external onBlurValidator function', () => {
- it('should show error returned by validator', async () => {
- const validator = jest.fn(syncValidatorReturningError)
+ it('should show error returned by onBlurValidator', async () => {
+ const onBlurValidator = jest.fn(syncValidatorReturningError)
render(
)
await waitFor(() => {
// Wait for since external validators are processed asynchronously
- expect(validator).toHaveBeenCalledTimes(1)
+ expect(onBlurValidator).toHaveBeenCalledTimes(1)
expect(screen.queryByRole('alert')).toBeInTheDocument()
})
const input = document.querySelector('input')
@@ -1071,13 +1073,13 @@ describe('Field.String', () => {
await waitFor(() => {
// Wait for since external validators are processed asynchronously
- expect(validator).toHaveBeenCalledTimes(2)
- expect(validator).toHaveBeenNthCalledWith(
+ expect(onBlurValidator).toHaveBeenCalledTimes(2)
+ expect(onBlurValidator).toHaveBeenNthCalledWith(
1,
'abc',
expect.anything()
)
- expect(validator).toHaveBeenNthCalledWith(
+ expect(onBlurValidator).toHaveBeenNthCalledWith(
2,
'abcdef',
expect.anything()
@@ -1089,12 +1091,12 @@ describe('Field.String', () => {
})
})
- it('should not show error when validator returns undefined', async () => {
- const validator = jest.fn(syncValidatorReturningUndefined)
+ it('should not show error when onBlurValidator returns undefined', async () => {
+ const onBlurValidator = jest.fn(syncValidatorReturningUndefined)
render(
)
@@ -1108,19 +1110,19 @@ describe('Field.String', () => {
})
describe('validation using an asynchronous external onBlurValidator function', () => {
- it('should show error returned by validator', async () => {
- const validator = jest.fn(asyncValidatorResolvingWithError)
+ it('should show error returned by onBlurValidator', async () => {
+ const onBlurValidator = jest.fn(asyncValidatorResolvingWithError)
render(
)
await waitFor(() => {
// Wait for since external validators are processed asynchronously
- expect(validator).toHaveBeenCalledTimes(1)
+ expect(onBlurValidator).toHaveBeenCalledTimes(1)
expect(screen.queryByRole('alert')).toBeInTheDocument()
})
const input = document.querySelector('input')
@@ -1129,13 +1131,13 @@ describe('Field.String', () => {
await waitFor(() => {
// Wait for since external validators are processed asynchronously
- expect(validator).toHaveBeenCalledTimes(2)
- expect(validator).toHaveBeenNthCalledWith(
+ expect(onBlurValidator).toHaveBeenCalledTimes(2)
+ expect(onBlurValidator).toHaveBeenNthCalledWith(
1,
'abc',
expect.anything()
)
- expect(validator).toHaveBeenNthCalledWith(
+ expect(onBlurValidator).toHaveBeenNthCalledWith(
2,
'abcdef',
expect.anything()
@@ -1147,12 +1149,14 @@ describe('Field.String', () => {
})
})
- it('should not show error when validator returns undefined', async () => {
- const validator = jest.fn(asyncValidatorResolvingWithUndefined)
+ it('should not show error when onBlurValidator returns undefined', async () => {
+ const onBlurValidator = jest.fn(
+ asyncValidatorResolvingWithUndefined
+ )
render(
)
@@ -1234,7 +1238,7 @@ describe('Field.String', () => {
).toBe('At least 4.')
})
- it('should provide error message to the validator', async () => {
+ it('should provide error message to the onBlurValidator', async () => {
let collectDeprecatedMessage = null
let collectCustomMessage = null
const customMessage = 'Your custom error message'
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/String/stories/String.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/String/stories/String.stories.tsx
index 97f01bb2125..0e132a3a0da 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/String/stories/String.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/String/stories/String.stories.tsx
@@ -106,7 +106,7 @@ export function ErrorMessages() {
{
+ onChangeValidator={() => {
return new FormError('OrganizationNumber.errorRequired')
}}
errorMessages={{
@@ -120,7 +120,7 @@ export function ErrorMessages() {
value="abc"
minLength={4}
// pattern="[0-9]"
- // validator={() => {
+ // onChangeValidator={() => {
// return new FormError('OrganizationNumber.errorRequired')
// }}
// errorMessages={{
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Upload/Upload.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/Upload/Upload.tsx
index 8430e563aba..b2ced390916 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/Upload/Upload.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/Upload/Upload.tsx
@@ -40,7 +40,9 @@ export type Props = Omit<
| 'onFileDelete'
| 'skeleton'
> & {
- asyncFileHandler?: (newFiles: UploadValue) => Promise
+ fileHandler?: (
+ newFiles: UploadValue
+ ) => UploadValue | Promise
}
const validateRequired = (
@@ -62,7 +64,7 @@ const validateRequired = (
const updateFileLoadingState = (
files: UploadValue,
- isLoading: boolean
+ { isLoading } = { isLoading: false }
) => {
return files.map((file) => ({ ...file, isLoading }))
}
@@ -96,7 +98,7 @@ function UploadComponent(props: Props) {
handleChange,
handleFocus,
handleBlur,
- asyncFileHandler,
+ fileHandler,
...rest
} = useFieldProps(preparedProps, {
executeOnChangeRegardlessOfError: true,
@@ -131,20 +133,21 @@ function UploadComponent(props: Props) {
// Set loading
setFiles([
...fileContext,
- ...updateFileLoadingState(newFiles, true),
+ ...updateFileLoadingState(newFiles, { isLoading: true }),
])
const uploadedFiles = updateFileLoadingState(
- await asyncFileHandler(newFiles),
- false
+ await fileHandler(newFiles),
+ { isLoading: false }
)
+ // Set error, if any
handleChange([...fileContext, ...uploadedFiles])
} else {
handleChange(files)
}
},
- [fileContext, asyncFileHandler, setFiles, updateFileLoadingState]
+ [fileContext, setFiles, fileHandler, handleChange]
)
const changeHandler = useCallback(
@@ -153,13 +156,13 @@ function UploadComponent(props: Props) {
handleBlur()
handleFocus()
- if (asyncFileHandler) {
+ if (fileHandler) {
handleChangeAsync(files)
} else {
handleChange(files)
}
},
- [handleBlur, handleChange, handleFocus, asyncFileHandler, fileContext]
+ [handleBlur, handleFocus, fileHandler, handleChangeAsync, handleChange]
)
const width = widthProp as FieldBlockWidth
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Upload/UploadDocs.ts b/packages/dnb-eufemia/src/extensions/forms/Field/Upload/UploadDocs.ts
index 0fdfe4b5949..9563077829b 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/Upload/UploadDocs.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/Upload/UploadDocs.ts
@@ -5,6 +5,11 @@ import {
import { PropertiesTableProps } from '../../../../shared/types'
export const UploadFieldProperties: PropertiesTableProps = {
+ fileHandler: {
+ doc: 'File handler function that takes newly added files (`newFiles: UploadValue`) as a parameter and returns the processed files. The function can either be synchronous or asynchronous. It returns a promise (`Promise`) containing the processed files when asynchronous.',
+ type: 'function',
+ status: 'optional',
+ },
...UploadProperties,
title: undefined,
text: undefined,
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Upload/__tests__/Upload.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/Upload/__tests__/Upload.test.tsx
index cc653ce581f..fe53f07ca5b 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/Upload/__tests__/Upload.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/Upload/__tests__/Upload.test.tsx
@@ -905,7 +905,51 @@ describe('Field.Upload', () => {
).toHaveTextContent(nbForms.Upload.errorRequired)
})
- it('should handle displaying error from asyncFileHandler', async () => {
+ it('should handle displaying error from fileHandler with sync function', async () => {
+ const fileValid = createMockFile('1.png', 100, 'image/png')
+ const fileInValid = createMockFile('invalid.png', 100, 'image/png')
+
+ const syncFileHandlerFnError = function mockSyncFileUpload(
+ newFiles: UploadValue
+ ) {
+ return newFiles.map((file) => {
+ if (file.file.name.length > 5) {
+ file.errorMessage = 'File name is too long'
+ }
+ return file
+ })
+ }
+
+ render( )
+
+ const element = getRootElement()
+
+ await waitFor(() =>
+ fireEvent.drop(element, {
+ dataTransfer: {
+ files: [fileValid],
+ },
+ })
+ )
+
+ expect(
+ document.querySelector('.dnb-form-status')
+ ).not.toBeInTheDocument()
+
+ await waitFor(() =>
+ fireEvent.drop(element, {
+ dataTransfer: {
+ files: [fileInValid],
+ },
+ })
+ )
+
+ expect(document.querySelector('.dnb-form-status')).toHaveTextContent(
+ 'File name is too long'
+ )
+ })
+
+ it('should handle displaying error from fileHandler with async function', async () => {
const file = createMockFile('fileName-1.png', 100, 'image/png')
const asyncValidatorResolvingWithErrorMessage = () =>
@@ -928,7 +972,7 @@ describe('Field.Upload', () => {
asyncValidatorResolvingWithErrorMessage
)
- render( )
+ render( )
const element = getRootElement()
@@ -949,7 +993,7 @@ describe('Field.Upload', () => {
})
})
- it('should handle displaying success from asyncFileHandler', async () => {
+ it('should handle displaying success from fileHandler with async function', async () => {
const file = createMockFile('fileName-1.png', 100, 'image/png')
const asyncValidatorResolvingWithSuccess = () =>
@@ -971,7 +1015,7 @@ describe('Field.Upload', () => {
asyncValidatorResolvingWithSuccess
)
- render( )
+ render( )
const element = getRootElement()
@@ -992,16 +1036,14 @@ describe('Field.Upload', () => {
})
})
- it('should display spinner when loading asyncFileHandler', async () => {
+ it('should display spinner when loading fileHandler with async function', async () => {
const file = createMockFile('fileName-1.png', 100, 'image/png')
const asyncValidatorResolvingWithSuccess = () =>
new Promise(() => jest.fn())
render(
-
+
)
const element = getRootElement()
@@ -1027,5 +1069,188 @@ describe('Field.Upload', () => {
).toBeInTheDocument()
})
})
+
+ it('should add new files from fileHandler with async function', async () => {
+ const fileExisting = createMockFile(
+ 'fileName-existing.png',
+ 100,
+ 'image/png'
+ )
+ const newFile1 = createMockFile(
+ 'fileName-new-1.png',
+ 100,
+ 'image/png'
+ )
+ const newFile2 = createMockFile(
+ 'fileName-new-2.png',
+ 100,
+ 'image/png'
+ )
+
+ const asyncValidatorResolvingWithSuccess = () =>
+ new Promise((resolve) =>
+ setTimeout(
+ () =>
+ resolve([
+ {
+ file: newFile1,
+ id: 'server_generated_id_1',
+ exists: false,
+ },
+ {
+ file: newFile2,
+ id: 'server_generated_id_2',
+ exists: false,
+ },
+ ]),
+ 1
+ )
+ )
+
+ const asyncFileHandlerFnSuccess = jest.fn(
+ asyncValidatorResolvingWithSuccess
+ )
+
+ render(
+
+ )
+
+ expect(
+ document.querySelectorAll('.dnb-upload__file-cell').length
+ ).toBe(1)
+ expect(
+ screen.queryByText('fileName-existing.png')
+ ).toBeInTheDocument()
+ expect(
+ screen.queryByText('fileName-new-1.png')
+ ).not.toBeInTheDocument()
+ expect(
+ screen.queryByText('fileName-new-2.png')
+ ).not.toBeInTheDocument()
+
+ const element = getRootElement()
+
+ await waitFor(() => {
+ fireEvent.drop(element, {
+ dataTransfer: {
+ files: [newFile1, newFile2],
+ },
+ })
+ expect(
+ document.querySelectorAll('.dnb-upload__file-cell').length
+ ).toBe(3)
+ expect(
+ screen.queryByText('fileName-existing.png')
+ ).toBeInTheDocument()
+ expect(
+ screen.queryByText('fileName-new-1.png')
+ ).toBeInTheDocument()
+ expect(
+ screen.queryByText('fileName-new-2.png')
+ ).toBeInTheDocument()
+ })
+ })
+
+ it('should not add existing file using fileHandler with async function', async () => {
+ const file = createMockFile('fileName.png', 100, 'image/png')
+
+ const asyncValidatorResolvingWithSuccess = () =>
+ new Promise((resolve) =>
+ setTimeout(
+ () =>
+ resolve([
+ {
+ file,
+ },
+ ]),
+ 1
+ )
+ )
+
+ const asyncFileHandlerFnSuccess = jest.fn(
+ asyncValidatorResolvingWithSuccess
+ )
+
+ render(
+
+ )
+
+ expect(
+ document.querySelectorAll('.dnb-upload__file-cell').length
+ ).toBe(1)
+ expect(screen.queryByText('fileName.png')).toBeInTheDocument()
+
+ const element = getRootElement()
+
+ await waitFor(() => {
+ fireEvent.drop(element, {
+ dataTransfer: {
+ files: [file],
+ },
+ })
+ expect(
+ document.querySelectorAll('.dnb-upload__file-cell').length
+ ).toBe(1)
+ expect(screen.queryByText('fileName.png')).toBeInTheDocument()
+ })
+ })
+ })
+
+ it('should handle a mix of successful and failed files in fileHandler with async function', async () => {
+ const successFile = createMockFile('successFile.png', 100, 'image/png')
+ const failFile = createMockFile('failFile.png', 100, 'image/png')
+
+ const asyncValidatorWithMixedResults = () =>
+ new Promise((resolve) =>
+ setTimeout(
+ () =>
+ resolve([
+ {
+ file: successFile,
+ id: 'server_generated_id',
+ exists: false,
+ },
+ {
+ file: failFile,
+ id: 'internal_id_fail',
+ exists: false,
+ errorMessage: 'Failed to process',
+ },
+ ]),
+ 1
+ )
+ )
+
+ const asyncFileHandlerFn = jest.fn(asyncValidatorWithMixedResults)
+
+ render( )
+
+ const element = getRootElement()
+
+ await waitFor(() =>
+ fireEvent.drop(element, {
+ dataTransfer: {
+ files: [successFile, failFile],
+ },
+ })
+ )
+
+ await waitFor(() => {
+ expect(asyncFileHandlerFn).toHaveBeenCalledTimes(1)
+ expect(
+ document.querySelectorAll('.dnb-upload__file-cell').length
+ ).toBe(2)
+ expect(screen.queryByText('successFile.png')).toBeInTheDocument()
+ expect(screen.queryByText('failFile.png')).toBeInTheDocument()
+ expect(document.querySelector('.dnb-form-status')).toHaveTextContent(
+ 'Failed to process'
+ )
+ })
})
})
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Upload/stories/Upload.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/Upload/stories/Upload.stories.tsx
index fc72908ae49..263b6a86106 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/Upload/stories/Upload.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/Upload/stories/Upload.stories.tsx
@@ -1,27 +1,19 @@
-import { Field, Form } from '../../..'
+import { Field, Form, Tools } from '../../..'
import { Flex } from '../../../../../components'
-// import { createMockFile } from '../../../../../components/upload/__tests__/testHelpers'
+import useUpload from '../../../../../components/upload/useUpload'
+import { createRequest } from '../../../Form/Handler/stories/FormHandler.stories'
+import { UploadValue } from '../Upload'
export default {
title: 'Eufemia/Extensions/Forms/Upload',
}
export function Upload() {
- // const { setFiles } = OriginalUpload.useUpload('unique-id')
-
- // React.useEffect(() => {
- // setFiles([
- // { file: createMockFile('fileName-1.png', 100, 'image/png') },
- // ])
- // }, [setFiles])
-
return (
{
console.log('global onChange', data)
@@ -47,3 +39,65 @@ export function Upload() {
)
}
+
+async function mockAsyncFileUpload__withoutPromises(
+ newFiles: UploadValue
+): Promise {
+ const updatedFiles: UploadValue = []
+
+ for (const [index, file] of Object.entries(newFiles)) {
+ const formData = new FormData()
+ formData.append('file', file.file, file.file.name)
+
+ const request = createRequest()
+ await request(Math.floor(Math.random() * 2000) + 1000) // Simulate a request
+
+ try {
+ const mockResponse = {
+ ok: (parseFloat(index) + 2) % 2 === 0, // Every other request will fail
+ json: async () => ({
+ server_generated_id: `${file.file.name}_${crypto.randomUUID()}`,
+ }),
+ }
+
+ if (!mockResponse.ok) {
+ throw new Error('Unable to upload this file')
+ }
+
+ const data = await mockResponse.json()
+ updatedFiles.push({
+ ...file,
+ id: data.server_generated_id,
+ })
+ } catch (error: any) {
+ updatedFiles.push({
+ ...file,
+ errorMessage: error.message,
+ })
+ }
+ }
+
+ return updatedFiles
+}
+
+const Output = () => {
+ const { files } = useUpload('async_upload_context_id')
+ return
+}
+export const WithAsyncFileHandler = () => {
+ return (
+ console.log(form)}>
+
+
+
+
+
+
+ )
+}
diff --git a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/FieldBlock.tsx b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/FieldBlock.tsx
index ac4064c73b5..a712a7a5259 100644
--- a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/FieldBlock.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/FieldBlock.tsx
@@ -181,6 +181,7 @@ function FieldBlock(props: Props) {
const blockId = useId(props.id)
const [wasUpdated, forceUpdate] = useReducer(() => ({}), {})
const mountedFieldsRef = useRef({})
+ const fieldStateRef = useRef(null)
const stateRecordRef = useRef({})
const fieldStateIdsRef = useRef(null)
const contentsRef = useRef(null)
@@ -254,11 +255,11 @@ function FieldBlock(props: Props) {
}
}, [])
- const setFieldState = useCallback(
+ const setBlockRecord = useCallback(
(props: StateBasis) => {
if (nestedFieldBlockContext) {
// If this FieldBlock is inside another one, forward the call to the outer one
- nestedFieldBlockContext.setFieldState(props)
+ nestedFieldBlockContext.setBlockRecord(props)
return
}
@@ -269,6 +270,17 @@ function FieldBlock(props: Props) {
[nestedFieldBlockContext, setInternalRecord]
)
+ const setFieldState = useCallback(
+ (identifier: Identifier, fieldState: SubmitState) => {
+ if (fieldState !== fieldStateRef.current) {
+ fieldStateRef.current = fieldState
+
+ forceUpdate()
+ }
+ },
+ []
+ )
+
const showFieldError = useCallback(
(identifier: Identifier, show: boolean) => {
if (nestedFieldBlockContext) {
@@ -465,6 +477,11 @@ function FieldBlock(props: Props) {
hasCustomContentWidth ? 'custom' : contentWidth
}`,
labelHeight && `dnb-forms-field-block--label-height-${labelHeight}`,
+ composition && 'dnb-forms-field-block__composition',
+ composition &&
+ `dnb-forms-field-block__composition--${
+ composition === true ? 'horizontal' : composition
+ }`,
className
)
const gridClasses = classnames(
@@ -534,6 +551,7 @@ function FieldBlock(props: Props) {
return (
)}
@@ -611,10 +630,6 @@ function FieldBlock(props: Props) {
hasCustomContentWidth ? 'custom' : contentWidth
}`,
align && `dnb-forms-field-block__contents--align-${align}`,
- composition &&
- `dnb-forms-field-block__contents__composition--${
- composition === true ? 'horizontal' : composition
- }`,
contentClassName
)}
ref={contentsRef}
@@ -623,7 +638,7 @@ function FieldBlock(props: Props) {
diff --git a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/FieldBlockContext.ts b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/FieldBlockContext.ts
index 8f9bcfa688a..b6f9b17416f 100644
--- a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/FieldBlockContext.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/FieldBlockContext.ts
@@ -1,5 +1,5 @@
import React from 'react'
-import type { FieldProps, Identifier } from '../types'
+import type { FieldProps, Identifier, SubmitState } from '../types'
export type FieldErrorIdsRef = Record
export type MountedFieldsRef = Record
@@ -35,7 +35,7 @@ export type StatusContent = {
}
export type FieldBlockContextProps = {
- setFieldState?: ({
+ setBlockRecord?: ({
identifier,
type,
stateId,
@@ -43,6 +43,7 @@ export type FieldBlockContextProps = {
showInitially,
show,
}: StateBasis) => void
+ setFieldState?: (identifier: Identifier, fieldState: SubmitState) => void
showFieldError?: (identifier: Identifier, showError: boolean) => void
hasErrorProp?: boolean
composition?: true
diff --git a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/FieldBlock.test.tsx b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/FieldBlock.test.tsx
index 86e7c6cd457..c2e9c4e36d1 100644
--- a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/FieldBlock.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/FieldBlock.test.tsx
@@ -1,4 +1,5 @@
import React from 'react'
+import { wait } from '../../../../core/jest/jestSetup'
import { fireEvent, render, waitFor } from '@testing-library/react'
import { Input } from '../../../../components'
import FieldBlock from '../FieldBlock'
@@ -957,6 +958,99 @@ describe('FieldBlock', () => {
).toBe('Nested')
})
+ describe('fieldState', () => {
+ it('should show indicator when fieldState is set to pending', async () => {
+ render(
+
+
+
+ )
+
+ const elements = document.querySelectorAll(
+ '.dnb-forms-submit-indicator'
+ )
+ expect(elements).toHaveLength(1)
+ expect(elements[0]).toHaveClass(
+ 'dnb-forms-submit-indicator--state-pending'
+ )
+ })
+
+ it('should show indicator two (2) times when nested', async () => {
+ render(
+
+ content
+
+ )
+
+ const elements = document.querySelectorAll(
+ '.dnb-forms-submit-indicator'
+ )
+ expect(elements).toHaveLength(2)
+ expect(elements[0]).toHaveClass(
+ 'dnb-forms-submit-indicator--state-pending'
+ )
+ expect(elements[1]).toHaveClass(
+ 'dnb-forms-submit-indicator--state-pending'
+ )
+ })
+
+ it('should show indicator two (2) times when nested with useFieldProps', async () => {
+ const onChange = jest.fn(async () => {
+ await wait(10)
+ return null
+ })
+ const MockComponent = () => {
+ const { id, handleChange } = useFieldProps({
+ onChange,
+ })
+
+ return (
+
+
+
+ )
+ }
+
+ render(
+
+
+
+ )
+
+ const elements = document.querySelectorAll(
+ '.dnb-forms-submit-indicator'
+ )
+ expect(elements).toHaveLength(2)
+
+ expect(elements[0].className).not.toContain(
+ 'dnb-forms-submit-indicator--state-'
+ )
+ expect(elements[1].className).not.toContain(
+ 'dnb-forms-submit-indicator--state-'
+ )
+
+ await userEvent.type(document.querySelector('input'), '1')
+
+ expect(elements[0]).toHaveClass(
+ 'dnb-forms-submit-indicator--state-pending'
+ )
+ expect(elements[1]).toHaveClass(
+ 'dnb-forms-submit-indicator--state-pending'
+ )
+
+ await waitFor(() => {
+ expect(elements[0]).toHaveClass(
+ 'dnb-forms-submit-indicator--state-complete'
+ )
+ })
+ await waitFor(() => {
+ expect(elements[1]).toHaveClass(
+ 'dnb-forms-submit-indicator--state-complete'
+ )
+ })
+ })
+ })
+
describe('help', () => {
it('should render content when open is true', async () => {
render(
diff --git a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-sbanken-have-to-match-help-button-in-composition-fields.snap.png b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-sbanken-have-to-match-help-button-in-composition-fields.snap.png
index 8cba97a3d09..f4e3064298d 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-sbanken-have-to-match-help-button-in-composition-fields.snap.png and b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-sbanken-have-to-match-help-button-in-composition-fields.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-sbanken-have-to-match-help-button-with-html.snap.png b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-sbanken-have-to-match-help-button-with-html.snap.png
index 11b1045f35a..20af8082a89 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-sbanken-have-to-match-help-button-with-html.snap.png and b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-sbanken-have-to-match-help-button-with-html.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-sbanken-have-to-match-label-description-help-button.snap.png b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-sbanken-have-to-match-label-description-help-button.snap.png
index 8c432197f82..3a576a94c8f 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-sbanken-have-to-match-label-description-help-button.snap.png and b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-sbanken-have-to-match-label-description-help-button.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-ui-have-to-match-help-button-in-composition-fields.snap.png b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-ui-have-to-match-help-button-in-composition-fields.snap.png
index bbf1087b1c4..fe4e6ae4831 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-ui-have-to-match-help-button-in-composition-fields.snap.png and b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-ui-have-to-match-help-button-in-composition-fields.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-ui-have-to-match-help-button-with-html.snap.png b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-ui-have-to-match-help-button-with-html.snap.png
index c72ff21cfde..1f5c94c830a 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-ui-have-to-match-help-button-with-html.snap.png and b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-ui-have-to-match-help-button-with-html.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-ui-have-to-match-label-description-help-button.snap.png b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-ui-have-to-match-label-description-help-button.snap.png
index bc989a0c5a0..dbb0967d4eb 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-ui-have-to-match-label-description-help-button.snap.png and b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/__image_snapshots__/fieldblock-for-ui-have-to-match-label-description-help-button.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/stories/FieldBlock.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/stories/FieldBlock.stories.tsx
index 51f2db3c659..aa10159744b 100644
--- a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/stories/FieldBlock.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/stories/FieldBlock.stories.tsx
@@ -3,7 +3,7 @@ import FieldBlock from '../FieldBlock'
import Input from '../../../../components/Input'
import { useFieldProps } from '../../hooks'
import { Field, Form } from '../..'
-import { Anchor, Card, Flex } from '../../../../components'
+import { Anchor, Flex } from '../../../../components'
export default {
title: 'Eufemia/Extensions/Forms/FieldBlock',
@@ -190,7 +190,7 @@ export const WithInlineHelp = () => {
Kredittopplysninger
-
+
{
content: 'Dette er hvor mye du har tenkt å låne totalt.',
}}
/>
-
+
{
Subheading
-
+
{
await new Promise((resolve) => setTimeout(resolve, 1000))
}}
/>
-
+
)
}
diff --git a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/style/dnb-field-block.scss b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/style/dnb-field-block.scss
index 178870b002b..d66077b4019 100644
--- a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/style/dnb-field-block.scss
+++ b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/style/dnb-field-block.scss
@@ -169,6 +169,7 @@ fieldset.dnb-forms-field-block {
&__status {
grid-area: status;
+
.dnb-form-status__shell {
margin-top: 0.5rem; // set margin-top on shell, so we can animate the height
max-width: 60ch; // to enhance readability
@@ -204,31 +205,42 @@ fieldset.dnb-forms-field-block {
}
}
}
+ }
- &__composition {
- &--vertical {
- display: flex;
- flex-flow: column;
- row-gap: var(--spacing-small);
- }
+ &__composition--vertical &__contents {
+ display: flex;
+ flex-flow: column;
+ row-gap: var(--spacing-x-small);
+ }
- &--horizontal {
- display: flex;
- flex-flow: row;
- column-gap: var(--spacing-small);
+ &__composition--horizontal &__contents {
+ display: flex;
+ flex-flow: row;
+ column-gap: var(--spacing-small);
- @include allAbove(x-small) {
- align-items: flex-end; // To support fields with labels of different size
+ @include allAbove(x-small) {
+ align-items: flex-end; // To support fields with labels of different size
- &[class*='align-center'] {
- align-items: center; // To support fields without labels, but different heights
- }
- }
- @include allBelow(x-small) {
- row-gap: var(--spacing-x-small);
- flex-flow: column;
- }
+ &[class*='align-center'] {
+ align-items: center; // To support fields without labels, but different heights
}
}
+ @include allBelow(x-small) {
+ row-gap: var(--spacing-x-small);
+ flex-flow: column;
+ }
+ }
+
+ // Because we want to hide / show the indicator responsively,
+ // we render both, but hide the one we don't want to show.
+ @include allBelow(x-small) {
+ &__composition > &__grid > .dnb-forms-submit-indicator {
+ display: none;
+ }
+ }
+ @include allAbove(x-small) {
+ &__composition > &__grid > &__contents .dnb-forms-submit-indicator {
+ display: none;
+ }
}
}
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/ButtonRow/style/dnb-form-button-row.scss b/packages/dnb-eufemia/src/extensions/forms/Form/ButtonRow/style/dnb-form-button-row.scss
index d7d3f8848da..7e4597b37d3 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Form/ButtonRow/style/dnb-form-button-row.scss
+++ b/packages/dnb-eufemia/src/extensions/forms/Form/ButtonRow/style/dnb-form-button-row.scss
@@ -7,19 +7,27 @@
gap: var(--spacing-small);
}
-.dnb-card + .dnb-forms-button-row,
-.dnb-card + .dnb-button--primary {
- &:not([class*='space__top']) {
- margin-top: var(--spacing-small);
+.dnb-card {
+ & + .dnb-forms-button-row,
+ & + .dnb-button--primary {
+ &:not([class*='space__top']) {
+ margin-top: var(--spacing-small);
- .dnb-button[class*='space__top'] {
- margin-top: 0;
+ .dnb-button[class*='space__top'] {
+ margin-top: 0;
+ }
}
}
+}
- @include allAbove(small) {
- &:not([class*='space__left']) {
- margin-left: var(--spacing-medium);
+// Deprecated – can be removed in v11 (its handled by the outset prop)
+.dnb-card:not([style*='--outset']) {
+ & + .dnb-forms-button-row,
+ & + .dnb-button--primary {
+ @include allAbove(small) {
+ &:not([class*='space__left']) {
+ margin-left: var(--spacing-medium);
+ }
}
}
}
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Card/Card.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/Card/Card.tsx
new file mode 100644
index 00000000000..277c768ba67
--- /dev/null
+++ b/packages/dnb-eufemia/src/extensions/forms/Form/Card/Card.tsx
@@ -0,0 +1,10 @@
+import React from 'react'
+import CardInstance, {
+ Props as CardProps,
+} from '../../../../components/card/Card'
+
+function Card(props: CardProps) {
+ return
+}
+
+export default Card
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Card/CardDocs.ts b/packages/dnb-eufemia/src/extensions/forms/Form/Card/CardDocs.ts
new file mode 100644
index 00000000000..f6c7fcefd65
--- /dev/null
+++ b/packages/dnb-eufemia/src/extensions/forms/Form/Card/CardDocs.ts
@@ -0,0 +1,18 @@
+import { CardProperties } from '../../../../components/card/CardDocs'
+import { PropertiesTableProps } from '../../../../shared/types'
+
+export const FormCardProperties: PropertiesTableProps = {
+ outset: {
+ ...CardProperties.outset,
+ doc: 'Same as `outset` in [Card](/uilib/components/card/properties). Defaults to `true`.',
+ },
+ stack: {
+ ...CardProperties.stack,
+ doc: 'Same as `stack` in [Card](/uilib/components/card/properties). Defaults to `true`.',
+ },
+ '[Card](/uilib/components/card/properties)': {
+ doc: 'Card properties.',
+ type: 'Various',
+ status: 'optional',
+ },
+}
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Card/__tests__/Card.screenshot.test.ts b/packages/dnb-eufemia/src/extensions/forms/Form/Card/__tests__/Card.screenshot.test.ts
new file mode 100644
index 00000000000..56716295014
--- /dev/null
+++ b/packages/dnb-eufemia/src/extensions/forms/Form/Card/__tests__/Card.screenshot.test.ts
@@ -0,0 +1,42 @@
+/**
+ * Screenshot Test
+ * This file will not run on "test:staged" because we don't require any related files
+ */
+
+import { makeScreenshot } from '../../../../../core/jest/jestSetupScreenshots'
+
+const url = '/uilib/extensions/forms/Form/Card/demos'
+
+describe('Form.Card', () => {
+ it('have to match outset', async () => {
+ const screenshot = await makeScreenshot({
+ url,
+ selector: '[data-visual-test="forms-card"]',
+ wrapperStyle: {
+ padding: '2rem',
+ },
+ })
+ expect(screenshot).toMatchImageSnapshot()
+ })
+})
+
+describe.each(['ui', 'sbanken'])(
+ 'Card small screen for %s',
+ (themeName) => {
+ const params = {
+ themeName,
+ pageViewport: {
+ width: 400,
+ },
+ url,
+ }
+
+ it('have to match outset', async () => {
+ const screenshot = await makeScreenshot({
+ ...params,
+ selector: '[data-visual-test="forms-card"]',
+ })
+ expect(screenshot).toMatchImageSnapshot()
+ })
+ }
+)
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Card/__tests__/Card.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/Card/__tests__/Card.test.tsx
new file mode 100644
index 00000000000..972f56cb640
--- /dev/null
+++ b/packages/dnb-eufemia/src/extensions/forms/Form/Card/__tests__/Card.test.tsx
@@ -0,0 +1,69 @@
+import React from 'react'
+import { render } from '@testing-library/react'
+import { Form } from '../../..'
+
+describe('Form.Card', () => {
+ it('should set "outset" by default', () => {
+ const { rerender } = render( )
+
+ const element = document.querySelector('.dnb-card')
+
+ expect(element).toHaveStyle('--outset--small: 0')
+ expect(element).toHaveStyle('--outset--medium: 1')
+ expect(element).toHaveStyle('--outset--large: 1')
+
+ rerender(
+
+ )
+
+ expect(element).toHaveStyle('--outset--small: 1')
+ expect(element).toHaveStyle('--outset--medium: 0')
+ expect(element).toHaveStyle('--outset--large: 0')
+
+ rerender( )
+
+ expect(element).toHaveStyle('--outset--small: 0')
+ expect(element).toHaveStyle('--outset--medium: 0')
+ expect(element).toHaveStyle('--outset--large: 0')
+ })
+
+ it('should set "stack" by default', () => {
+ const { rerender } = render( )
+
+ {
+ const element = document.querySelector('.dnb-card')
+ const container = element.querySelector('.dnb-flex-container')
+
+ expect(element).toHaveClass('dnb-flex-item--align-self-stretch')
+ expect(container).toHaveClass('dnb-flex-container--align-stretch')
+ expect(container).toHaveClass(
+ 'dnb-flex-container--align-self-stretch'
+ )
+ expect(container).toHaveClass('dnb-flex-container--spacing-medium')
+ }
+
+ rerender( )
+
+ {
+ const element = document.querySelector('.dnb-card')
+ const container = element.querySelector('.dnb-flex-container')
+
+ expect(element).toHaveClass('dnb-flex-item--align-self-stretch')
+ expect(container).not.toHaveClass(
+ 'dnb-flex-container--align-stretch'
+ )
+ expect(container).toHaveClass(
+ 'dnb-flex-container--align-self-stretch'
+ )
+ expect(container).not.toHaveClass(
+ 'dnb-flex-container--spacing-medium'
+ )
+ }
+ })
+})
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Card/__tests__/__image_snapshots__/card-small-screen-for-sbanken-have-to-match-outset.snap.png b/packages/dnb-eufemia/src/extensions/forms/Form/Card/__tests__/__image_snapshots__/card-small-screen-for-sbanken-have-to-match-outset.snap.png
new file mode 100644
index 00000000000..2ee6d1a3605
Binary files /dev/null and b/packages/dnb-eufemia/src/extensions/forms/Form/Card/__tests__/__image_snapshots__/card-small-screen-for-sbanken-have-to-match-outset.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Card/__tests__/__image_snapshots__/card-small-screen-for-ui-have-to-match-outset.snap.png b/packages/dnb-eufemia/src/extensions/forms/Form/Card/__tests__/__image_snapshots__/card-small-screen-for-ui-have-to-match-outset.snap.png
new file mode 100644
index 00000000000..345981d2aa8
Binary files /dev/null and b/packages/dnb-eufemia/src/extensions/forms/Form/Card/__tests__/__image_snapshots__/card-small-screen-for-ui-have-to-match-outset.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Card/__tests__/__image_snapshots__/formcard-have-to-match-outset.snap.png b/packages/dnb-eufemia/src/extensions/forms/Form/Card/__tests__/__image_snapshots__/formcard-have-to-match-outset.snap.png
new file mode 100644
index 00000000000..8afd9b100c8
Binary files /dev/null and b/packages/dnb-eufemia/src/extensions/forms/Form/Card/__tests__/__image_snapshots__/formcard-have-to-match-outset.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Card/index.ts b/packages/dnb-eufemia/src/extensions/forms/Form/Card/index.ts
new file mode 100644
index 00000000000..9bb50edbc10
--- /dev/null
+++ b/packages/dnb-eufemia/src/extensions/forms/Form/Card/index.ts
@@ -0,0 +1,2 @@
+export { default } from './Card'
+export * from './Card'
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Handler/__tests__/Handler.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/Handler/__tests__/Handler.test.tsx
index 902f8482cc1..e0cce0782fa 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Form/Handler/__tests__/Handler.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Form/Handler/__tests__/Handler.test.tsx
@@ -544,7 +544,7 @@ describe('Form.Handler', () => {
expect(inputElement).toBeDisabled()
})
- it('should not disable form elements during on async validator handling', async () => {
+ it('should not disable form elements during an async validator handling', async () => {
const onSubmit = async () => null
const asyncValidator = async () => {
return null
@@ -568,7 +568,7 @@ describe('Form.Handler', () => {
rerender(
-
+
)
@@ -772,7 +772,7 @@ describe('Form.Handler', () => {
})
})
- it('should call onSubmit and onSubmitComplete with async validator', async () => {
+ it('should call onSubmit and onSubmitComplete with async onChangeValidator', async () => {
const onSubmit: OnSubmit = jest.fn()
const onSubmitComplete = jest.fn()
@@ -788,7 +788,7 @@ describe('Form.Handler', () => {
@@ -819,7 +819,7 @@ describe('Form.Handler', () => {
})
})
- it('should not call async validator when field is not mounted anymore', async () => {
+ it('should not call async onChangeValidator when field is not mounted anymore', async () => {
const onSubmit: OnSubmit = jest.fn()
const asyncValidator = jest.fn(async () => {
return null
@@ -830,7 +830,7 @@ describe('Form.Handler', () => {
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Handler/stories/FormHandler.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/Handler/stories/FormHandler.stories.tsx
index 03f7c65b3de..0ee999297de 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Form/Handler/stories/FormHandler.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Form/Handler/stories/FormHandler.stories.tsx
@@ -1,6 +1,6 @@
import React, { useCallback, useEffect } from 'react'
import { Field, Form } from '../../..'
-import { Button, Card, GlobalStatus } from '../../../../../components'
+import { Button, GlobalStatus } from '../../../../../components'
import { debounceAsync } from '../../../../../shared/helpers'
export default {
@@ -128,14 +128,14 @@ export function AdvancedForm() {
<>
MainHeading
-
+
SubHeading
@@ -166,7 +166,7 @@ export function AdvancedForm() {
path="/fieldD"
required
/>
-
+
@@ -179,9 +179,9 @@ export function AdvancedForm() {
-
+
-
+
>
)
}
@@ -268,12 +268,12 @@ export function SimpleForm() {
onSubmitRequest={onSubmitRequest}
onSubmitComplete={onSubmitComplete}
>
-
+
@@ -282,7 +282,7 @@ export function SimpleForm() {
path="/myField2"
onChange={onFieldChange}
/>
-
+
@@ -302,12 +302,12 @@ const delay = debounceAsync(async function () {
export function SubmitIndicator() {
return (
-
+
-
+
)
}
@@ -319,7 +319,7 @@ export function GlobalStatusStory() {
Heading
-
+
-
+
>
)
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Isolation/stories/Isolation.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/Isolation/stories/Isolation.stories.tsx
index f303a7b1960..f3b998dda5f 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Form/Isolation/stories/Isolation.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Form/Isolation/stories/Isolation.stories.tsx
@@ -1,6 +1,6 @@
import React from 'react'
import { Field, Form } from '../../..'
-import { Card, Flex, HeightAnimation } from '../../../../../components'
+import { Flex, HeightAnimation } from '../../../../../components'
export default {
title: 'Eufemia/Extensions/Forms/Isolation',
@@ -115,7 +115,7 @@ export const TransformOnCommit = () => {
mySelection: 'other',
}}
>
-
+
Legg til ny hovedkontaktperson
@@ -165,7 +165,7 @@ export const TransformOnCommit = () => {
-
+
)
}
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/MainHeading/__tests__/__image_snapshots__/formmainheading-have-to-match-above-card.snap.png b/packages/dnb-eufemia/src/extensions/forms/Form/MainHeading/__tests__/__image_snapshots__/formmainheading-have-to-match-above-card.snap.png
index b6eadeb84e8..67caab86daf 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/Form/MainHeading/__tests__/__image_snapshots__/formmainheading-have-to-match-above-card.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Form/MainHeading/__tests__/__image_snapshots__/formmainheading-have-to-match-above-card.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/MainHeading/__tests__/__image_snapshots__/formmainheading-have-to-match-help-button.snap.png b/packages/dnb-eufemia/src/extensions/forms/Form/MainHeading/__tests__/__image_snapshots__/formmainheading-have-to-match-help-button.snap.png
index 12122c32b65..299c66978b1 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/Form/MainHeading/__tests__/__image_snapshots__/formmainheading-have-to-match-help-button.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Form/MainHeading/__tests__/__image_snapshots__/formmainheading-have-to-match-help-button.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Section/stories/Section.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/Section/stories/Section.stories.tsx
index 68242f04657..844388e63d6 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Form/Section/stories/Section.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Form/Section/stories/Section.stories.tsx
@@ -1,6 +1,6 @@
import React, { useCallback } from 'react'
import { Field, Form, JSONSchema, SectionProps, Value } from '../../..'
-import { Card, Flex } from '../../../../../components'
+import { Flex } from '../../../../../components'
import { Props as FieldNameProps } from '../../../Field/Name'
export default {
@@ -169,10 +169,10 @@ export const NestedSections = () => {
const MySection = (props: SectionProps) => {
return (
-
+
-
+
)
}
@@ -228,7 +228,7 @@ export function EditViewContainer() {
},
}}
>
-
+
Your account
-
+
)
}
@@ -276,13 +276,13 @@ export function OpenWhenFieldValidationError() {
},
}}
>
-
+
Your account
-
+
)
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/SubHeading/__tests__/__image_snapshots__/formsubheading-have-to-match-above-card.snap.png b/packages/dnb-eufemia/src/extensions/forms/Form/SubHeading/__tests__/__image_snapshots__/formsubheading-have-to-match-above-card.snap.png
index 47d7edee661..1b7dc384d78 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/Form/SubHeading/__tests__/__image_snapshots__/formsubheading-have-to-match-above-card.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Form/SubHeading/__tests__/__image_snapshots__/formsubheading-have-to-match-above-card.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/SubHeading/__tests__/__image_snapshots__/formsubheading-have-to-match-help-button.snap.png b/packages/dnb-eufemia/src/extensions/forms/Form/SubHeading/__tests__/__image_snapshots__/formsubheading-have-to-match-help-button.snap.png
index 336e6ad8c08..45a59fe0216 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/Form/SubHeading/__tests__/__image_snapshots__/formsubheading-have-to-match-help-button.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Form/SubHeading/__tests__/__image_snapshots__/formsubheading-have-to-match-help-button.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/stories/Visibility.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/stories/Visibility.stories.tsx
index 04f230583d8..2d7c159639c 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/stories/Visibility.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/stories/Visibility.stories.tsx
@@ -1,6 +1,6 @@
import React, { useCallback } from 'react'
import { Field, Form, Value } from '../../..'
-import { Flex, Section, Card } from '../../../../../components'
+import { Flex, Section } from '../../../../../components'
import { P, Ul, Li } from '../../../../../elements'
export default {
@@ -185,7 +185,7 @@ export const wrappingVisibilityInFragmentAllVisible = () => {
visible: true,
}}
>
-
+
Test heading
<>
{
text
>
-
+
)
}
@@ -224,7 +224,7 @@ export const wrappingVisibilityInFragmentAllHidden = () => {
visible: false,
}}
>
-
+
Test heading
<>
{
text
>
-
+
)
}
@@ -267,7 +267,7 @@ export const wrappingVisibilityInFragments2Hidden = () => {
visible5: true,
}}
>
-
+
Test heading
<>
{
text
>
-
+
)
}
@@ -315,7 +315,7 @@ export const wrappingVisibilityInFragments3Hidden = () => {
visible5: false,
}}
>
-
+
Test heading
<>
{
text
>
-
+
)
}
@@ -363,7 +363,7 @@ export const wrappingVisibilityInFragment = () => {
visible5: false,
}}
>
-
+
<>
Test heading
<>
@@ -448,7 +448,7 @@ export const wrappingVisibilityInFragment = () => {
/>
Text that should appear underneath
>
-
+
)
}
@@ -461,7 +461,7 @@ export const wrappingSingleVisibilityInRootFragment = () => {
visible1: false,
}}
>
-
+
<>
{
text
>
-
+
)
}
@@ -477,7 +477,7 @@ export const wrappingSingleVisibilityInRootFragment = () => {
export function VisibilityOnValidation() {
return (
-
+
-
+
)
}
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/index.ts b/packages/dnb-eufemia/src/extensions/forms/Form/index.ts
index 1b59f78a1b3..3b2c6c245b6 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Form/index.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/Form/index.ts
@@ -11,6 +11,7 @@ export { default as MainHeading } from './MainHeading'
export { default as SubHeading } from './SubHeading'
export { default as Visibility } from './Visibility'
export { default as Section } from './Section'
+export { default as Card } from './Card'
export { default as Isolation } from './Isolation'
export { default as Snapshot } from './Snapshot'
export { default as useData } from './data-context/useData'
diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/Array/ArrayDocs.ts b/packages/dnb-eufemia/src/extensions/forms/Iterate/Array/ArrayDocs.ts
index e5912f3d3f7..90a8abb1d12 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Iterate/Array/ArrayDocs.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/Array/ArrayDocs.ts
@@ -52,7 +52,7 @@ export const ArrayProperties: PropertiesTableProps = {
type: 'unknown',
status: 'optional',
},
- validator: DataValueWritePropsProperties.validator,
+ onChangeValidator: DataValueWritePropsProperties.onChangeValidator,
validateInitially: DataValueWritePropsProperties.validateInitially,
continuousValidation: DataValueWritePropsProperties.continuousValidation,
containerMode: {
diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/Array/__tests__/Array.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/Array/__tests__/Array.test.tsx
index 58b73db770c..c9b34f5e142 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Iterate/Array/__tests__/Array.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/Array/__tests__/Array.test.tsx
@@ -504,9 +504,9 @@ describe('Iterate.Array', () => {
})
})
- describe('validator', () => {
- it('should validate validator initially (validateInitially)', async () => {
- const validator = jest.fn((arrayValue) => {
+ describe('onChangeValidator', () => {
+ it('should validate onChangeValidator initially (validateInitially)', async () => {
+ const onChangeValidator = jest.fn((arrayValue) => {
if (arrayValue.length === 2) {
return new Error('Error message')
}
@@ -520,7 +520,7 @@ describe('Iterate.Array', () => {
>
@@ -529,8 +529,8 @@ describe('Iterate.Array', () => {
)
- expect(validator).toHaveBeenCalledTimes(1)
- expect(validator).toHaveBeenCalledWith(
+ expect(onChangeValidator).toHaveBeenCalledTimes(1)
+ expect(onChangeValidator).toHaveBeenCalledWith(
['foo', 'bar'],
expect.anything()
)
@@ -546,8 +546,8 @@ describe('Iterate.Array', () => {
fireEvent.click(document.querySelector('button'))
- expect(validator).toHaveBeenCalledTimes(2)
- expect(validator).toHaveBeenCalledWith(
+ expect(onChangeValidator).toHaveBeenCalledTimes(2)
+ expect(onChangeValidator).toHaveBeenCalledWith(
['foo', 'bar', 'baz'],
expect.anything()
)
@@ -559,8 +559,8 @@ describe('Iterate.Array', () => {
})
})
- it('should validate validator on form submit', async () => {
- const validator = jest.fn((arrayValue) => {
+ it('should validate onChangeValidator on form submit', async () => {
+ const onChangeValidator = jest.fn((arrayValue) => {
if (arrayValue.length === 2) {
return new Error('Error message')
}
@@ -572,20 +572,23 @@ describe('Iterate.Array', () => {
items: ['foo', 'bar'],
}}
>
-
+
)
- expect(validator).toHaveBeenCalledTimes(0)
+ expect(onChangeValidator).toHaveBeenCalledTimes(0)
const form = document.querySelector('form')
fireEvent.submit(form)
- expect(validator).toHaveBeenCalledTimes(1)
- expect(validator).toHaveBeenCalledWith(
+ expect(onChangeValidator).toHaveBeenCalledTimes(1)
+ expect(onChangeValidator).toHaveBeenCalledWith(
['foo', 'bar'],
expect.anything()
)
@@ -601,8 +604,8 @@ describe('Iterate.Array', () => {
fireEvent.click(document.querySelector('button'))
- expect(validator).toHaveBeenCalledTimes(2)
- expect(validator).toHaveBeenCalledWith(
+ expect(onChangeValidator).toHaveBeenCalledTimes(2)
+ expect(onChangeValidator).toHaveBeenCalledWith(
['foo', 'bar', 'baz'],
expect.anything()
)
@@ -618,7 +621,7 @@ describe('Iterate.Array', () => {
const findFirstDuplication = (arr) =>
arr.findIndex((e, i) => arr.indexOf(e) !== i)
- const validator = jest.fn((arrayValue) => {
+ const onChangeValidator = jest.fn((arrayValue) => {
const index = findFirstDuplication(arrayValue)
if (index > -1) {
const value = arrayValue[index]
@@ -628,7 +631,10 @@ describe('Iterate.Array', () => {
render(
-
+
diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/Array/types.ts b/packages/dnb-eufemia/src/extensions/forms/Iterate/Array/types.ts
index ad8aef4cced..6e8eeaa9e68 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Iterate/Array/types.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/Array/types.ts
@@ -24,7 +24,7 @@ export type Props = Omit<
limit?: number
countPath?: Path
countPathLimit?: number
- validator?: Validator
+ onChangeValidator?: Validator
withoutFlex?: boolean
animate?: boolean
placeholder?: React.ReactNode
diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/__tests__/EditAndViewContainer.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/__tests__/EditAndViewContainer.test.tsx
index db4e2e32dc1..78d1a364a09 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/__tests__/EditAndViewContainer.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/__tests__/EditAndViewContainer.test.tsx
@@ -86,7 +86,7 @@ describe('EditContainer and ViewContainer', () => {
{
+ onChangeValidator={(value) => {
if (value === '01') {
return new Error('error')
}
diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/Iterate.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/Iterate.stories.tsx
index 3e17807f36e..191e70358fc 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/Iterate.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/Iterate.stories.tsx
@@ -1,6 +1,6 @@
import React, { useCallback } from 'react'
import { Field, Form, Iterate, Tools, Value, Wizard } from '../..'
-import { Card, Flex } from '../../../../components'
+import { Flex } from '../../../../components'
export default {
title: 'Eufemia/Extensions/Forms/Iterate',
@@ -17,7 +17,7 @@ export const AnimatedContainer = () => {
id="myForm"
>
-
+
Empty list>}>
@@ -34,7 +34,7 @@ export const AnimatedContainer = () => {
text="Add new item"
top
/>
-
+
{
Accounts
-
+
-
+
@@ -178,13 +178,13 @@ export const InitialOpen = () => {
Statsborgerskap
-
+
{
+ onChangeValidator={(arrayValue) => {
const findFirstDuplication = (arr) =>
arr.findIndex((e, i) => arr.indexOf(e) !== i)
@@ -207,7 +207,7 @@ export const InitialOpen = () => {
pushValue={null}
text="Legg til flere statsborgerskap"
/>
-
+
@@ -228,7 +228,7 @@ export const WithArrayValidator = () => {
{
+ onChangeValidator={(arrayValue) => {
if (!(arrayValue?.length > 0)) {
return new Error('You need at least one item')
}
@@ -272,7 +272,7 @@ export function InWizard() {
-
+
@@ -292,7 +292,7 @@ export function InWizard() {
variant="tertiary"
pushValue={{}}
/>
-
+
diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/PushContainer.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/PushContainer.stories.tsx
index 3b81ebd8f10..8873adc93da 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/PushContainer.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/PushContainer.stories.tsx
@@ -1,6 +1,6 @@
import React, { useLayoutEffect } from 'react'
import { Field, Form, Iterate, Tools, Value } from '../..'
-import { Card, Flex } from '../../../../components'
+import { Flex } from '../../../../components'
export default {
title: 'Eufemia/Extensions/Forms/Iterate/PushContainer',
@@ -27,13 +27,13 @@ export const ComplexPushContainer = () => {
return (
Representatives
-
+
-
+
)
diff --git a/packages/dnb-eufemia/src/extensions/forms/Value/Provider/stories/ValueProvider.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Value/Provider/stories/ValueProvider.stories.tsx
index b4ad11af359..6a57bb8428f 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Value/Provider/stories/ValueProvider.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Value/Provider/stories/ValueProvider.stories.tsx
@@ -1,5 +1,5 @@
import { Field, Form, Value } from '../../..'
-import { Card, HeightAnimation } from '../../../../../components'
+import { HeightAnimation } from '../../../../../components'
export default {
title: 'Eufemia/Extensions/Forms/ValueProvider',
@@ -8,7 +8,7 @@ export default {
export function ValueProvider() {
return (
-
+
-
+
)
}
diff --git a/packages/dnb-eufemia/src/extensions/forms/Value/SummaryList/SummaryList.tsx b/packages/dnb-eufemia/src/extensions/forms/Value/SummaryList/SummaryList.tsx
index 61564d940f6..9b0b91c1440 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Value/SummaryList/SummaryList.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Value/SummaryList/SummaryList.tsx
@@ -7,7 +7,10 @@ import ValueProvider from '../Provider/ValueProvider'
import { ValueProps } from '../../types'
import { useVerifyChildren } from './useVerifyChildren'
-export type Props = Omit & {
+export type Props = Omit<
+ DlAllProps,
+ 'label' | 'labelSrOnly' | 'children'
+> & {
children: React.ReactNode
transformLabel?: ValueProps['transformLabel']
inheritVisibility?: ValueProps['inheritVisibility']
diff --git a/packages/dnb-eufemia/src/extensions/forms/Value/SummaryList/__tests__/SummaryList.screenshot.test.ts b/packages/dnb-eufemia/src/extensions/forms/Value/SummaryList/__tests__/SummaryList.screenshot.test.ts
index 6a2d12fec10..035f46ed271 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Value/SummaryList/__tests__/SummaryList.screenshot.test.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/Value/SummaryList/__tests__/SummaryList.screenshot.test.ts
@@ -35,4 +35,12 @@ describe('Field.SummaryList', () => {
})
expect(screenshot).toMatchImageSnapshot()
})
+
+ it('have to match without a label', async () => {
+ const screenshot = await makeScreenshot({
+ style: { width: '6rem' },
+ selector: '[data-visual-test="forms-value-summary-empty-label"]',
+ })
+ expect(screenshot).toMatchImageSnapshot()
+ })
})
diff --git a/packages/dnb-eufemia/src/extensions/forms/Value/SummaryList/__tests__/SummaryList.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Value/SummaryList/__tests__/SummaryList.test.tsx
index 02e96825441..9353266d61a 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Value/SummaryList/__tests__/SummaryList.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Value/SummaryList/__tests__/SummaryList.test.tsx
@@ -17,6 +17,27 @@ describe('Field.SummaryList', () => {
expect(element.getAttribute('aria-label')).toBe('Aria Label')
})
+ it('should have dnb-sr-only class when no label is given', () => {
+ render(
+
+
+
+ )
+ expect(document.querySelector('dt')).toHaveClass('dnb-sr-only')
+ })
+
+ it('should set dnb-sr-only class when labelSrOnly is true', () => {
+ render(
+
+
+
+ )
+
+ const element = document.querySelector('dt')
+ expect(element).toHaveClass('dnb-sr-only')
+ expect(element).toHaveTextContent('Label')
+ })
+
it('should warn when child is not a Value.* component', () => {
const log = jest.spyOn(console, 'log').mockImplementation()
diff --git a/packages/dnb-eufemia/src/extensions/forms/Value/SummaryList/__tests__/__image_snapshots__/fieldsummarylist-have-to-match-without-a-label.snap.png b/packages/dnb-eufemia/src/extensions/forms/Value/SummaryList/__tests__/__image_snapshots__/fieldsummarylist-have-to-match-without-a-label.snap.png
new file mode 100644
index 00000000000..06fcd7b3253
Binary files /dev/null and b/packages/dnb-eufemia/src/extensions/forms/Value/SummaryList/__tests__/__image_snapshots__/fieldsummarylist-have-to-match-without-a-label.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/Value/SummaryList/stories/SummaryList.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Value/SummaryList/stories/SummaryList.stories.tsx
index 69f51e99265..fa7ab94ef6c 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Value/SummaryList/stories/SummaryList.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Value/SummaryList/stories/SummaryList.stories.tsx
@@ -1,5 +1,5 @@
import { Field, Form, Value } from '../../..'
-import { Card, Flex } from '../../../../../components'
+import { Flex } from '../../../../../components'
import { P } from '../../../../../elements'
export default {
@@ -33,7 +33,7 @@ export function SummaryList() {
function ContactInformationView() {
return (
<>
-
+
Subheading
@@ -45,7 +45,7 @@ function ContactInformationView() {
-
+
diff --git a/packages/dnb-eufemia/src/extensions/forms/Value/Upload/stories/Upload.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Value/Upload/stories/Upload.stories.tsx
index 21b18eb069c..51ba2b143b9 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Value/Upload/stories/Upload.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Value/Upload/stories/Upload.stories.tsx
@@ -1,5 +1,4 @@
import { Form, Value } from '../../..'
-import { Card } from '../../../../../components'
import { P } from '../../../../../elements'
export default {
@@ -42,7 +41,7 @@ export function Upload() {
what: ['Foo', 'Bar', 'Baz'],
}}
>
-
+
layout="grid"
-
-
+
+
layout="horizontal"
-
-
+
+
layout="vertical"
-
-
+
+
empty values
label.toUpperCase()}
@@ -88,7 +87,7 @@ export function Upload() {
-
+
)
}
diff --git a/packages/dnb-eufemia/src/extensions/forms/Value/ValueDocs.ts b/packages/dnb-eufemia/src/extensions/forms/Value/ValueDocs.ts
index 2b5492fa61a..1e594e1e7b8 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Value/ValueDocs.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/Value/ValueDocs.ts
@@ -16,6 +16,11 @@ export const ValueProperties: PropertiesTableProps = {
type: 'string',
status: 'optional',
},
+ labelSrOnly: {
+ doc: 'Use `true` to make the label only readable by screen readers.',
+ type: 'boolean',
+ status: 'optional',
+ },
transformLabel: {
doc: 'Transforms the label before it gets displayed. Receives the label as the first parameter. The second parameter is a object containing the `convertJsxToString` function.',
type: 'function',
diff --git a/packages/dnb-eufemia/src/extensions/forms/ValueBlock/ValueBlock.tsx b/packages/dnb-eufemia/src/extensions/forms/ValueBlock/ValueBlock.tsx
index b057b0f58aa..37a82ae78c0 100644
--- a/packages/dnb-eufemia/src/extensions/forms/ValueBlock/ValueBlock.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/ValueBlock/ValueBlock.tsx
@@ -43,6 +43,7 @@ function ValueBlock(props: Props) {
const {
className,
label: labelProp,
+ labelSrOnly,
transformLabel = (label: Props['label']) => label,
inline,
maxWidth = props.composition ? props.maxWidth : 'large',
@@ -116,7 +117,12 @@ function ValueBlock(props: Props) {
value={{ ...summaryListContext, isNested: true }}
>
-
-
+
{label && {label} }
{label}
diff --git a/packages/dnb-eufemia/src/extensions/forms/ValueBlock/__tests__/ValueBlock.test.tsx b/packages/dnb-eufemia/src/extensions/forms/ValueBlock/__tests__/ValueBlock.test.tsx
index ccb04c3bb22..ebc27f09f84 100644
--- a/packages/dnb-eufemia/src/extensions/forms/ValueBlock/__tests__/ValueBlock.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/ValueBlock/__tests__/ValueBlock.test.tsx
@@ -69,6 +69,18 @@ describe('ValueBlock', () => {
expect(element).toHaveClass('dnb-forms-value-block--max-width-medium')
})
+ it('should set dnb-sr-only class when labelSrOnly is true', () => {
+ render(
+
+ content
+
+ )
+
+ const element = document.querySelector('.dnb-form-label')
+ expect(element).toHaveClass('dnb-sr-only')
+ expect(element).toHaveTextContent('Label')
+ })
+
it('should put children in a wrapper element "__content"', () => {
render(content )
diff --git a/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/WizardContainer.screenshot.test.ts b/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/WizardContainer.screenshot.test.ts
index 830db5d5b8e..f1c5dfede77 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/WizardContainer.screenshot.test.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/WizardContainer.screenshot.test.ts
@@ -8,6 +8,10 @@ describe('Wizard.Container', () => {
pageViewport: {
width: 700,
},
+ wrapperStyle: {
+ 'padding-left': '2.5rem',
+ 'padding-right': '2.5rem',
+ },
selector:
'[data-visual-test="wizard-layout-card-border"] .dnb-forms-wizard-layout__contents',
})
diff --git a/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/WizardContainer.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/WizardContainer.test.tsx
index 3f23b7426c0..74f6732aeac 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/WizardContainer.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/WizardContainer.test.tsx
@@ -281,9 +281,9 @@ describe('Wizard.Container', () => {
})
it('should support navigating back and forth with async validators', async () => {
- const validator = async (value: string) => {
+ const onChangeValidator = async (value: string) => {
if (value !== 'valid') {
- return new Error('validator-error')
+ return new Error('onChangeValidator-error')
}
}
@@ -300,7 +300,7 @@ describe('Wizard.Container', () => {
@@ -337,7 +337,7 @@ describe('Wizard.Container', () => {
expect(output()).toHaveTextContent('Step 1')
expect(screen.queryByRole('alert')).toHaveTextContent(
- 'validator-error'
+ 'onChangeValidator-error'
)
fireEvent.blur(input())
@@ -372,7 +372,7 @@ describe('Wizard.Container', () => {
expect(output()).toHaveTextContent('Step 1')
expect(screen.queryByRole('alert')).toHaveTextContent(
- 'validator-error'
+ 'onChangeValidator-error'
)
fireEvent.blur(input())
@@ -1336,14 +1336,17 @@ describe('Wizard.Container', () => {
})
})
- it('should handle async validator', async () => {
+ it('should handle async onChangeValidator', async () => {
const asyncValidator = async () => null
render(
Step 1
-
+
@@ -1389,7 +1392,7 @@ describe('Wizard.Container', () => {
})
})
- it('should handle async validator with error', async () => {
+ it('should handle async onChangeValidator with error', async () => {
const asyncValidator = async (value: string) => {
if (value !== 'valid') {
return new Error('Error message')
@@ -1400,7 +1403,7 @@ describe('Wizard.Container', () => {
Step 1
-
+
diff --git a/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/__image_snapshots__/wizardcontainer-have-to-match-border.snap.png b/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/__image_snapshots__/wizardcontainer-have-to-match-border.snap.png
index f3b0a41cb23..a700fb867b8 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/__image_snapshots__/wizardcontainer-have-to-match-border.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/__image_snapshots__/wizardcontainer-have-to-match-border.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/__image_snapshots__/wizardcontainer-have-to-match-container-with-status-message.snap.png b/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/__image_snapshots__/wizardcontainer-have-to-match-container-with-status-message.snap.png
index 8ff6c39b18e..2be93e98d84 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/__image_snapshots__/wizardcontainer-have-to-match-container-with-status-message.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/__image_snapshots__/wizardcontainer-have-to-match-container-with-status-message.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/__image_snapshots__/wizardcontainer-have-to-match-large-screen.snap.png b/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/__image_snapshots__/wizardcontainer-have-to-match-large-screen.snap.png
index 0d0bc81d5c2..d5be0e921ef 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/__image_snapshots__/wizardcontainer-have-to-match-large-screen.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/__image_snapshots__/wizardcontainer-have-to-match-large-screen.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/__image_snapshots__/wizardcontainer-have-to-match-small-screen.snap.png b/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/__image_snapshots__/wizardcontainer-have-to-match-small-screen.snap.png
index 0bce3d4c15e..b1e78da594d 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/__image_snapshots__/wizardcontainer-have-to-match-small-screen.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Wizard/Container/__tests__/__image_snapshots__/wizardcontainer-have-to-match-small-screen.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/Wizard/EditButton/__tests__/EditButton.screenshot.test.ts b/packages/dnb-eufemia/src/extensions/forms/Wizard/EditButton/__tests__/EditButton.screenshot.test.ts
index 536238c939d..0e4355ac740 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Wizard/EditButton/__tests__/EditButton.screenshot.test.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/Wizard/EditButton/__tests__/EditButton.screenshot.test.ts
@@ -9,6 +9,10 @@ describe('EditButton', () => {
style: {
width: '20rem',
},
+ wrapperStyle: {
+ 'padding-left': '2.5rem',
+ 'padding-right': '2.5rem',
+ },
selector:
'[data-visual-test="wizard-edit-button"] .dnb-forms-wizard-layout__contents',
})
diff --git a/packages/dnb-eufemia/src/extensions/forms/Wizard/EditButton/__tests__/__image_snapshots__/editbutton-have-to-match-button-with-hr.snap.png b/packages/dnb-eufemia/src/extensions/forms/Wizard/EditButton/__tests__/__image_snapshots__/editbutton-have-to-match-button-with-hr.snap.png
index bec834dcb25..ac61ee72952 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/Wizard/EditButton/__tests__/__image_snapshots__/editbutton-have-to-match-button-with-hr.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Wizard/EditButton/__tests__/__image_snapshots__/editbutton-have-to-match-button-with-hr.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/Wizard/stories/Wizard.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Wizard/stories/Wizard.stories.tsx
index 73f9c1cb82c..add290991b4 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Wizard/stories/Wizard.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Wizard/stories/Wizard.stories.tsx
@@ -1,6 +1,6 @@
import React, { useCallback } from 'react'
import { P } from '../../../../elements'
-import { Button, Card } from '../../../../components'
+import { Button } from '../../../../components'
import Field, { Form, Wizard } from '../../Forms'
import { createRequest } from '../../Form/Handler/stories/FormHandler.stories'
import { debounceAsync } from '../../../../shared/helpers'
@@ -186,19 +186,19 @@ export function AsyncStepChange() {
// variant="drawer"
>
-
+
-
+
setActiveIndex(1)}>
@@ -209,9 +209,9 @@ export function AsyncStepChange() {
-
+
-
+
@@ -226,11 +226,11 @@ function RouterWizardContainer() {
const Step1 = () => {
return (
-
+
foo
bar
-
+
)
diff --git a/packages/dnb-eufemia/src/extensions/forms/Wizard/style/dnb-wizard-layout.scss b/packages/dnb-eufemia/src/extensions/forms/Wizard/style/dnb-wizard-layout.scss
index 06b1edc1eed..87a14b6a361 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Wizard/style/dnb-wizard-layout.scss
+++ b/packages/dnb-eufemia/src/extensions/forms/Wizard/style/dnb-wizard-layout.scss
@@ -3,7 +3,7 @@
.dnb-forms-wizard-layout {
display: flex;
flex-flow: row wrap;
- column-gap: var(--spacing-medium);
+ column-gap: var(--spacing-x-large);
&--drawer {
flex-direction: column;
diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAge.tsx b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAge.tsx
index 4182a009ee2..7540d355482 100644
--- a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAge.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAge.tsx
@@ -1,6 +1,5 @@
import React from 'react'
import { Field, Form, Iterate, Value, Wizard } from '../..'
-import { Card } from '../../../../components'
import { Lead } from '../../../../elements'
import { Translation, translations } from './ChildrenWithAgeTranslations'
import type { SectionProps } from '../../Form/Section'
@@ -57,7 +56,7 @@ function EditContainer({
const hasChildren = getValue('/hasChildren') === true
return (
-
+
{tr.ChildrenWithAge.hasChildren.title}
)}
-
+
)
}
@@ -194,7 +193,7 @@ function SummaryContainer({
const tr = Form.useTranslation()
return (
-
+
{{tr.ChildrenWithAge.hasChildren.title} }
@@ -252,7 +251,7 @@ function SummaryContainer({
{typeof toWizardStep === 'number' ? (
) : null}
-
+
)
}
diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-field-and-value-when-multiple-children.snap.png b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-field-and-value-when-multiple-children.snap.png
index 830a623a7f3..faee3a153a8 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-field-and-value-when-multiple-children.snap.png and b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-field-and-value-when-multiple-children.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-field-and-value-when-multiple-no-answers.snap.png b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-field-and-value-when-multiple-no-answers.snap.png
index 222001187eb..39e1cd20a49 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-field-and-value-when-multiple-no-answers.snap.png and b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-field-and-value-when-multiple-no-answers.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-field-and-value-when-no-children.snap.png b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-field-and-value-when-no-children.snap.png
index 915158655d6..abb19f0e0a6 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-field-and-value-when-no-children.snap.png and b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-field-and-value-when-no-children.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-field-and-value-when-previously-filled-out-data.snap.png b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-field-and-value-when-previously-filled-out-data.snap.png
index 915158655d6..abb19f0e0a6 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-field-and-value-when-previously-filled-out-data.snap.png and b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-field-and-value-when-previously-filled-out-data.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-field-and-value-when-multiple-children.snap.png b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-field-and-value-when-multiple-children.snap.png
index 3fe2169bf6a..0d76ff624be 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-field-and-value-when-multiple-children.snap.png and b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-field-and-value-when-multiple-children.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-field-and-value-when-multiple-no-answers.snap.png b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-field-and-value-when-multiple-no-answers.snap.png
index fa470aca492..5424cf279ce 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-field-and-value-when-multiple-no-answers.snap.png and b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-field-and-value-when-multiple-no-answers.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-field-and-value-when-no-children.snap.png b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-field-and-value-when-no-children.snap.png
index dffe647c90b..f038213d3ef 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-field-and-value-when-no-children.snap.png and b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-field-and-value-when-no-children.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-field-and-value-when-previously-filled-out-data.snap.png b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-field-and-value-when-previously-filled-out-data.snap.png
index dffe647c90b..f038213d3ef 100644
Binary files a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-field-and-value-when-previously-filled-out-data.snap.png and b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-field-and-value-when-previously-filled-out-data.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/DataValueWritePropsDocs.ts b/packages/dnb-eufemia/src/extensions/forms/hooks/DataValueWritePropsDocs.ts
index 17efdac19ff..e31d5ee2e1b 100644
--- a/packages/dnb-eufemia/src/extensions/forms/hooks/DataValueWritePropsDocs.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/hooks/DataValueWritePropsDocs.ts
@@ -76,7 +76,7 @@ export const DataValueWritePropsProperties: PropertiesTableProps = {
type: 'object',
status: 'optional',
},
- validator: {
+ onChangeValidator: {
doc: 'Custom validator function that is triggered on every change done by the user. The function can be either asynchronous or synchronous. The first parameter is the value, and the second parameter returns an object containing { errorMessages, connectWithPath, validators }.',
type: 'function',
status: 'optional',
diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx
index a8f318a2800..85ca015d91a 100644
--- a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx
@@ -284,10 +284,10 @@ describe('useFieldProps', () => {
})
describe('with local validation', () => {
- it('should return error when validator callback returns error', async () => {
+ it('should return error when onChangeValidator callback returns error', async () => {
const { result, rerender } = renderHook(useFieldProps, {
initialProps: {
- validator: () => new Error('This is wrong...'),
+ onChangeValidator: () => new Error('This is wrong...'),
value: 'foo',
validateInitially: true,
},
@@ -298,7 +298,7 @@ describe('useFieldProps', () => {
})
rerender({
- validator: () => undefined,
+ onChangeValidator: () => undefined,
value: 'bar',
validateInitially: true,
})
@@ -415,7 +415,7 @@ describe('useFieldProps', () => {
const { result } = renderHook(useFieldProps, {
initialProps: {
- validator: onChangeValidator,
+ onChangeValidator,
onBlurValidator,
},
})
@@ -452,7 +452,7 @@ describe('useFieldProps', () => {
const { result } = renderHook(useFieldProps, {
initialProps: {
onBlurValidator,
- validator: onChangeValidator,
+ onChangeValidator,
required: true,
},
})
@@ -518,7 +518,7 @@ describe('useFieldProps', () => {
const { result } = renderHook(useFieldProps, {
initialProps: {
onBlurValidator,
- validator: onChangeValidator,
+ onChangeValidator,
required: true,
},
})
@@ -541,7 +541,7 @@ describe('useFieldProps', () => {
})
})
- describe('with async validator', () => {
+ describe('with async onChangeValidator', () => {
const validateBlur = async (result, value = Date.now()) => {
act(() => {
result.current.handleChange(String(value))
@@ -550,7 +550,7 @@ describe('useFieldProps', () => {
}
it('should set fieldState', async () => {
- const validator = async () => {
+ const onChangeValidator = async () => {
await wait(1)
return null
@@ -558,7 +558,7 @@ describe('useFieldProps', () => {
const { result, rerender } = renderHook(useFieldProps, {
initialProps: {
- validator,
+ onChangeValidator,
value: '',
},
})
@@ -580,7 +580,7 @@ describe('useFieldProps', () => {
})
rerender({
- validator,
+ onChangeValidator,
value: '456',
})
@@ -611,7 +611,7 @@ describe('useFieldProps', () => {
const { result, rerender } = renderHook(useFieldProps, {
initialProps: {
- validator,
+ onChangeValidator: validator,
onBlurValidator: undefined,
value: '',
info: 'Info message',
@@ -659,7 +659,7 @@ describe('useFieldProps', () => {
})
rerender({
- validator,
+ onChangeValidator: validator,
onBlurValidator: undefined,
value: '456',
info: 'Info message changed',
@@ -691,7 +691,7 @@ describe('useFieldProps', () => {
})
rerender({
- validator: undefined,
+ onChangeValidator: undefined,
onBlurValidator: validator,
value: '456',
info: 'Info message changed',
@@ -725,8 +725,8 @@ describe('useFieldProps', () => {
})
})
- it('should set fieldState to error for async validator', async () => {
- const validator = async () => {
+ it('should set fieldState to error for async onChangeValidator', async () => {
+ const onChangeValidator = async () => {
await wait(1)
return new Error('Error message')
@@ -734,7 +734,7 @@ describe('useFieldProps', () => {
const { result, rerender } = renderHook(useFieldProps, {
initialProps: {
- validator,
+ onChangeValidator,
value: '',
},
})
@@ -753,7 +753,7 @@ describe('useFieldProps', () => {
})
rerender({
- validator,
+ onChangeValidator,
value: '456',
})
@@ -859,7 +859,7 @@ describe('useFieldProps', () => {
})
it('should hide fieldState error when disabled', async () => {
- const validator = async () => {
+ const onChangeValidator = async () => {
await wait(1)
return new Error('Error message')
@@ -867,7 +867,7 @@ describe('useFieldProps', () => {
const { result, rerender } = renderHook(useFieldProps, {
initialProps: {
- validator,
+ onChangeValidator,
value: '',
disabled: undefined,
},
@@ -887,7 +887,7 @@ describe('useFieldProps', () => {
})
rerender({
- validator,
+ onChangeValidator,
value: '456',
disabled: true,
})
@@ -1096,7 +1096,7 @@ describe('useFieldProps', () => {
schema,
// Step 3.
- validator: async (value: string) => {
+ onChangeValidator: async (value: string) => {
return value === 'throw-onChangeValidator'
? new Error(value)
: undefined
@@ -1521,7 +1521,7 @@ describe('useFieldProps', () => {
})
})
- it('should validate "validator" before onChange call', async () => {
+ it('should validate "onChangeValidator" before onChange call', async () => {
const events = []
const onChange: OnChange = async (value) => {
@@ -1531,14 +1531,14 @@ describe('useFieldProps', () => {
return { success: 'saved' } as const
}
}
- const validator = async () => {
- events.push('validator')
+ const onChangeValidator = async () => {
+ events.push('onChangeValidator')
}
const { result } = renderHook(useFieldProps, {
initialProps: {
onChange,
- validator,
+ onChangeValidator,
value: '',
},
})
@@ -1548,11 +1548,11 @@ describe('useFieldProps', () => {
await validateBlur(result, '123')
expect(result.current.fieldState).toBe('pending')
- expect(events).toEqual(['validator'])
+ expect(events).toEqual(['onChangeValidator'])
await waitFor(() => {
expect(result.current.fieldState).toBe('complete')
- expect(events).toEqual(['validator', 'onChange'])
+ expect(events).toEqual(['onChangeValidator', 'onChange'])
})
// Reset events
@@ -1561,11 +1561,11 @@ describe('useFieldProps', () => {
await validateBlur(result, '456')
expect(result.current.fieldState).toBe('pending')
- expect(events).toEqual(['validator'])
+ expect(events).toEqual(['onChangeValidator'])
await waitFor(() => {
expect(result.current.fieldState).toBe('success')
- expect(events).toEqual(['validator', 'onChange'])
+ expect(events).toEqual(['onChangeValidator', 'onChange'])
})
})
@@ -1619,13 +1619,13 @@ describe('useFieldProps', () => {
it('should set "disabled" on blur when "onBlurValidator" and async onChange is given', async () => {
const onChange: OnChange = async () => null
- const validator = async () => null
+ const onChangeValidator = async () => null
const onBlurValidator = async () => null
const { result, rerender } = renderHook(useFieldProps, {
initialProps: {
onChange,
- validator,
+ onChangeValidator,
onBlurValidator,
},
})
@@ -1650,7 +1650,7 @@ describe('useFieldProps', () => {
rerender({
onChange,
- validator: undefined,
+ onChangeValidator: undefined,
onBlurValidator,
})
@@ -1669,7 +1669,7 @@ describe('useFieldProps', () => {
})
})
- it('should validate "validator" and "onBlurValidator" before onChange call', async () => {
+ it('should validate "onChangeValidator" and "onBlurValidator" before onChange call', async () => {
const events = []
const onChange: OnChange = async (value) => {
@@ -1679,8 +1679,8 @@ describe('useFieldProps', () => {
return { success: 'saved' } as const
}
}
- const validator = async () => {
- events.push('validator')
+ const onChangeValidator = async () => {
+ events.push('onChangeValidator')
}
const onBlurValidator = async () => {
events.push('onBlurValidator')
@@ -1689,7 +1689,7 @@ describe('useFieldProps', () => {
const { result } = renderHook(useFieldProps, {
initialProps: {
onChange,
- validator,
+ onChangeValidator,
onBlurValidator,
value: '',
},
@@ -1700,12 +1700,12 @@ describe('useFieldProps', () => {
await validateBlur(result, '123')
expect(result.current.fieldState).toBe('pending')
- expect(events).toEqual(['validator', 'onBlurValidator'])
+ expect(events).toEqual(['onChangeValidator', 'onBlurValidator'])
await waitFor(() => {
expect(result.current.fieldState).toBe('complete')
expect(events).toEqual([
- 'validator',
+ 'onChangeValidator',
'onBlurValidator',
'onChange',
])
@@ -1717,27 +1717,27 @@ describe('useFieldProps', () => {
await validateBlur(result, '456')
expect(result.current.fieldState).toBe('pending')
- expect(events).toEqual(['validator', 'onBlurValidator'])
+ expect(events).toEqual(['onChangeValidator', 'onBlurValidator'])
await wait(100)
await waitFor(() => {
expect(result.current.fieldState).toBe('success')
expect(events).toEqual([
- 'validator',
+ 'onChangeValidator',
'onBlurValidator',
'onChange',
])
})
})
- it('should skip onChange call when "validator" returns error', async () => {
+ it('should skip onChange call when "onChangeValidator" returns error', async () => {
const events = []
const onChange: OnChange = async () => {
events.push('onChange')
}
- const validator = async () => {
- events.push('validator')
+ const onChangeValidator = async () => {
+ events.push('onChangeValidator')
return new Error('Error message')
}
@@ -1745,7 +1745,7 @@ describe('useFieldProps', () => {
const { result } = renderHook(useFieldProps, {
initialProps: {
onChange,
- validator,
+ onChangeValidator,
value: '',
},
})
@@ -1755,7 +1755,7 @@ describe('useFieldProps', () => {
await validateBlur(result, '123')
expect(result.current.fieldState).toBe('pending')
- expect(events).toEqual(['validator'])
+ expect(events).toEqual(['onChangeValidator'])
await waitFor(() => {
expect(result.current.fieldState).toBe('error')
@@ -1767,11 +1767,11 @@ describe('useFieldProps', () => {
await validateBlur(result, '456')
expect(result.current.fieldState).toBe('pending')
- expect(events).toEqual(['validator'])
+ expect(events).toEqual(['onChangeValidator'])
await waitFor(() => {
expect(result.current.fieldState).toBe('error')
- expect(events).toEqual(['validator'])
+ expect(events).toEqual(['onChangeValidator'])
})
})
@@ -1852,7 +1852,7 @@ describe('useFieldProps', () => {
})
})
- it('should wait for both "validator" and "onBlurValidator" and DataContext onChange before local onChange call', async () => {
+ it('should wait for both "onChangeValidator" and "onBlurValidator" and DataContext onChange before local onChange call', async () => {
const events = []
const path = '/foo'
@@ -1868,8 +1868,8 @@ describe('useFieldProps', () => {
return new Error('Error message')
}
}
- const validator = async () => {
- events.push('validator')
+ const onChangeValidator = async () => {
+ events.push('onChangeValidator')
}
const onBlurValidator = async () => {
events.push('onBlurValidator')
@@ -1878,7 +1878,7 @@ describe('useFieldProps', () => {
const { result } = renderHook(useFieldProps, {
initialProps: {
onChange,
- validator,
+ onChangeValidator,
onBlurValidator,
path,
value: '',
@@ -1893,12 +1893,12 @@ describe('useFieldProps', () => {
await validateBlur(result, '123')
expect(result.current.fieldState).toBe('pending')
- expect(events).toEqual(['validator', 'onBlurValidator'])
+ expect(events).toEqual(['onChangeValidator', 'onBlurValidator'])
await waitFor(() => {
expect(result.current.fieldState).toBe('error')
expect(events).toEqual([
- 'validator',
+ 'onChangeValidator',
'onBlurValidator',
'onChangeForm',
])
@@ -1910,12 +1910,12 @@ describe('useFieldProps', () => {
await validateBlur(result, '456')
expect(result.current.fieldState).toBe('pending')
- expect(events).toEqual(['validator', 'onBlurValidator'])
+ expect(events).toEqual(['onChangeValidator', 'onBlurValidator'])
await waitFor(() => {
expect(result.current.fieldState).toBe('success')
expect(events).toEqual([
- 'validator',
+ 'onChangeValidator',
'onBlurValidator',
'onChangeForm',
'onChangeField',
@@ -1923,7 +1923,7 @@ describe('useFieldProps', () => {
})
})
- it('should handle gracefully the "validator" and "onBlurValidator" and DataContext "onChange" and before the local onChange call', async () => {
+ it('should handle gracefully the "onChangeValidator" and "onBlurValidator" and DataContext "onChange" and before the local onChange call', async () => {
const events = []
const path = '/foo'
@@ -1945,11 +1945,11 @@ describe('useFieldProps', () => {
return { success: 'saved' } as const
}
- const validator = async (value) => {
- events.push('validator')
+ const onChangeValidator = async (value) => {
+ events.push('onChangeValidator')
if (value === 'invalid') {
- return new Error('Error message by validator')
+ return new Error('Error message by onChangeValidator')
}
}
const onBlurValidator = async (value) => {
@@ -1963,7 +1963,7 @@ describe('useFieldProps', () => {
const { result } = renderHook(useFieldProps, {
initialProps: {
onChange: onChangeField,
- validator,
+ onChangeValidator,
onBlurValidator,
path,
value: '',
@@ -1986,7 +1986,7 @@ describe('useFieldProps', () => {
await waitFor(() => {
expect(events).toEqual([
- 'validator',
+ 'onChangeValidator',
'onChangeForm',
'onChangeField',
])
@@ -2002,7 +2002,7 @@ describe('useFieldProps', () => {
await waitFor(() => {
expect(events).toEqual([
- 'validator',
+ 'onChangeValidator',
'onChangeForm',
'onChangeField',
'onBlurValidator',
@@ -2018,12 +2018,12 @@ describe('useFieldProps', () => {
result.current.handleChange('invalid')
})
- expect(events).toEqual(['validator'])
+ expect(events).toEqual(['onChangeValidator'])
expect(result.current.fieldState).toBe('pending')
expect(result.current.error).toBeUndefined()
await waitFor(() => {
- expect(events).toEqual(['validator'])
+ expect(events).toEqual(['onChangeValidator'])
expect(result.current.fieldState).toBe('error')
expect(result.current.error).toBeInstanceOf(Error)
})
@@ -2035,11 +2035,11 @@ describe('useFieldProps', () => {
expect(result.current.fieldState).toBe('pending')
expect(result.current.error).toBeInstanceOf(Error)
expect(getError(result.current.error).message).toBe(
- 'Error message by validator'
+ 'Error message by onChangeValidator'
)
await waitFor(() => {
- expect(events).toEqual(['validator', 'onBlurValidator'])
+ expect(events).toEqual(['onChangeValidator', 'onBlurValidator'])
expect(result.current.fieldState).toBe('error')
expect(result.current.error).toBeInstanceOf(Error)
expect(getError(result.current.error).message).toBe(
@@ -2054,13 +2054,13 @@ describe('useFieldProps', () => {
result.current.handleChange('valid')
})
- expect(events).toEqual(['validator'])
+ expect(events).toEqual(['onChangeValidator'])
expect(result.current.fieldState).toBe('pending')
expect(result.current.error).toBeUndefined()
await waitFor(() => {
expect(events).toEqual([
- 'validator',
+ 'onChangeValidator',
'onChangeForm',
'onChangeField',
])
@@ -2086,7 +2086,7 @@ describe('useFieldProps', () => {
})
})
- it('should have correct order for when calling the "validator" (if initiated and "onBlurValidator") and DataContext "onChange", before the local onChange call', async () => {
+ it('should have correct order for when calling the "onChangeValidator" (if initiated and "onBlurValidator") and DataContext "onChange", before the local onChange call', async () => {
const events = []
const path = '/foo'
@@ -2112,11 +2112,11 @@ describe('useFieldProps', () => {
return { success: 'saved' } as const
}
}
- const validator = async (value) => {
- events.push('validator')
+ const onChangeValidator = async (value) => {
+ events.push('onChangeValidator')
- if (value === 'invalid-validator') {
- return new Error('Error in validator')
+ if (value === 'invalid-onChangeValidator') {
+ return new Error('Error in onChangeValidator')
}
}
const onBlurValidator = async (value) => {
@@ -2130,7 +2130,7 @@ describe('useFieldProps', () => {
const { result } = renderHook(useFieldProps, {
initialProps: {
onChange: onChangeField,
- validator,
+ onChangeValidator,
onBlurValidator,
path,
value: '',
@@ -2143,7 +2143,7 @@ describe('useFieldProps', () => {
/**
* The order we test in is:
*
- * - validator
+ * - onChangeValidator
* - onBlurValidator (if initiated)
* - onChangeForm
* - onChangeField
@@ -2160,7 +2160,7 @@ describe('useFieldProps', () => {
await waitFor(() => {
expect(events).toEqual([
- 'validator',
+ 'onChangeValidator',
'onChangeForm',
'onChangeField',
])
@@ -2176,7 +2176,7 @@ describe('useFieldProps', () => {
await waitFor(() => {
expect(events).toEqual([
- 'validator',
+ 'onChangeValidator',
'onChangeForm',
'onChangeField',
'onBlurValidator',
@@ -2189,18 +2189,18 @@ describe('useFieldProps', () => {
events.splice(0, events.length)
act(() => {
- result.current.handleChange('invalid-validator')
+ result.current.handleChange('invalid-onChangeValidator')
})
- expect(events).toEqual(['validator'])
+ expect(events).toEqual(['onChangeValidator'])
expect(result.current.fieldState).toBe('pending')
expect(result.current.error).toBeUndefined()
await waitFor(() => {
- expect(events).toEqual(['validator'])
+ expect(events).toEqual(['onChangeValidator'])
expect(result.current.fieldState).toBe('error')
expect(getError(result.current.error).message).toBe(
- 'Error in validator'
+ 'Error in onChangeValidator'
)
})
@@ -2211,7 +2211,7 @@ describe('useFieldProps', () => {
result.current.handleChange('invalid-onBlurValidator')
})
- expect(events).toEqual(['validator'])
+ expect(events).toEqual(['onChangeValidator'])
await wait(1)
@@ -2223,7 +2223,7 @@ describe('useFieldProps', () => {
await waitFor(() => {
expect(events).toEqual([
- 'validator',
+ 'onChangeValidator',
'onChangeForm',
'onChangeField',
'onBlurValidator',
@@ -2241,12 +2241,12 @@ describe('useFieldProps', () => {
result.current.handleChange('invalid-onChangeForm')
})
- expect(events).toEqual(['validator'])
+ expect(events).toEqual(['onChangeValidator'])
expect(result.current.fieldState).toBe('pending')
await waitFor(() => {
- expect(events).toEqual(['validator', 'onChangeForm'])
+ expect(events).toEqual(['onChangeValidator', 'onChangeForm'])
expect(result.current.fieldState).toBe('error')
expect(getError(result.current.error).message).toBe(
'Error in onChangeForm'
@@ -2260,13 +2260,13 @@ describe('useFieldProps', () => {
result.current.handleChange('invalid-onChangeField')
})
- expect(events).toEqual(['validator'])
+ expect(events).toEqual(['onChangeValidator'])
expect(result.current.fieldState).toBe('pending')
await waitFor(() => {
expect(events).toEqual([
- 'validator',
+ 'onChangeValidator',
'onChangeForm',
'onChangeField',
])
@@ -2283,7 +2283,7 @@ describe('useFieldProps', () => {
result.current.handleChange('invalid-onBlurValidator')
})
- expect(events).toEqual(['validator'])
+ expect(events).toEqual(['onChangeValidator'])
await wait(1)
@@ -2293,7 +2293,7 @@ describe('useFieldProps', () => {
await waitFor(() => {
expect(events).toEqual([
- 'validator',
+ 'onChangeValidator',
'onChangeForm',
'onChangeField',
'onBlurValidator',
@@ -3372,6 +3372,7 @@ describe('useFieldProps', () => {
expect(result.current.error).toBeInstanceOf(Error)
})
+ // Deprecated – can be removed in v11
describe('validator', () => {
describe('validateInitially', () => {
it('should show error message initially', async () => {
@@ -4371,17 +4372,17 @@ describe('useFieldProps', () => {
})
})
- describe('onBlurValidator', () => {
+ describe('onChangeValidator', () => {
describe('validateInitially', () => {
it('should show error message initially', async () => {
- const onBlurValidator = jest.fn(() => {
+ const onChangeValidator = jest.fn(() => {
return new Error('My Error')
})
render(
@@ -4390,11 +4391,11 @@ describe('useFieldProps', () => {
await waitFor(() => {
expect(screen.queryByRole('alert')).toBeInTheDocument()
})
- expect(onBlurValidator).toHaveBeenCalledTimes(1)
+ expect(onChangeValidator).toHaveBeenCalledTimes(1)
})
- it('should show error message initially when validator is async', async () => {
- const onBlurValidator = jest.fn(async () => {
+ it('should show error message initially when onChangeValidator is async', async () => {
+ const onChangeValidator = jest.fn(async () => {
return new Error('My Error')
})
@@ -4402,7 +4403,7 @@ describe('useFieldProps', () => {
@@ -4411,33 +4412,31 @@ describe('useFieldProps', () => {
await waitFor(() => {
expect(screen.queryByRole('alert')).toBeInTheDocument()
})
- expect(onBlurValidator).toHaveBeenCalledTimes(1)
+ expect(onChangeValidator).toHaveBeenCalledTimes(1)
})
})
describe('connectWithPath', () => {
- const validatorFn: UseFieldProps['validator'] = (
- num,
- { connectWithPath }
- ) => {
- const amount = connectWithPath('/refValue').getValue()
+ const onChangeValidatorFn: UseFieldProps['onChangeValidator'] =
+ (num, { connectWithPath }) => {
+ const amount = connectWithPath('/refValue').getValue()
- if (amount >= num) {
- return new Error(`The amount should be greater than ${amount}`)
+ if (amount >= num) {
+ return new Error(`The amount should be greater than ${amount}`)
+ }
}
- }
- it('should show validator error on form submit', async () => {
- const validator = jest.fn(validatorFn)
+ it('should show onChangeValidator error on form submit', async () => {
+ const onChangeValidator = jest.fn(onChangeValidatorFn)
render(
)
@@ -4452,9 +4451,8 @@ describe('useFieldProps', () => {
'The amount should be greater than 2'
)
})
-
- expect(validator).toHaveBeenCalledTimes(1)
- expect(validator).toHaveBeenLastCalledWith(
+ expect(onChangeValidator).toHaveBeenCalledTimes(1)
+ expect(onChangeValidator).toHaveBeenLastCalledWith(
2,
expect.objectContaining({
connectWithPath: expect.any(Function),
@@ -4463,56 +4461,55 @@ describe('useFieldProps', () => {
})
it('should update error message on input change', async () => {
- const validator = jest.fn(validatorFn)
+ const onChangeValidator = jest.fn(onChangeValidatorFn)
render(
-
+
)
- const [inputWithRefValue, inputWithValidator] = Array.from(
+ const [inputWithRefValue] = Array.from(
document.querySelectorAll('input')
)
expect(screen.queryByRole('alert')).not.toBeInTheDocument()
- // Make a change to the input with the validator
- await userEvent.type(inputWithValidator, '2')
- fireEvent.blur(inputWithValidator)
+ // Show error message
+ fireEvent.submit(document.querySelector('form'))
await waitFor(() => {
expect(screen.queryByRole('alert')).toBeInTheDocument()
expect(screen.queryByRole('alert')).toHaveTextContent(
- 'The amount should be greater than 12'
+ 'The amount should be greater than 2'
)
})
// Make a change to the ref input
- await userEvent.type(inputWithRefValue, '3')
+ await userEvent.type(inputWithRefValue, '2')
expect(screen.queryByRole('alert')).toHaveTextContent(
- 'The amount should be greater than 123'
+ 'The amount should be greater than 22'
)
})
it('should hide error message when validation is successful', async () => {
- const validator = jest.fn(validatorFn)
+ const onChangeValidator = jest.fn(onChangeValidatorFn)
render(
)
@@ -4528,13 +4525,16 @@ describe('useFieldProps', () => {
await waitFor(() => {
expect(screen.queryByRole('alert')).toBeInTheDocument()
+ expect(screen.queryByRole('alert')).toHaveTextContent(
+ 'The amount should be greater than 2'
+ )
})
await userEvent.type(inputWithRefValue, '{Backspace}')
expect(screen.queryByRole('alert')).not.toBeInTheDocument()
- expect(validator).toHaveBeenCalledTimes(2)
- expect(validator).toHaveBeenLastCalledWith(
+ expect(onChangeValidator).toHaveBeenCalledTimes(2)
+ expect(onChangeValidator).toHaveBeenLastCalledWith(
2,
expect.objectContaining({
connectWithPath: expect.any(Function),
@@ -4542,17 +4542,17 @@ describe('useFieldProps', () => {
)
})
- it('should keep error message hidden during ref input change', async () => {
- const validator = jest.fn(validatorFn)
+ it('should keep error message hidden after validation is successful and another input change', async () => {
+ const onChangeValidator = jest.fn(onChangeValidatorFn)
render(
)
@@ -4568,12 +4568,15 @@ describe('useFieldProps', () => {
await waitFor(() => {
expect(screen.queryByRole('alert')).toBeInTheDocument()
+ expect(screen.queryByRole('alert')).toHaveTextContent(
+ 'The amount should be greater than 2'
+ )
})
await userEvent.type(inputWithRefValue, '{Backspace}')
expect(screen.queryByRole('alert')).not.toBeInTheDocument()
- expect(validator).toHaveBeenCalledTimes(2)
+ expect(onChangeValidator).toHaveBeenCalledTimes(2)
await userEvent.type(inputWithRefValue, '2')
@@ -4581,36 +4584,41 @@ describe('useFieldProps', () => {
})
describe('validateInitially', () => {
- it('should not show error message initially', async () => {
- const validator = jest.fn(validatorFn)
+ it('should show error message initially', async () => {
+ const onChangeValidator = jest.fn(onChangeValidatorFn)
render(
)
- expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toBeInTheDocument()
+ expect(screen.queryByRole('alert')).toHaveTextContent(
+ 'The amount should be greater than 2'
+ )
+ })
})
- it('should not show error message while typing', async () => {
- const validator = jest.fn(validatorFn)
+ it('should not show error message after it was hidden while typing', async () => {
+ const onChangeValidator = jest.fn(onChangeValidatorFn)
render(
@@ -4620,7 +4628,12 @@ describe('useFieldProps', () => {
document.querySelectorAll('input')
)
- expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toBeInTheDocument()
+ expect(screen.queryByRole('alert')).toHaveTextContent(
+ 'The amount should be greater than 2'
+ )
+ })
await userEvent.type(inputWithRefValue, '{Backspace}')
@@ -4633,36 +4646,41 @@ describe('useFieldProps', () => {
})
describe('validateUnchanged', () => {
- it('should not show error message initially', async () => {
- const validator = jest.fn(validatorFn)
+ it('should show error message initially', async () => {
+ const onChangeValidator = jest.fn(onChangeValidatorFn)
render(
)
- expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toBeInTheDocument()
+ expect(screen.queryByRole('alert')).toHaveTextContent(
+ 'The amount should be greater than 2'
+ )
+ })
})
- it('should not show error message while typing', async () => {
- const validator = jest.fn(validatorFn)
+ it('should hide and show error message while typing', async () => {
+ const onChangeValidator = jest.fn(onChangeValidatorFn)
render(
@@ -4672,7 +4690,12 @@ describe('useFieldProps', () => {
document.querySelectorAll('input')
)
- expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toBeInTheDocument()
+ expect(screen.queryByRole('alert')).toHaveTextContent(
+ 'The amount should be greater than 2'
+ )
+ })
await userEvent.type(inputWithRefValue, '{Backspace}')
@@ -4681,23 +4704,1005 @@ describe('useFieldProps', () => {
await userEvent.type(inputWithRefValue, '3')
await waitFor(() => {
- expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+ expect(screen.queryByRole('alert')).toBeInTheDocument()
+ expect(screen.queryByRole('alert')).toHaveTextContent(
+ 'The amount should be greater than 3'
+ )
})
})
})
describe('continuousValidation', () => {
it('should show not show error message initially', async () => {
- const validator = jest.fn(validatorFn)
+ const onChangeValidator = jest.fn(onChangeValidatorFn)
render(
+
+ )
+
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+ })
+
+ it('should hide and show error message while typing', async () => {
+ const onChangeValidator = jest.fn(onChangeValidatorFn)
+
+ render(
+
+
+
+
+
+ )
+
+ const [inputWithRefValue] = Array.from(
+ document.querySelectorAll('input')
+ )
+
+ // Show error message
+ fireEvent.submit(document.querySelector('form'))
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toBeInTheDocument()
+ expect(screen.queryByRole('alert')).toHaveTextContent(
+ 'The amount should be greater than 2'
+ )
+ })
+
+ await userEvent.type(inputWithRefValue, '{Backspace}')
+
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+
+ await userEvent.type(inputWithRefValue, '3')
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toBeInTheDocument()
+ expect(screen.queryByRole('alert')).toHaveTextContent(
+ 'The amount should be greater than 3'
+ )
+ })
+ })
+ })
+ })
+
+ describe('onChangeValidators given as an array', () => {
+ it('should call all onChangeValidators returned as an array', async () => {
+ const fooValidator = jest.fn((value) => {
+ if (value.includes('foo')) {
+ return new Error('foo')
+ }
+ })
+
+ const barValidator = jest.fn((value) => {
+ if (value.includes('bar')) {
+ return new Error('bar')
+ }
+ })
+
+ const myOnChangeValidator = jest.fn(() => {
+ return [fooValidator, barValidator]
+ })
+
+ render(
+
+
+
+ )
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toHaveTextContent('foo')
+ })
+ expect(myOnChangeValidator).toHaveBeenCalledTimes(1)
+ expect(fooValidator).toHaveBeenCalledTimes(1)
+ expect(barValidator).toHaveBeenCalledTimes(0)
+
+ await userEvent.type(
+ document.querySelector('input'),
+ '{Backspace}bar'
+ )
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toHaveTextContent('bar')
+ })
+ expect(myOnChangeValidator).toHaveBeenCalledTimes(5)
+ expect(fooValidator).toHaveBeenCalledTimes(5)
+ expect(barValidator).toHaveBeenCalledTimes(4)
+ })
+
+ it('should call all validators returned as an array (mixed async and sync)', async () => {
+ const fooValidator = jest.fn(async (value) => {
+ if (value.includes('foo')) {
+ return new Error('foo')
+ }
+ })
+
+ const barValidator = jest.fn((value) => {
+ if (value.includes('bar')) {
+ return new Error('bar')
+ }
+ })
+
+ // The main validator needs to be async, because it contains async validators in the array
+ const myValidator = jest.fn(async () => {
+ return [fooValidator, barValidator]
+ })
+
+ render(
+
+
+
+ )
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toHaveTextContent('foo')
+ })
+ expect(myValidator).toHaveBeenCalledTimes(1)
+ expect(fooValidator).toHaveBeenCalledTimes(1)
+ expect(barValidator).toHaveBeenCalledTimes(0)
+
+ await userEvent.type(
+ document.querySelector('input'),
+ '{Backspace}bar'
+ )
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toHaveTextContent('bar')
+ })
+ expect(myValidator).toHaveBeenCalledTimes(5)
+ expect(fooValidator).toHaveBeenCalledTimes(5)
+ expect(barValidator).toHaveBeenCalledTimes(4)
+ })
+ })
+
+ describe('exportValidators', () => {
+ it('should call exported validators from mock component', async () => {
+ let internalValidators, fooValidator, barValidator, bazValidator
+
+ const MockComponent = (props) => {
+ barValidator = jest.fn((value) => {
+ if (value.includes('bar')) {
+ return new Error('bar')
+ }
+ })
+
+ bazValidator = jest.fn((value) => {
+ if (value.includes('baz')) {
+ return new Error('baz')
+ }
+ })
+
+ internalValidators = jest.fn((value) => {
+ return barValidator(value) || bazValidator(value)
+ })
+
+ return (
+
+ )
+ }
+
+ const publicValidator = jest.fn(
+ (value, { validators: { barValidator, bazValidator } }) => {
+ fooValidator = jest.fn(() => {
+ if (value.includes('foo')) {
+ return new Error('foo')
+ }
+ })
+
+ return [fooValidator, barValidator, bazValidator]
+ }
+ )
+
+ render(
+
+
+
+ )
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toHaveTextContent('foo')
+ })
+ expect(publicValidator).toHaveBeenCalledTimes(1)
+ expect(fooValidator).toHaveBeenCalledTimes(1)
+ expect(barValidator).toHaveBeenCalledTimes(0)
+ expect(bazValidator).toHaveBeenCalledTimes(0)
+ expect(internalValidators).toHaveBeenCalledTimes(0)
+
+ expect(publicValidator).toHaveBeenLastCalledWith(
+ 'foo',
+ expect.objectContaining({
+ validators: {
+ barValidator,
+ bazValidator,
+ },
+ })
+ )
+
+ await userEvent.type(
+ document.querySelector('input'),
+ '{Backspace}bar'
+ )
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toHaveTextContent('bar')
+ })
+ expect(publicValidator).toHaveBeenCalledTimes(5)
+ expect(fooValidator).toHaveBeenCalledTimes(1)
+ expect(barValidator).toHaveBeenCalledTimes(4)
+ expect(bazValidator).toHaveBeenCalledTimes(3)
+ expect(internalValidators).toHaveBeenCalledTimes(0)
+
+ await userEvent.type(
+ document.querySelector('input'),
+ '{Backspace}baz'
+ )
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toHaveTextContent('baz')
+ })
+ expect(publicValidator).toHaveBeenCalledTimes(9)
+ expect(fooValidator).toHaveBeenCalledTimes(1)
+ expect(barValidator).toHaveBeenCalledTimes(8)
+ expect(bazValidator).toHaveBeenCalledTimes(7)
+ expect(internalValidators).toHaveBeenCalledTimes(0)
+ })
+
+ it('should export and call same validator without "Maximum call stack size exceeded"', async () => {
+ const onBlurValidator = jest.fn((value) => {
+ if (value === '1234') {
+ return Error('Error message')
+ }
+ })
+
+ render(
+
+ )
+
+ const input = document.querySelector('input')
+
+ await userEvent.type(input, '123')
+ fireEvent.blur(input)
+
+ expect(onBlurValidator).toHaveBeenCalledTimes(1)
+ expect(document.querySelector('.dnb-form-status')).toBeNull()
+
+ await userEvent.type(input, '4')
+ fireEvent.blur(input)
+
+ expect(onBlurValidator).toHaveBeenCalledTimes(2)
+ await waitFor(() => {
+ expect(
+ document.querySelector('.dnb-form-status')
+ ).toHaveTextContent('Error message')
+ })
+ })
+
+ it('should show error on every value change', async () => {
+ const exportedValidator = jest.fn((value) => {
+ if (value === '1234') {
+ return Error('Error message')
+ }
+ })
+
+ const myValidator = jest.fn((value, { validators }) => {
+ const { exportedValidator } = validators
+
+ return [exportedValidator]
+ })
+
+ const MockComponent = (props) => {
+ return (
+
+ )
+ }
+
+ render( )
+
+ const input = document.querySelector('input')
+
+ await userEvent.type(input, '123')
+ fireEvent.blur(input)
+
+ expect(document.querySelector('.dnb-form-status')).toBeNull()
+
+ await userEvent.type(input, '4')
+ fireEvent.blur(input)
+
+ expect(exportedValidator).toHaveBeenCalledTimes(2)
+ expect(myValidator).toHaveBeenCalledTimes(2)
+ await waitFor(() => {
+ expect(
+ document.querySelector('.dnb-form-status')
+ ).toHaveTextContent('Error message')
+ })
+
+ await userEvent.type(input, '{Backspace}4')
+ fireEvent.blur(input)
+
+ expect(exportedValidator).toHaveBeenCalledTimes(3)
+ expect(myValidator).toHaveBeenCalledTimes(3)
+ await waitFor(() => {
+ expect(
+ document.querySelector('.dnb-form-status')
+ ).toHaveTextContent('Error message')
+ })
+ })
+
+ it('should support mixed sync and async validators', async () => {
+ const exportedValidator = jest.fn(async (value) => {
+ if (value === '1234') {
+ return Error('Error message')
+ }
+ })
+
+ const myValidator = jest.fn((value, { validators }) => {
+ const { exportedValidator } = validators
+
+ return [exportedValidator]
+ })
+
+ const MockComponent = (props) => {
+ return (
+
+ )
+ }
+
+ render( )
+
+ const input = document.querySelector('input')
+
+ await userEvent.type(input, '123')
+ fireEvent.blur(input)
+
+ expect(document.querySelector('.dnb-form-status')).toBeNull()
+
+ await userEvent.type(input, '4')
+ fireEvent.blur(input)
+
+ expect(exportedValidator).toHaveBeenCalledTimes(1)
+ expect(myValidator).toHaveBeenCalledTimes(4)
+ await waitFor(() => {
+ expect(
+ document.querySelector('.dnb-form-status')
+ ).toHaveTextContent('Error message')
+ })
+
+ await userEvent.type(input, '{Backspace}4')
+ fireEvent.blur(input)
+
+ expect(exportedValidator).toHaveBeenCalledTimes(2)
+ expect(myValidator).toHaveBeenCalledTimes(6)
+ await waitFor(() => {
+ expect(
+ document.querySelector('.dnb-form-status')
+ ).toHaveTextContent('Error message')
+ })
+ })
+
+ it('should only call returned validators (barValidator should not be called)', async () => {
+ let internalValidators, fooValidator, barValidator, bazValidator
+
+ const MockComponent = (props) => {
+ barValidator = jest.fn((value) => {
+ if (value.includes('bar')) {
+ return new Error('bar')
+ }
+ })
+
+ bazValidator = jest.fn((value) => {
+ if (value.includes('baz')) {
+ return new Error('baz')
+ }
+ })
+
+ internalValidators = jest.fn((value) => {
+ return barValidator(value) || bazValidator(value)
+ })
+
+ return (
+
+ )
+ }
+
+ const publicValidator = jest.fn(
+ (value, { validators: { bazValidator } }) => {
+ fooValidator = jest.fn(() => {
+ if (value.includes('foo')) {
+ return new Error('foo')
+ }
+ })
+
+ return [fooValidator, bazValidator]
+ }
+ )
+
+ render(
+
+
+
+ )
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toHaveTextContent('foo')
+ })
+ expect(publicValidator).toHaveBeenCalledTimes(1)
+ expect(fooValidator).toHaveBeenCalledTimes(1)
+ expect(barValidator).toHaveBeenCalledTimes(0)
+ expect(bazValidator).toHaveBeenCalledTimes(0)
+ expect(internalValidators).toHaveBeenCalledTimes(0)
+
+ expect(publicValidator).toHaveBeenLastCalledWith(
+ 'foo',
+ expect.objectContaining({
+ validators: {
+ barValidator,
+ bazValidator,
+ },
+ })
+ )
+
+ await userEvent.type(
+ document.querySelector('input'),
+ '{Backspace}bar' // remove one letter from bar, so the bar validator should return undefined
+ )
+ await waitFor(() => {
+ // Here we should not see the bar validator called
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+ })
+ expect(publicValidator).toHaveBeenCalledTimes(5)
+ expect(fooValidator).toHaveBeenCalledTimes(1)
+ expect(barValidator).toHaveBeenCalledTimes(0)
+ expect(bazValidator).toHaveBeenCalledTimes(4)
+ expect(internalValidators).toHaveBeenCalledTimes(0)
+
+ await userEvent.type(
+ document.querySelector('input'),
+ '{Backspace}baz'
+ )
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toHaveTextContent('baz')
+ })
+ expect(publicValidator).toHaveBeenCalledTimes(9)
+ expect(fooValidator).toHaveBeenCalledTimes(1)
+ expect(barValidator).toHaveBeenCalledTimes(0)
+ expect(bazValidator).toHaveBeenCalledTimes(8)
+ expect(internalValidators).toHaveBeenCalledTimes(0)
+ })
+
+ it('should show error when validateInitially is set to true', async () => {
+ const exportedValidator = jest.fn(() => {
+ return Error('Error message')
+ })
+
+ const myValidator = jest.fn((value, { validators }) => {
+ const { exportedValidator } = validators
+ return [exportedValidator]
+ })
+
+ const MockComponent = (props) => {
+ return (
+
+ )
+ }
+
+ render( )
+
+ await waitFor(() => {
+ expect(
+ document.querySelector('.dnb-form-status')
+ ).toBeInTheDocument()
+ })
+ })
+
+ it('should not run exported internal validators when a onChangeValidator is given', async () => {
+ const exportedValidator = jest.fn(() => {
+ return undefined
+ })
+
+ const myValidator = jest.fn(() => {
+ return undefined
+ })
+
+ const MockComponent = (props) => {
+ return (
+
+ )
+ }
+
+ render( )
+
+ await expect(() => {
+ expect(
+ document.querySelector('.dnb-form-status')
+ ).toBeInTheDocument()
+ }).toNeverResolve()
+ })
+
+ it('should not call internal validators when they are not returned in the publicValidator', async () => {
+ let internalValidators, barValidator, bazValidator
+
+ const MockComponent = (props) => {
+ barValidator = jest.fn((value) => {
+ if (value.includes('bar')) {
+ return new Error('bar')
+ }
+ })
+
+ bazValidator = jest.fn((value) => {
+ if (value.includes('baz')) {
+ return new Error('baz')
+ }
+ })
+
+ internalValidators = jest.fn((value) => {
+ return barValidator(value) || bazValidator(value)
+ })
+
+ return (
+
+ )
+ }
+
+ const publicValidator = jest.fn((value) => {
+ if (value.includes('foo')) {
+ return new Error('foo')
+ }
+ })
+
+ render(
+
+
+
+ )
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toHaveTextContent('foo')
+ })
+ expect(publicValidator).toHaveBeenCalledTimes(1)
+ expect(barValidator).toHaveBeenCalledTimes(0)
+ expect(bazValidator).toHaveBeenCalledTimes(0)
+ expect(internalValidators).toHaveBeenCalledTimes(0)
+
+ expect(publicValidator).toHaveBeenLastCalledWith(
+ 'foo',
+ expect.objectContaining({
+ validators: {
+ barValidator,
+ bazValidator,
+ },
+ })
+ )
+
+ await userEvent.type(
+ document.querySelector('input'),
+ '{Backspace}bar'
+ )
+ await expect(() => {
+ expect(screen.queryByRole('alert')).toBeInTheDocument()
+ }).toNeverResolve()
+ expect(publicValidator).toHaveBeenCalledTimes(5)
+ expect(barValidator).toHaveBeenCalledTimes(0)
+ expect(bazValidator).toHaveBeenCalledTimes(0)
+ expect(internalValidators).toHaveBeenCalledTimes(0)
+
+ await userEvent.type(
+ document.querySelector('input'),
+ '{Backspace}baz'
+ )
+
+ await expect(() => {
+ expect(screen.queryByRole('alert')).toBeInTheDocument()
+ }).toNeverResolve()
+ expect(publicValidator).toHaveBeenCalledTimes(9)
+ expect(barValidator).toHaveBeenCalledTimes(0)
+ expect(bazValidator).toHaveBeenCalledTimes(0)
+ expect(internalValidators).toHaveBeenCalledTimes(0)
+ })
+ })
+ })
+
+ describe('onBlurValidator', () => {
+ describe('validateInitially', () => {
+ it('should show error message initially', async () => {
+ const onBlurValidator = jest.fn(() => {
+ return new Error('My Error')
+ })
+
+ render(
+
+
+
+ )
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toBeInTheDocument()
+ })
+ expect(onBlurValidator).toHaveBeenCalledTimes(1)
+ })
+
+ it('should show error message initially when onBlurValidator is async', async () => {
+ const onBlurValidator = jest.fn(async () => {
+ return new Error('My Error')
+ })
+
+ render(
+
+
+
+ )
+
+ expect(
+ document.querySelector('.dnb-forms-submit-indicator')
+ ).toHaveClass('dnb-forms-submit-indicator--state-pending')
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toBeInTheDocument()
+ })
+ expect(onBlurValidator).toHaveBeenCalledTimes(1)
+ })
+ })
+
+ describe('connectWithPath', () => {
+ const onBlurValidatorFn: UseFieldProps['onBlurValidator'] = (
+ num,
+ { connectWithPath }
+ ) => {
+ const amount = connectWithPath('/refValue').getValue()
+
+ if (amount >= num) {
+ return new Error(`The amount should be greater than ${amount}`)
+ }
+ }
+
+ it('should show onBlurValidator error on form submit', async () => {
+ const onBlurValidator = jest.fn(onBlurValidatorFn)
+
+ render(
+
+
+
+
+
+ )
+
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+
+ fireEvent.submit(document.querySelector('form'))
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toBeInTheDocument()
+ expect(screen.queryByRole('alert')).toHaveTextContent(
+ 'The amount should be greater than 2'
+ )
+ })
+
+ expect(onBlurValidator).toHaveBeenCalledTimes(1)
+ expect(onBlurValidator).toHaveBeenLastCalledWith(
+ 2,
+ expect.objectContaining({
+ connectWithPath: expect.any(Function),
+ })
+ )
+ })
+
+ it('should update error message on input change', async () => {
+ const onBlurValidator = jest.fn(onBlurValidatorFn)
+
+ render(
+
+
+
+
+
+ )
+
+ const [inputWithRefValue, inputWithOnBlurValidator] = Array.from(
+ document.querySelectorAll('input')
+ )
+
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+
+ // Make a change to the input with the validator
+ await userEvent.type(inputWithOnBlurValidator, '2')
+ fireEvent.blur(inputWithOnBlurValidator)
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toBeInTheDocument()
+ expect(screen.queryByRole('alert')).toHaveTextContent(
+ 'The amount should be greater than 12'
+ )
+ })
+
+ // Make a change to the ref input
+ await userEvent.type(inputWithRefValue, '3')
+
+ expect(screen.queryByRole('alert')).toHaveTextContent(
+ 'The amount should be greater than 123'
+ )
+ })
+
+ it('should hide error message when validation is successful', async () => {
+ const onBlurValidator = jest.fn(onBlurValidatorFn)
+
+ render(
+
+
+
+
+
+ )
+
+ const [inputWithRefValue] = Array.from(
+ document.querySelectorAll('input')
+ )
+
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+
+ // Show error message
+ fireEvent.submit(document.querySelector('form'))
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toBeInTheDocument()
+ })
+
+ await userEvent.type(inputWithRefValue, '{Backspace}')
+
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+ expect(onBlurValidator).toHaveBeenCalledTimes(2)
+ expect(onBlurValidator).toHaveBeenLastCalledWith(
+ 2,
+ expect.objectContaining({
+ connectWithPath: expect.any(Function),
+ })
+ )
+ })
+
+ it('should keep error message hidden during ref input change', async () => {
+ const onBlurValidator = jest.fn(onBlurValidatorFn)
+
+ render(
+
+
+
+
+
+ )
+
+ const [inputWithRefValue] = Array.from(
+ document.querySelectorAll('input')
+ )
+
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+
+ // Show error message
+ fireEvent.submit(document.querySelector('form'))
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toBeInTheDocument()
+ })
+
+ await userEvent.type(inputWithRefValue, '{Backspace}')
+
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+ expect(onBlurValidator).toHaveBeenCalledTimes(2)
+
+ await userEvent.type(inputWithRefValue, '2')
+
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+ })
+
+ describe('validateInitially', () => {
+ it('should show error message initially', async () => {
+ const onBlurValidator = jest.fn(onBlurValidatorFn)
+
+ render(
+
+
+
+
+
+ )
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toBeInTheDocument()
+ })
+ })
+
+ it('should not show error message while typing', async () => {
+ const onBlurValidator = jest.fn(onBlurValidatorFn)
+
+ render(
+
+
+
+
+
+ )
+
+ const [inputWithRefValue] = Array.from(
+ document.querySelectorAll('input')
+ )
+
+ expect(screen.queryByRole('alert')).toBeInTheDocument()
+
+ await userEvent.type(inputWithRefValue, '{Backspace}')
+
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+
+ await userEvent.type(inputWithRefValue, '3')
+
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+ })
+ })
+
+ describe('validateUnchanged', () => {
+ it('should not show error message initially', async () => {
+ const onBlurValidator = jest.fn(onBlurValidatorFn)
+
+ render(
+
+
+
+
+
+ )
+
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+ })
+
+ it('should not show error message while typing', async () => {
+ const onBlurValidator = jest.fn(onBlurValidatorFn)
+
+ render(
+
+
+
+
+
+ )
+
+ const [inputWithRefValue] = Array.from(
+ document.querySelectorAll('input')
+ )
+
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+
+ await userEvent.type(inputWithRefValue, '{Backspace}')
+
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+
+ await userEvent.type(inputWithRefValue, '3')
+
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument()
+ })
+ })
+
+ describe('continuousValidation', () => {
+ it('should show not show error message initially', async () => {
+ const onBlurValidator = jest.fn(onBlurValidatorFn)
+
+ render(
+
+
+
+
diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts
index 0f723046fc9..0cdfbba0333 100644
--- a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts
@@ -58,7 +58,7 @@ const useLayoutEffect =
type SubmitStateWithValidating = SubmitState | 'validating'
type AsyncProcesses =
- | 'validator'
+ | 'onChangeValidator'
| 'onBlurValidator'
| 'onChangeLocal'
| 'onChangeContext'
@@ -121,7 +121,9 @@ export default function useFieldProps(
onBlur,
onChange,
onBlurValidator,
+ // Deprecated – can be removed in v11
validator,
+ onChangeValidator = validator,
exportValidators,
schema,
validateInitially,
@@ -214,6 +216,7 @@ export default function useFieldProps(
fieldBlockContext && fieldBlockContext.disableStatusSummary !== true
)
const {
+ setBlockRecord,
setFieldState: setFieldStateFieldBlock,
showFieldError: showFieldErrorFieldBlock,
mountedFieldsRef: mountedFieldsRefFieldBlock,
@@ -342,10 +345,10 @@ export default function useFieldProps(
dataContextError
)
- const onChangeValidatorRef = useRef(validator)
+ const onChangeValidatorRef = useRef(onChangeValidator)
useUpdateEffect(() => {
- onChangeValidatorRef.current = validator
- }, [validator])
+ onChangeValidatorRef.current = onChangeValidator
+ }, [onChangeValidator]) // Tobias, will this still work? now that we do onChangeValidator = validator?
const onBlurValidatorRef = useRef(onBlurValidator)
useUpdateEffect(() => {
onBlurValidatorRef.current = onBlurValidator
@@ -385,7 +388,7 @@ export default function useFieldProps(
}
const eventPool = useRef({
- validator: null,
+ onChangeValidator: null,
onBlurValidator: null,
onChangeContext: null,
onChangeLocal: null,
@@ -402,13 +405,12 @@ export default function useFieldProps(
const runPool = useCallback(async (cb = null) => {
for (const key in eventPool.current) {
- if (!eventPool.current[key] || eventPool.current[key].pending) {
+ if (!eventPool.current[key]) {
continue
}
const { fn, runAsync } = eventPool.current[key] || {}
if (fn) {
- eventPool.current[key].pending = true
eventPool.current[key] = null
if (runAsync) {
@@ -429,11 +431,18 @@ export default function useFieldProps(
(state: SubmitStateWithValidating) => {
fieldStateRef.current = state
setFieldStateDataContext?.(identifier, resolveValidatingState(state))
+ setFieldStateFieldBlock?.(identifier, resolveValidatingState(state))
+
if (!validateInitially) {
forceUpdate()
}
},
- [setFieldStateDataContext, identifier, validateInitially]
+ [
+ setFieldStateDataContext,
+ identifier,
+ setFieldStateFieldBlock,
+ validateInitially,
+ ]
)
const revealError = useCallback(() => {
@@ -471,17 +480,47 @@ export default function useFieldProps(
showFieldErrorFieldBlock,
])
- const getErrorMessages = useCallback(() => {
+ const errorMessagesCacheRef = useRef({
+ errorMessages: null,
+ extendedErrorMessages: null,
+ })
+ const combinedErrorMessages = useMemo(() => {
+ // Compare the error messages with the previous ones,
+ // in case "errorMessages" is not wrapped in useMemo.
+ const cache = errorMessagesCacheRef.current
+ if (
+ errorMessages &&
+ cache.extendedErrorMessages &&
+ // We compare the "errorMessages" object with the cached version.
+ // Ideally, this comparison would be unnecessary when using useMemo, as documented.
+ // However, to safeguard against potential infinite loops, we perform this comparison.
+ // Why can this happen? Because the "errorMessages" object is a reference, and when provided without useMemo,
+ // it will come in as a new object every time it is used, so combinedErrorMessages as a hook dependency will be updated.
+ // Using array.join('') is approximately twice as fast as concatenating strings in a loop.
+ Object.values(cache.errorMessages || {}).join('') ===
+ Object.values(errorMessages || {}).join('')
+ ) {
+ return cache.extendedErrorMessages
+ }
+
const messages = {
...contextErrorMessages,
...contextErrorMessages?.[identifier],
...errorMessages,
}
- return extendErrorMessagesWithTranslationMessages(
- overwriteErrorMessagesWithGivenAjvKeys(messages),
- translationRef.current
- )
+ const extendedErrorMessages =
+ extendErrorMessagesWithTranslationMessages(
+ overwriteErrorMessagesWithGivenAjvKeys(messages),
+ translationRef.current
+ )
+
+ errorMessagesCacheRef.current = {
+ errorMessages,
+ extendedErrorMessages,
+ }
+
+ return extendedErrorMessages
}, [contextErrorMessages, errorMessages, identifier])
/**
@@ -492,11 +531,10 @@ export default function useFieldProps(
if (error instanceof FormError) {
const prepare = (error: FormError) => {
let message = error.message
- const errorMessages = getErrorMessages()
const { ajvKeyword } = error
if (typeof ajvKeyword === 'string') {
- const ajvMessage = errorMessages?.[ajvKeyword]
+ const ajvMessage = combinedErrorMessages?.[ajvKeyword]
if (ajvMessage) {
message = ajvMessage
}
@@ -505,15 +543,15 @@ export default function useFieldProps(
/** @deprecated – can be removed in v11 */
const { validationRule } = error
if (typeof validationRule === 'string') {
- const ajvMessage = errorMessages?.[validationRule]
+ const ajvMessage = combinedErrorMessages?.[validationRule]
if (ajvMessage) {
message = ajvMessage
}
}
- if (errorMessages[message]) {
+ if (combinedErrorMessages?.[message]) {
// - For when the message is e.g. Field.errorRequired or Custom.key, but delivered in the `errorMessages` object
- message = errorMessages[message]
+ message = combinedErrorMessages?.[message]
if (error.messageValues) {
message = Object.entries(error.messageValues || {}).reduce(
@@ -543,7 +581,7 @@ export default function useFieldProps(
return error as FormError
},
- [getErrorMessages, formatMessage]
+ [combinedErrorMessages, formatMessage]
)
contextErrorRef.current = useMemo(() => {
@@ -581,12 +619,10 @@ export default function useFieldProps(
validateUnchanged ||
continuousValidation
) {
- if (onChangeValidatorRef.current) {
- runOnChangeValidator()
- }
+ runOnChangeValidator()
}
- if (localErrorRef.current && onBlurValidatorRef.current) {
+ if (localErrorRef.current) {
runOnBlurValidator()
}
})
@@ -595,13 +631,11 @@ export default function useFieldProps(
const exportValidatorsRef = useRef(exportValidators)
exportValidatorsRef.current = exportValidators
const additionalArgs = useMemo(() => {
- const errorMessages = getErrorMessages()
-
const args: ValidatorAdditionalArgs = {
/** @deprecated – can be removed in v11 */
- ...errorMessages,
+ ...combinedErrorMessages,
- errorMessages,
+ errorMessages: combinedErrorMessages,
validators: exportValidatorsRef.current,
connectWithPath: (path) => {
setFieldEventListener?.(
@@ -617,7 +651,7 @@ export default function useFieldProps(
}
return args
- }, [getErrorMessages, getValueByPath, setFieldEventListener])
+ }, [combinedErrorMessages, getValueByPath, setFieldEventListener])
const callStackRef = useRef>>([])
const hasBeenCalledRef = useCallback((validator: Validator) => {
@@ -741,7 +775,7 @@ export default function useFieldProps(
setFieldErrorBoundary?.(identifier, error)
// Set the visual states
- setFieldStateFieldBlock?.({
+ setBlockRecord?.({
stateId,
identifier,
type: 'error',
@@ -759,7 +793,7 @@ export default function useFieldProps(
setFieldErrorBoundary,
setFieldErrorDataContext,
setFieldStateDataContext,
- setFieldStateFieldBlock,
+ setBlockRecord,
stateId,
validateInitially,
]
@@ -855,7 +889,7 @@ export default function useFieldProps(
}
if (isAsync(onChangeValidatorRef.current)) {
- defineAsyncProcess('validator')
+ defineAsyncProcess('onChangeValidator')
setFieldState('validating')
hideError()
}
@@ -994,6 +1028,8 @@ export default function useFieldProps(
}
revealOnBlurValidatorResult({ result })
+
+ return { result }
},
[
asyncBehaviorIsEnabled,
@@ -1114,7 +1150,7 @@ export default function useFieldProps(
validateInitially &&
!changedRef.current
) {
- const { result } = await callOnBlurValidator()
+ const { result } = await startOnBlurValidatorProcess()
if (result instanceof Error) {
initiator = 'onBlurValidator'
@@ -1133,7 +1169,6 @@ export default function useFieldProps(
}
}
}, [
- callOnBlurValidator,
clearErrorState,
disabled,
emptyValue,
@@ -1143,6 +1178,7 @@ export default function useFieldProps(
required,
requiredProp,
setFieldState,
+ startOnBlurValidatorProcess,
startOnChangeValidatorValidation,
startProcess,
validateInitially,
@@ -1370,7 +1406,7 @@ export default function useFieldProps(
name: 'onChangeContext',
waitFor: [
{
- processName: 'validator',
+ processName: 'onChangeValidator',
withStates: ['validating', 'error'],
hasValue: valueRef.current,
},
@@ -1454,7 +1490,7 @@ export default function useFieldProps(
}
addToPool(
- 'validator',
+ 'onChangeValidator',
validateValue,
isAsync(onChangeValidatorRef.current)
)
@@ -1541,7 +1577,7 @@ export default function useFieldProps(
name: 'onChangeLocal',
waitFor: [
{
- processName: 'validator',
+ processName: 'onChangeValidator',
withStates: ['validating', 'error'],
hasValue: args[0],
},
@@ -2017,7 +2053,7 @@ export default function useFieldProps(
}
addToPool(
- 'validator',
+ 'onChangeValidator',
startOnChangeValidatorValidation,
isAsync(onChangeValidatorRef.current)
)
@@ -2045,21 +2081,21 @@ export default function useFieldProps(
// Set the error in the field block context if this field is inside a field block
useEffect(() => {
if (inFieldBlock) {
- setFieldStateFieldBlock?.({
+ setBlockRecord?.({
identifier,
type: 'error',
content: errorProp,
showInitially: true,
show: true,
})
- setFieldStateFieldBlock?.({
+ setBlockRecord?.({
identifier,
type: 'warning',
content: warning,
showInitially: true,
show: true,
})
- setFieldStateFieldBlock?.({
+ setBlockRecord?.({
identifier,
type: 'info',
content: info,
@@ -2081,7 +2117,7 @@ export default function useFieldProps(
inFieldBlock,
info,
mountedFieldsRefFieldBlock,
- setFieldStateFieldBlock,
+ setBlockRecord,
warning,
])
diff --git a/packages/dnb-eufemia/src/extensions/forms/stories/FieldAndValueVisibility.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/stories/FieldAndValueVisibility.stories.tsx
index 4597c25dfd1..9069c013da1 100644
--- a/packages/dnb-eufemia/src/extensions/forms/stories/FieldAndValueVisibility.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/stories/FieldAndValueVisibility.stories.tsx
@@ -1,6 +1,6 @@
import React from 'react'
import { Field, Form, Value, Wizard } from '..'
-import { Flex, Card } from '../../../components'
+import { Flex } from '../../../components'
export default {
title: 'Eufemia/Extensions/Forms/FieldAndValueVisibility',
@@ -31,7 +31,7 @@ export const ValueVisibility = () => {
// onSubmit={(data) => console.log('onSubmit', data)}
>
-
+
{count}
@@ -72,7 +72,7 @@ export const ValueVisibility = () => {
-
+
)
}
diff --git a/packages/dnb-eufemia/src/extensions/forms/stories/PizzaDemo.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/stories/PizzaDemo.stories.tsx
index 9fc1acbce44..19d3dec79c8 100644
--- a/packages/dnb-eufemia/src/extensions/forms/stories/PizzaDemo.stories.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/stories/PizzaDemo.stories.tsx
@@ -1,5 +1,4 @@
import React from 'react'
-import { Card } from '../../../components'
import { Field, Form, Wizard, Value, Tools } from '..'
import { Provider } from '../../../../shared'
@@ -38,7 +37,7 @@ export function PizzaDemo() {
Which pizza do you want?
-
+
Your Pizza
-
+
-
+
Allergies
-
+
@@ -76,14 +75,14 @@ export function PizzaDemo() {
Delivery address
-
+
Your name
-
+
-
+
Your address
@@ -105,7 +104,7 @@ export function PizzaDemo() {
postalCode={{ required: true, path: '/postalCode' }}
city={{ required: true, path: '/city' }}
/>
-
+
@@ -113,16 +112,16 @@ export function PizzaDemo() {
Summary
-
+
-
+
-
+
Deliver address
@@ -139,7 +138,7 @@ export function PizzaDemo() {
-
+
diff --git a/packages/dnb-eufemia/src/extensions/forms/types.ts b/packages/dnb-eufemia/src/extensions/forms/types.ts
index b65c5707c4e..3228bb4162a 100644
--- a/packages/dnb-eufemia/src/extensions/forms/types.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/types.ts
@@ -307,7 +307,9 @@ export interface UseFieldProps<
// - Validation
required?: boolean
schema?: AllJSONSchemaVersions
+ /** @deprecated Use `onChangeValidator` instead */
validator?: Validator
+ onChangeValidator?: Validator
onBlurValidator?: Validator
exportValidators?: Record>
validateRequired?: (
@@ -434,6 +436,9 @@ export interface ValueProps
*/
label?: React.ReactNode
+ /** Use `true` to make the label only readable by screen readers. */
+ labelSrOnly?: boolean
+
/**
* Use `true` to inherit the label from a visible (rendered) field with the same path.
*/
diff --git a/packages/dnb-eufemia/src/extensions/payment-card/__tests__/__snapshots__/PaymentCard.test.tsx.snap b/packages/dnb-eufemia/src/extensions/payment-card/__tests__/__snapshots__/PaymentCard.test.tsx.snap
index 1b953ef23f1..5111e15b325 100644
--- a/packages/dnb-eufemia/src/extensions/payment-card/__tests__/__snapshots__/PaymentCard.test.tsx.snap
+++ b/packages/dnb-eufemia/src/extensions/payment-card/__tests__/__snapshots__/PaymentCard.test.tsx.snap
@@ -21,7 +21,7 @@ exports[`PaymentCard scss has to match style dependencies css 1`] = `
*/
:root {
--sb-font-family-default: "Roboto", "Helvetica", "Arial", sans-serif;
- --sb-font-family-headings: "MaisonNeueHeadings", "Roboto", "Helvetica",
+ --sb-font-family-heading: "MaisonNeueHeadings", "Roboto", "Helvetica",
"Arial", sans-serif;
--sb-font-weight-default: normal;
--sb-font-weight-basis: normal;
diff --git a/packages/dnb-eufemia/src/fragments/drawer-list/DrawerList.d.ts b/packages/dnb-eufemia/src/fragments/drawer-list/DrawerList.d.ts
index ee8fbc06e87..6fc74e6756a 100644
--- a/packages/dnb-eufemia/src/fragments/drawer-list/DrawerList.d.ts
+++ b/packages/dnb-eufemia/src/fragments/drawer-list/DrawerList.d.ts
@@ -13,27 +13,35 @@ export type DrawerListWrapperElement =
| React.ReactNode;
export type DrawerListDefaultValue = string | number;
export type DrawerListValue = string | number;
-export type DrawerListDataObject = {
+/** @deprecated use `DrawerListDataArrayObject` */
+export type DrawerListDataObject = DrawerListDataArrayObject;
+export type DrawerListDataArrayObject = {
+ [customProperty: string]: unknown;
selected_value?: string | React.ReactNode;
selectedKey?: string | number;
+ /** @deprecated use `selectedKey` */
selected_key?: string | number;
suffix_value?: string | React.ReactNode;
- content?: string | React.ReactNode | string[];
+ content?: DrawerListContent;
+ disabled?: boolean;
search_content?: string | React.ReactNode | string[];
};
-export type DrawerListDataObjectUnion =
- | string
- | React.ReactNode
- | DrawerListDataObject;
+/** @deprecated use `DrawerListDataArrayItem` */
+export type DrawerListDataObjectUnion = DrawerListDataArrayItem;
+export type DrawerListDataArrayItem =
+ | DrawerListDataArrayObject
+ | DrawerListContent;
+export type DrawerListDataArray = DrawerListDataArrayItem[];
+export type DrawerListDataRecord = Record;
+export type DrawerListDataAll = DrawerListDataRecord | DrawerListDataArray;
export type DrawerListData =
| string
- | ((...args: any[]) => any)
+ | ((...args: any[]) => DrawerListDataAll)
+ | DrawerListDataAll;
+export type DrawerListContent =
+ | string
| React.ReactNode
- | Record
- | DrawerListDataObjectUnion[];
-export type DrawerListSelectedValue = string | React.ReactNode;
-export type DrawerListSuffixValue = string | React.ReactNode;
-export type DrawerListContent = string | React.ReactNode | string[];
+ | (string | React.ReactNode)[];
export type DrawerListRawData =
| any[]
| Record
@@ -108,7 +116,7 @@ export interface DrawerListProps {
*/
default_value?: DrawerListDefaultValue;
/**
- * Define a preselected data entry (index) or key inside an array item. Can be a string or integer.
+ * Define a preselected `data` entry. In order of priority, `value` can be set to: object key (if `data` is an object), `selectedKey` prop (if `data` is an array), array index (if no `selectedKey`) or content (if `value` is a non-integer string).
*/
value?: DrawerListValue;
/**
@@ -146,12 +154,9 @@ export interface DrawerListProps {
skip_keysearch?: boolean;
opened?: boolean;
/**
- * The data we want to fill the list with. Provide the data as a JSON string, array, or object in the specified data structure. If you don't have to define a value, you can also send in a function which will be called once the user opens the DrawerList.
+ * The data we want to fill the list with. The data can be provided as an array or object. Or as a function that returns the data (called when user opens the list).
*/
data?: DrawerListData;
- selected_value?: DrawerListSelectedValue;
- suffix_value?: DrawerListSuffixValue;
- content?: DrawerListContent;
prepared_data?: any[];
raw_data?: DrawerListRawData;
/**
@@ -188,18 +193,8 @@ export type DrawerListOptionsProps = {
export type DrawerListItemProps = {
children: React.ReactNode;
selected: boolean;
- /**
- * Define a preselected data entry (index) or key inside an array item. Can be a string or integer.
- */
value: string;
- on_click: ({
- value
- }: {
- /**
- * Define a preselected data entry (index) or key inside an array item. Can be a string or integer.
- */
- value: string;
- }) => void;
+ on_click: ({ value }: { value: string }) => void;
};
export type DrawerListAllProps = DrawerListProps &
SpacingProps &
diff --git a/packages/dnb-eufemia/src/fragments/drawer-list/DrawerList.js b/packages/dnb-eufemia/src/fragments/drawer-list/DrawerList.js
index b3373c6bc4c..71d46ee73fe 100644
--- a/packages/dnb-eufemia/src/fragments/drawer-list/DrawerList.js
+++ b/packages/dnb-eufemia/src/fragments/drawer-list/DrawerList.js
@@ -310,6 +310,7 @@ class DrawerListInstance extends React.PureComponent {
selected: !dataItem.ignore_events && _id == selected_item,
onClick: this.selectItemHandler,
onKeyDown: this.preventTab,
+ disabled: dataItem.disabled,
}
if (ignoreEvents) {
@@ -468,6 +469,7 @@ DrawerList.Item = React.forwardRef((props, ref) => {
selected, // eslint-disable-line
active, // eslint-disable-line
value, // eslint-disable-line
+ disabled, // eslint-disable-line
...rest
} = props
@@ -481,6 +483,8 @@ DrawerList.Item = React.forwardRef((props, ref) => {
role,
tabIndex: selected ? '0' : '-1',
'aria-selected': active,
+ disabled,
+ 'aria-disabled': disabled,
}
if (selected) {
params['aria-current'] = true // has best support on NVDA
@@ -523,6 +527,7 @@ DrawerList.Item.propTypes = {
selected: PropTypes.bool,
active: PropTypes.bool,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ disabled: PropTypes.bool,
}
DrawerList.Item.defaultProps = {
role: 'option',
@@ -577,6 +582,10 @@ ItemContent.propTypes = {
hash: PropTypes.string,
children: PropTypes.oneOfType([PropTypes.node, PropTypes.object]),
}
+ItemContent.defaultProps = {
+ hash: '',
+ children: undefined,
+}
DrawerList.HorizontalItem = ({ className, ...props }) => (
{DATA}'],
status: 'required',
},
value: {
- doc: 'Define a preselected data entry (index) or key inside an array item. Can be a string or integer.',
+ doc: 'Define a preselected `data` entry. In order of priority, `value` can be set to: object key (if `data` is an object), `selectedKey` prop (if `data` is an array), array index (if no `selectedKey`) or content (if `value` is a non-integer string).',
type: ['string', 'number'],
status: 'optional',
},
@@ -165,7 +165,7 @@ export const DrawerListEvents: PropertiesTableProps = {
status: 'optional',
},
on_select: {
- doc: 'Will be called once the user selects an item by a click or keyboard navigation.',
+ doc: 'Will be called once the user focuses or selects an item by a click or keyboard navigation.',
type: 'function',
status: 'optional',
},
@@ -180,3 +180,36 @@ export const DrawerListEvents: PropertiesTableProps = {
status: 'optional',
},
}
+
+export const DrawerListItem: PropertiesTableProps = {
+ content: {
+ doc: 'Visual content in the list item',
+ type: ['string', 'React.node', '(string | React.Node)[]'],
+ status: 'optional',
+ },
+ disabled: {
+ doc: 'Disables the list item from selection',
+ type: 'boolean',
+ status: 'optional',
+ },
+ selectedKey: {
+ doc: 'If set, can be used instead of array index by the `value` prop',
+ type: ['string', 'number'],
+ status: 'optional',
+ },
+ selected_value: {
+ doc: 'Replaces the standard value output for selected item. Only used in some implementations (Dropdown, Autocomplete).',
+ type: ['string', 'React.Node'],
+ status: 'optional',
+ },
+ suffix_value: {
+ doc: 'Content placed to the right in the list item.',
+ type: ['string', 'React.node'],
+ status: 'optional',
+ },
+ selected_key: {
+ doc: 'Use prop `selectedKey` instead',
+ type: ['string', 'number'],
+ status: 'deprecated',
+ },
+}
diff --git a/packages/dnb-eufemia/src/fragments/drawer-list/DrawerListHelpers.js b/packages/dnb-eufemia/src/fragments/drawer-list/DrawerListHelpers.js
index 613b26edae2..561cb400076 100644
--- a/packages/dnb-eufemia/src/fragments/drawer-list/DrawerListHelpers.js
+++ b/packages/dnb-eufemia/src/fragments/drawer-list/DrawerListHelpers.js
@@ -77,6 +77,7 @@ export const drawerListPropTypes = {
PropTypes.string,
PropTypes.number,
]),
+ /** @deprecated use `selectedKey` */
selected_key: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
@@ -89,6 +90,7 @@ export const drawerListPropTypes = {
PropTypes.string,
PropTypes.node,
]),
+ disabled: PropTypes.bool,
content: PropTypes.oneOfType([
PropTypes.string,
PropTypes.node,
@@ -290,6 +292,7 @@ export const normalizeData = (props) => {
const list = []
for (const key in data) {
list.push({
+ selectedKey: key,
selected_key: key,
value: key,
content: data[key],
@@ -327,13 +330,16 @@ export const getCurrentIndex = (value, data) => {
return data?.findIndex((cur) => parseCurrentValue(cur) === value)
}
// 2. if "selectedKey" is given in data, we now handle it as a value, and not an index.
- else if (selectedKeyExists()) {
- return data?.findIndex(
+ if (selectedKeyExists()) {
+ const index = data?.findIndex(
(cur) => String(parseCurrentValue(cur)) === String(value)
)
+ if (index > -1) {
+ return index
+ }
}
- // 3. if is numeric, handle it as a index.
- else if (!isNaN(parseFloat(value))) {
+ // 3. if is numeric, and no matching "selectedKey", handle it as a index.
+ if (!isNaN(parseFloat(value))) {
return value
}
@@ -346,6 +352,7 @@ export const getCurrentIndex = (value, data) => {
}
if (
typeof data[i]?.selectedKey !== 'undefined' ||
+ typeof data[i]?.selected_key !== 'undefined' ||
data[i]?.type === 'object'
) {
return true
@@ -398,7 +405,7 @@ export const getCurrentData = (item_index, data) => {
data = (data && data.find(({ __id }) => __id == item_index)) || null
if (data && data.__isTransformed) {
- data = parseCurrentValue(data)
+ return data.content
}
return data
diff --git a/packages/dnb-eufemia/src/fragments/drawer-list/DrawerListProvider.js b/packages/dnb-eufemia/src/fragments/drawer-list/DrawerListProvider.js
index cec84651da6..9bc1005ab4e 100644
--- a/packages/dnb-eufemia/src/fragments/drawer-list/DrawerListProvider.js
+++ b/packages/dnb-eufemia/src/fragments/drawer-list/DrawerListProvider.js
@@ -1243,6 +1243,10 @@ export default class DrawerListProvider extends React.PureComponent {
attributes,
}
+ if (data?.disabled) {
+ return false
+ }
+
const res = dispatchCustomElementEvent(
this.state,
'on_pre_change',
diff --git a/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/DrawerList.screenshot.test.ts b/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/DrawerList.screenshot.test.ts
index 2159f3a077d..7b031251a95 100644
--- a/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/DrawerList.screenshot.test.ts
+++ b/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/DrawerList.screenshot.test.ts
@@ -24,16 +24,14 @@ describe.each(['ui', 'sbanken'])('DrawerList for %s', (themeName) => {
expect(screenshot).toMatchImageSnapshot()
})
- if (themeName === 'sbanken') {
- it('have to match the sbanken drawer-list', async () => {
- const screenshot = await makeScreenshot({
- style: {
- width: '14rem',
- },
- selector:
- '[data-visual-test="drawer-list"] .dnb-drawer-list__list',
- })
- expect(screenshot).toMatchImageSnapshot()
+ it('have to match the disabled option', async () => {
+ const screenshot = await makeScreenshot({
+ style: {
+ width: '14rem',
+ 'padding-top': '3rem',
+ },
+ selector: '[data-visual-test="drawer-list-disabled"]',
})
- }
+ expect(screenshot).toMatchImageSnapshot()
+ })
})
diff --git a/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/DrawerList.test.tsx b/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/DrawerList.test.tsx
index 2ff704cfec1..78ad040941a 100644
--- a/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/DrawerList.test.tsx
+++ b/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/DrawerList.test.tsx
@@ -5,10 +5,16 @@
import React from 'react'
import { axeComponent, loadScss } from '../../../core/jest/jestSetup'
-import { act, render, screen, waitFor } from '@testing-library/react'
+import {
+ act,
+ render,
+ screen,
+ fireEvent,
+ waitFor,
+} from '@testing-library/react'
import DrawerList, {
DrawerListAllProps,
- DrawerListDataObjectUnion,
+ DrawerListDataArray,
DrawerListData,
} from '../DrawerList'
@@ -32,7 +38,7 @@ const props: DrawerListAllProps = {
no_animation: true,
}
-const mockData: DrawerListDataObjectUnion[] = [
+const mockData: DrawerListDataArray = [
{
content: ['1234 56 78901', 'Brukskonto - Kari Nordmann'],
},
@@ -81,6 +87,92 @@ describe('DrawerList component', () => {
).toBeInTheDocument()
})
+ describe('with disabled option', () => {
+ const disabledOptionProps = {
+ skip_portal: true,
+ opened: true,
+ no_animation: true,
+ data: [
+ { content: 'item 1' },
+ { disabled: true, content: 'item 2' },
+ { content: 'item 3' },
+ ],
+ }
+
+ it('has correct attributes', async () => {
+ render( )
+
+ const options = document.querySelectorAll('.dnb-drawer-list__option')
+ expect(options[1].getAttribute('disabled')).toEqual('')
+ expect(options[1].getAttribute('aria-disabled')).toEqual('true')
+ })
+
+ it('sends on_select events', async () => {
+ const on_select = jest.fn()
+
+ render( )
+
+ keydown(40) // down
+ await waitFor(() => {
+ expect(on_select).toHaveBeenCalledTimes(1)
+ expect(on_select.mock.calls[0][0].active_item).toBe(0)
+ })
+
+ keydown(40) // down
+ await waitFor(() => {
+ // on_select is called when navigating to disabled item
+ expect(on_select).toHaveBeenCalledTimes(2)
+ expect(on_select.mock.calls[1][0].active_item).toBe(1)
+ expect(on_select.mock.calls[1][0].data.disabled).toBe(true)
+ })
+
+ keydown(40) // down
+ await waitFor(() => {
+ // navigates to next item
+ expect(on_select).toHaveBeenCalledTimes(3)
+ expect(on_select.mock.calls[2][0].active_item).toBe(2)
+ })
+ })
+
+ it('can not be clicked', async () => {
+ const on_change = jest.fn()
+ const on_select = jest.fn()
+
+ render(
+
+ )
+
+ keydown(40) // down
+ keydown(40) // down
+ await waitFor(() => {
+ // verify item is disabled
+ expect(on_select).toHaveBeenCalledTimes(2)
+ expect(on_select.mock.calls[1][0].active_item).toBe(1)
+ expect(on_select.mock.calls[1][0].data.disabled).toBe(true)
+ })
+
+ keydown(13) // enter
+ await waitFor(() => {
+ // on_change and on_select is not called when attempting to chose a disabled item
+ expect(on_change).toHaveBeenCalledTimes(0)
+ expect(on_select).toHaveBeenCalledTimes(2)
+ })
+
+ await fireEvent.click(
+ document.querySelectorAll('.dnb-drawer-list__option')[1]
+ )
+ await waitFor(() => {
+ // on_change and on_select is not called when attempting to click a disabled item
+ expect(on_change).toHaveBeenCalledTimes(0)
+ expect(on_select).toHaveBeenCalledTimes(2)
+ })
+ })
+ })
+
it('handles default_value correctly on forcing re-render', () => {
const { rerender } = render(
{
const { rerender } = render(
)
@@ -204,7 +296,7 @@ describe('DrawerList component', () => {
rerender(
@@ -214,7 +306,7 @@ describe('DrawerList component', () => {
rerender(
diff --git a/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__image_snapshots__/drawerlist-for-sbanken-have-to-match-the-default-drawer-list.snap.png b/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__image_snapshots__/drawerlist-for-sbanken-have-to-match-the-default-drawer-list.snap.png
index c82c6740ebb..5a51cd430d5 100644
Binary files a/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__image_snapshots__/drawerlist-for-sbanken-have-to-match-the-default-drawer-list.snap.png and b/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__image_snapshots__/drawerlist-for-sbanken-have-to-match-the-default-drawer-list.snap.png differ
diff --git a/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__image_snapshots__/drawerlist-for-sbanken-have-to-match-the-disabled-option.snap.png b/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__image_snapshots__/drawerlist-for-sbanken-have-to-match-the-disabled-option.snap.png
new file mode 100644
index 00000000000..8257ee8ac1b
Binary files /dev/null and b/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__image_snapshots__/drawerlist-for-sbanken-have-to-match-the-disabled-option.snap.png differ
diff --git a/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__image_snapshots__/drawerlist-for-sbanken-have-to-match-the-sbanken-drawer-list.snap.png b/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__image_snapshots__/drawerlist-for-sbanken-have-to-match-the-sbanken-drawer-list.snap.png
deleted file mode 100644
index c82c6740ebb..00000000000
Binary files a/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__image_snapshots__/drawerlist-for-sbanken-have-to-match-the-sbanken-drawer-list.snap.png and /dev/null differ
diff --git a/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__image_snapshots__/drawerlist-for-ui-have-to-match-the-disabled-option.snap.png b/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__image_snapshots__/drawerlist-for-ui-have-to-match-the-disabled-option.snap.png
new file mode 100644
index 00000000000..3f7766011e0
Binary files /dev/null and b/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__image_snapshots__/drawerlist-for-ui-have-to-match-the-disabled-option.snap.png differ
diff --git a/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__image_snapshots__/drawerlist-have-to-match-the-default-drawer-list.snap.png b/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__image_snapshots__/drawerlist-have-to-match-the-default-drawer-list.snap.png
deleted file mode 100644
index e44f0190b8d..00000000000
Binary files a/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__image_snapshots__/drawerlist-have-to-match-the-default-drawer-list.snap.png and /dev/null differ
diff --git a/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__snapshots__/DrawerList.test.tsx.snap b/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__snapshots__/DrawerList.test.tsx.snap
index fe3fea65d45..f02b3068b3c 100644
--- a/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__snapshots__/DrawerList.test.tsx.snap
+++ b/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__snapshots__/DrawerList.test.tsx.snap
@@ -30,6 +30,8 @@ exports[`DrawerList scss has to match style dependencies css 1`] = `
--drawer-list-option-inner-background: var(--color-white);
--drawer-list-list-background: var(--color-white);
--drawer-list-list-line-height: var(--line-height-basis);
+ --drawer-list-option-disabled-background: var(--color-white);
+ --drawer-list-option-disabled-color: var(--color-black-20);
display: block;
position: relative;
width: inherit;
@@ -186,6 +188,13 @@ html[data-visual-test] .dnb-drawer-list--scroll .dnb-drawer-list__options, .dnb-
cursor: default;
pointer-events: none;
}
+.dnb-drawer-list__option[disabled] {
+ --drawer-list-option-inner-background: var(
+ --drawer-list-option-disabled-background
+ );
+ color: var(--drawer-list-option-disabled-color);
+ cursor: not-allowed;
+}
.dnb-drawer-list__triangle {
position: absolute;
top: calc(var(--drawer-list-focus-border-width) - var(--drawer-list-height) / 2);
diff --git a/packages/dnb-eufemia/src/fragments/drawer-list/style/dnb-drawer-list.scss b/packages/dnb-eufemia/src/fragments/drawer-list/style/dnb-drawer-list.scss
index 5aa73d34308..5a922d242d5 100644
--- a/packages/dnb-eufemia/src/fragments/drawer-list/style/dnb-drawer-list.scss
+++ b/packages/dnb-eufemia/src/fragments/drawer-list/style/dnb-drawer-list.scss
@@ -57,6 +57,9 @@
--drawer-list-option-inner-background: var(--color-white);
--drawer-list-list-background: var(--color-white);
--drawer-list-list-line-height: var(--line-height-basis);
+ // Disabled option
+ --drawer-list-option-disabled-background: var(--color-white);
+ --drawer-list-option-disabled-color: var(--color-black-20);
display: block; // has to be block element so we can se the content
position: relative;
@@ -243,6 +246,14 @@
cursor: default;
pointer-events: none;
}
+
+ &[disabled] {
+ --drawer-list-option-inner-background: var(
+ --drawer-list-option-disabled-background
+ );
+ color: var(--drawer-list-option-disabled-color);
+ cursor: not-allowed;
+ }
}
// arrow
diff --git a/packages/dnb-eufemia/src/fragments/drawer-list/style/themes/dnb-drawer-list-theme-sbanken.scss b/packages/dnb-eufemia/src/fragments/drawer-list/style/themes/dnb-drawer-list-theme-sbanken.scss
index a836fcf119c..4ae5ccc30d2 100644
--- a/packages/dnb-eufemia/src/fragments/drawer-list/style/themes/dnb-drawer-list-theme-sbanken.scss
+++ b/packages/dnb-eufemia/src/fragments/drawer-list/style/themes/dnb-drawer-list-theme-sbanken.scss
@@ -13,9 +13,14 @@
--drawer-list-options-border-radius: 0 0 0.5rem 0.5rem;
--drawer-list-options-border-radius-reversed: 0.5rem 0.5rem 0 0;
--drawer-list-option-border-width: 0.125rem;
+ --drawer-list-option-border-color: transparent;
--drawer-list-option-inner-border-display: none;
--drawer-list-option-inner-background: transparent;
--drawer-list-list-background: var(--sb-color-white);
+ --drawer-list-list-option-background: transparent;
+ // Disabled option
+ --drawer-list-option-disabled-background: var(--sb-color-gray-light);
+ --drawer-list-option-disabled-color: var(--sb-color-gray-dark);
&__list {
margin-top: calc(
@@ -31,14 +36,14 @@
&__option__inner {
overflow: visible;
- background-color: inherit;
padding: 1rem 0.75rem;
+ margin: 0 var(--drawer-list-option-border-width);
&::before {
content: '';
display: block;
position: absolute;
- bottom: -3px;
+ top: -1px;
left: 0.5rem;
width: calc(100% - 1rem);
height: 1px;
@@ -56,12 +61,9 @@
}
&__option {
- border: var(--drawer-list-option-border-width) solid
- var(--sb-color-gray-dark-2);
- border-bottom-color: transparent;
- border-top-color: transparent;
- border-right-color: transparent; // Fix ugly border with scrollbar
- background-color: transparent;
+ box-shadow: inset 0 0 0 var(--drawer-list-option-border-width)
+ var(--drawer-list-option-border-color);
+ background: var(--drawer-list-list-option-background);
z-index: 0;
&__item.item-nr-1 {
@@ -69,7 +71,7 @@
}
@include hover() {
- border-color: var(--sb-color-violet);
+ --drawer-list-option-border-color: var(--sb-color-violet);
z-index: 1;
.dnb-drawer-list__option__inner::before {
@@ -78,7 +80,7 @@
}
@include active() {
- border-color: var(--sb-color-violet);
+ --drawer-list-option-border-color: var(--sb-color-violet);
z-index: 1;
.dnb-drawer-list__option__inner::before {
@@ -87,7 +89,7 @@
}
&--selected {
- background-color: var(--sb-color-violet);
+ --drawer-list-list-option-background: var(--sb-color-violet);
color: var(--sb-color-white);
border-right-color: var(
--sb-color-gray-dark-2
@@ -122,28 +124,33 @@
@include allAbove(small) {
&--selected &__suffix {
z-index: 2; // over check icon
- background-color: inherit; // to "hide" the check icon
+ background-color: var(
+ --drawer-list-list-option-background
+ ); // to "hide" the check icon
}
}
+ &.first-of-type &__inner::before {
+ content: none;
+ }
+
&.last-of-type {
border-radius: var(--drawer-list-options-border-radius);
-
- .dnb-drawer-list__option__inner::before {
- display: none;
- }
}
&:focus-visible,
&--focus {
- border-color: var(--sb-color-blue-dark);
- outline: 0.0625rem solid var(--sb-color-blue-dark);
- background-color: var(--sb-color-blue-light-3);
+ --drawer-list-option-border-color: var(--sb-color-blue-dark);
+ --drawer-list-list-option-background: var(--sb-color-blue-light-3);
color: var(--sb-color-blue-dark);
font-weight: var(--sb-font-weight-medium);
z-index: 1;
/* stylelint-disable no-descending-specificity */
+ .dnb-drawer-list__option__inner {
+ --drawer-list-option-inner-background: transparent;
+ }
+
.dnb-drawer-list__option__inner::before {
display: var(--drawer-list-option-inner-border-display);
}
diff --git a/packages/dnb-eufemia/src/shared/VisibilityByTheme.tsx b/packages/dnb-eufemia/src/shared/VisibilityByTheme.tsx
index b65a1643106..26fc9451402 100644
--- a/packages/dnb-eufemia/src/shared/VisibilityByTheme.tsx
+++ b/packages/dnb-eufemia/src/shared/VisibilityByTheme.tsx
@@ -61,3 +61,16 @@ export default function VisibilityByTheme({
})
}
}
+
+VisibilityByTheme.Name = function ThemeName() {
+ const theme = useTheme()
+ if (theme.isEiendom) {
+ return 'Eiendom'
+ }
+ if (theme.isSbanken) {
+ return 'Sbanken'
+ }
+ if (theme.isUi) {
+ return 'DNB'
+ }
+}
diff --git a/packages/dnb-eufemia/src/style/elements/__tests__/__snapshots__/Elements.test.js.snap b/packages/dnb-eufemia/src/style/elements/__tests__/__snapshots__/Elements.test.js.snap
index f2364b55c58..05c84747c72 100644
--- a/packages/dnb-eufemia/src/style/elements/__tests__/__snapshots__/Elements.test.js.snap
+++ b/packages/dnb-eufemia/src/style/elements/__tests__/__snapshots__/Elements.test.js.snap
@@ -610,7 +610,6 @@ del .dnb-code {
*
*/
.dnb-lead, .dnb-h--xx-large, .dnb-h--x-large, .dnb-h--large, .dnb-h--medium, .dnb-h--basis, .dnb-h--small, .dnb-h--x-small, .dnb-core-style .dnb-lead, .dnb-core-style .dnb-h--xx-large, .dnb-core-style .dnb-h--x-large, .dnb-core-style .dnb-h--large, .dnb-core-style .dnb-h--medium, .dnb-core-style .dnb-h--basis, .dnb-core-style .dnb-h--small, .dnb-core-style .dnb-h--x-small, h1, h2, h3, h4, h5, h6, p, b, small, strong, .dnb-p, .dnb-small, .dnb-table, sub, sup {
- --typography-h-default-font-family: var(--font-family-default);
--typography-h-default-font-weight: var(--font-weight-medium);
--typography-h-xx-large-font-size: var(--font-size-xx-large);
--typography-h-xx-large-line-height: var(--line-height-xx-large);
@@ -672,7 +671,7 @@ del .dnb-code {
.dnb-core-style .dnb-h--small,
.dnb-core-style .dnb-h--x-small {
padding: 0;
- font-family: var(--typography-h-default-font-family);
+ font-family: var(--font-family-heading);
}
.dnb-lead:not([class*=dnb-space]),
.dnb-h--xx-large:not([class*=dnb-space]),
@@ -793,13 +792,13 @@ sub {
font-size: var(--typography-lead-small-font-size);
line-height: var(--typography-lead-small-line-height);
}
-.dnb-p--medium {
- font-weight: var(--font-weight-medium);
-}
.dnb-p b,
.dnb-p strong {
font-weight: var(--font-weight-medium);
}
+.dnb-p--medium {
+ font-weight: var(--font-weight-medium);
+}
.dnb-p--bold {
font-weight: var(--font-weight-bold);
}
@@ -823,7 +822,7 @@ sub {
font-size: var(--font-size-medium);
line-height: var(--line-height-medium);
}
-.dnb-p--small, .dnb-p__size--small, .dnb-p > small {
+.dnb-p--small, .dnb-p__size--small {
font-size: var(--font-size-small);
line-height: var(--line-height-small);
}
@@ -831,12 +830,97 @@ sub {
font-size: var(--font-size-x-small);
line-height: var(--line-height-x-small);
}
+.dnb-p > small {
+ font-size: var(--font-size-small);
+ line-height: var(--line-height-small);
+}
.dnb-p > cite {
font-weight: var(--font-weight-medium);
line-height: var(--line-height-basis);
font-style: italic;
}
+/*
+ * Typography
+ * Universal set of helper classes that do not have a specific element.
+ * The class ".dnb-t" does nothing, only it's modifiers ".dnb-t__[***]" do.
+ */
+.dnb-t__size--xx-large {
+ font-size: var(--font-size-xx-large);
+}
+.dnb-t__size--x-large {
+ font-size: var(--font-size-x-large);
+}
+.dnb-t__size--large {
+ font-size: var(--font-size-large);
+}
+.dnb-t__size--medium {
+ font-size: var(--font-size-medium);
+}
+.dnb-t__size--basis {
+ font-size: var(--font-size-basis);
+}
+.dnb-t__size--small {
+ font-size: var(--font-size-small);
+}
+.dnb-t__size--x-small {
+ font-size: var(--font-size-x-small);
+}
+.dnb-t__line-height--xx-large {
+ line-height: var(--line-height-xx-large);
+}
+.dnb-t__line-height--x-large {
+ line-height: var(--line-height-x-large);
+}
+.dnb-t__line-height--large {
+ line-height: var(--line-height-large);
+}
+.dnb-t__line-height--medium {
+ line-height: var(--line-height-medium);
+}
+.dnb-t__line-height--basis {
+ line-height: var(--line-height-basis);
+}
+.dnb-t__line-height--small {
+ line-height: var(--line-height-small);
+}
+.dnb-t__line-height--x-small {
+ line-height: var(--line-height-x-small);
+}
+.dnb-t__weight--regular {
+ font-weight: var(--font-weight-regular);
+}
+.dnb-t__weight--medium {
+ font-weight: var(--font-weight-medium);
+}
+.dnb-t__weight--bold {
+ font-weight: var(--font-weight-bold);
+}
+.dnb-t__align--center {
+ text-align: center;
+}
+.dnb-t__align--left {
+ text-align: left;
+}
+.dnb-t__align--right {
+ text-align: right;
+}
+.dnb-t__family--default {
+ font-family: var(--font-family-default);
+}
+.dnb-t__family--heading {
+ font-family: var(--font-family-heading);
+}
+.dnb-t__family--monospace {
+ font-family: var(--font-family-monospace);
+}
+.dnb-t__decoration--underline {
+ text-decoration: underline;
+}
+.dnb-t__slant--italic {
+ font-style: italic;
+}
+
.dnb-table b,
.dnb-table strong {
font-weight: var(--font-weight-medium);
diff --git a/packages/dnb-eufemia/src/style/themes/theme-eiendom/properties.js b/packages/dnb-eufemia/src/style/themes/theme-eiendom/properties.js
index b91cec0012b..3daa8ce876d 100644
--- a/packages/dnb-eufemia/src/style/themes/theme-eiendom/properties.js
+++ b/packages/dnb-eufemia/src/style/themes/theme-eiendom/properties.js
@@ -2,7 +2,7 @@
export default {
'--sb-font-family-default': '"Roboto", "Helvetica", "Arial", sans-serif',
- '--sb-font-family-headings': '"MaisonNeueHeadings", "Roboto", "Helvetica",',
+ '--sb-font-family-heading': '"MaisonNeueHeadings", "Roboto", "Helvetica",',
'--sb-font-weight-default': 'normal',
'--sb-font-weight-basis': 'normal',
'--sb-font-weight-regular': 'normal',
@@ -94,6 +94,7 @@ export default {
'--color-emerald-green-25': '#c4d4d6',
'--color-emerald-green-10': '#e8eeef',
'--font-family-default': '"DNB", sans-serif',
+ '--font-family-heading': 'var(--font-family-default)',
'--font-family-monospace': '"DNBMono", "Menlo", "Consolas", "Roboto Mono",',
'--font-weight-default': 'normal',
'--font-weight-basis': 'normal',
diff --git a/packages/dnb-eufemia/src/style/themes/theme-sbanken/fonts.scss b/packages/dnb-eufemia/src/style/themes/theme-sbanken/fonts.scss
index 5f474789e9f..fdfe031565d 100644
--- a/packages/dnb-eufemia/src/style/themes/theme-sbanken/fonts.scss
+++ b/packages/dnb-eufemia/src/style/themes/theme-sbanken/fonts.scss
@@ -13,14 +13,18 @@
font-style: normal;
}
-// For backwards compatibility
-.dnb-typo-medium,
-.dnb-typo-bold {
+.dnb-typo-medium {
font-family: var(--sb-font-family-default);
font-weight: var(--sb-font-weight-medium);
font-style: normal;
}
+.dnb-typo-bold {
+ font-family: var(--sb-font-family-default);
+ font-weight: var(--sb-font-weight-bold);
+ font-style: normal;
+}
+
$fonts-path: '../../../../assets/fonts/sbanken' !default;
// Maison
diff --git a/packages/dnb-eufemia/src/style/themes/theme-sbanken/properties.js b/packages/dnb-eufemia/src/style/themes/theme-sbanken/properties.js
index e0ba26fd86d..459b5543cb9 100644
--- a/packages/dnb-eufemia/src/style/themes/theme-sbanken/properties.js
+++ b/packages/dnb-eufemia/src/style/themes/theme-sbanken/properties.js
@@ -2,7 +2,7 @@
export default {
'--sb-font-family-default': '"Roboto", "Helvetica", "Arial", sans-serif',
- '--sb-font-family-headings': '"MaisonNeueHeadings", "Roboto", "Helvetica",',
+ '--sb-font-family-heading': '"MaisonNeueHeadings", "Roboto", "Helvetica",',
'--sb-font-weight-default': 'normal',
'--sb-font-weight-basis': 'normal',
'--sb-font-weight-regular': 'normal',
@@ -94,6 +94,7 @@ export default {
'--color-emerald-green-25': '#c4d4d6',
'--color-emerald-green-10': '#e8eeef',
'--font-family-default': 'var(--sb-font-family-default)',
+ '--font-family-heading': 'var(--sb-font-family-heading)',
'--font-family-monospace': '"DNBMono", "Menlo", "Consolas", "Roboto Mono",',
'--font-weight-default': 'normal',
'--font-weight-basis': 'normal',
diff --git a/packages/dnb-eufemia/src/style/themes/theme-sbanken/properties.scss b/packages/dnb-eufemia/src/style/themes/theme-sbanken/properties.scss
index ce17072a595..88509776686 100644
--- a/packages/dnb-eufemia/src/style/themes/theme-sbanken/properties.scss
+++ b/packages/dnb-eufemia/src/style/themes/theme-sbanken/properties.scss
@@ -6,7 +6,7 @@
:root {
// Typography Family
--sb-font-family-default: 'Roboto', 'Helvetica', 'Arial', sans-serif;
- --sb-font-family-headings: 'MaisonNeueHeadings', 'Roboto', 'Helvetica',
+ --sb-font-family-heading: 'MaisonNeueHeadings', 'Roboto', 'Helvetica',
'Arial', sans-serif;
// Typography Weights
diff --git a/packages/dnb-eufemia/src/style/themes/theme-sbanken/theme-mapping.scss b/packages/dnb-eufemia/src/style/themes/theme-sbanken/theme-mapping.scss
index bf40d28faa0..6acaf9f92d0 100644
--- a/packages/dnb-eufemia/src/style/themes/theme-sbanken/theme-mapping.scss
+++ b/packages/dnb-eufemia/src/style/themes/theme-sbanken/theme-mapping.scss
@@ -12,6 +12,7 @@
// font-family
--font-family-default: var(--sb-font-family-default);
+ --font-family-heading: var(--sb-font-family-heading);
// font-weight
--font-weight-medium: var(--sb-font-weight-medium);
diff --git a/packages/dnb-eufemia/src/style/themes/theme-ui/properties.js b/packages/dnb-eufemia/src/style/themes/theme-ui/properties.js
index b91cec0012b..3daa8ce876d 100644
--- a/packages/dnb-eufemia/src/style/themes/theme-ui/properties.js
+++ b/packages/dnb-eufemia/src/style/themes/theme-ui/properties.js
@@ -2,7 +2,7 @@
export default {
'--sb-font-family-default': '"Roboto", "Helvetica", "Arial", sans-serif',
- '--sb-font-family-headings': '"MaisonNeueHeadings", "Roboto", "Helvetica",',
+ '--sb-font-family-heading': '"MaisonNeueHeadings", "Roboto", "Helvetica",',
'--sb-font-weight-default': 'normal',
'--sb-font-weight-basis': 'normal',
'--sb-font-weight-regular': 'normal',
@@ -94,6 +94,7 @@ export default {
'--color-emerald-green-25': '#c4d4d6',
'--color-emerald-green-10': '#e8eeef',
'--font-family-default': '"DNB", sans-serif',
+ '--font-family-heading': 'var(--font-family-default)',
'--font-family-monospace': '"DNBMono", "Menlo", "Consolas", "Roboto Mono",',
'--font-weight-default': 'normal',
'--font-weight-basis': 'normal',
diff --git a/packages/dnb-eufemia/src/style/themes/theme-ui/properties.scss b/packages/dnb-eufemia/src/style/themes/theme-ui/properties.scss
index 79aeb2eb6a3..df1ae185498 100644
--- a/packages/dnb-eufemia/src/style/themes/theme-ui/properties.scss
+++ b/packages/dnb-eufemia/src/style/themes/theme-ui/properties.scss
@@ -6,6 +6,7 @@
:root {
// Typography Family
--font-family-default: 'DNB', sans-serif;
+ --font-family-heading: var(--font-family-default);
--font-family-monospace: 'DNBMono', 'Menlo', 'Consolas', 'Roboto Mono',
'Ubuntu Monospace', 'Noto Mono', 'Oxygen Mono', 'Liberation Mono',
monospace;