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 7d4e405ba51..e906026a322 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 @@ -10,9 +10,9 @@ export const Default = () => { ) } -export const OverStack = () => { +export const PrecedingFlexContainer = () => { return ( - + This is a main heading

Stack contents

@@ -21,11 +21,29 @@ export const OverStack = () => { ) } -export const OverStackWithCard = () => { +export const AboveCard = () => { return ( - + This is a main heading + +

Card contents

+
+
+ ) +} + +export const WithHelpButton = () => { + return ( + + + 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 01bef1cf209..d193006f0e3 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 @@ -10,10 +10,18 @@ import * as Examples from './Examples' -### Over Stack +### Above a flex container - + -### Over Stack with Card +### Above Card - +When placed above a [Card](/uilib/components/card/) component, the heading will be indented to align with the card content. + +On small screens, the indention will be removed. + + + +### With HelpButton + + 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 a433c2e6a8d..a4a1d409121 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 @@ -19,7 +19,7 @@ export const BelowMainHeading = () => { ) } -export const OverStack = () => { +export const PrecedingFlexContainer = () => { return ( This is a sub heading @@ -43,15 +43,13 @@ export const InsideCard = () => { ) } -export const OverStackWithCard = () => { +export const AboveCard = () => { return ( - + This is a sub heading - - -

Card contents

-
-
+ +

Card contents

+
) } @@ -65,3 +63,23 @@ export const TwoSubHeadings = () => {
) } + +export const WithHelpButton = () => { + return ( + + + + 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 6b9cf3638e0..2d02eed90d1 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 @@ -14,18 +14,26 @@ import * as Examples from './Examples' -### Over Stack +### Above a flex container - + ### Inside Card -### Over Stack with Card +### Above Card - +When placed above a [Card](/uilib/components/card/) component, the heading will be indented to align with the card content. + +On small screens, the indention will be removed. + + ### Two sub headings + +### With HelpButton + + 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 98a06785a81..acdfe6e543d 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,8 +8,11 @@ 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. + ```jsx import { Field } from '@dnb/eufemia/extensions/forms' + render( @@ -17,3 +20,22 @@ render( , ) ``` + +You can also use the `dataPath` property to provide the data to the component: + +```jsx +import { Field } from '@dnb/eufemia/extensions/forms' + +render( + + + , +) +``` diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Composition/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Composition/Examples.tsx index e27c7165bb8..a62307ab097 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Composition/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/base-fields/Composition/Examples.tsx @@ -17,7 +17,10 @@ export const CompositionError = () => ( width="large" > - +
) 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 22938272571..f32b1919b91 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 @@ -2,11 +2,9 @@ showTabs: true --- +import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable' +import { OptionProperties } from '@dnb/eufemia/src/extensions/forms/Field/Option/OptionDocs' + ## Properties -| Property | Type | Description | -| ---------- | -------------------- | --------------------------------------------- | -| `value` | `string` or `number` | _(optional)_ Value for this option. | -| `title` | `string` | _(optional)_ Text title for the option. | -| `text` | `string` | _(optional)_ Secondary text. | -| `children` | `React.Node` | _(optional)_ Optional way to provide `title`. | + 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 dfc8467efd5..67d12b390b8 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 @@ -6,12 +6,13 @@ showTabs: true `Field.Selection` is a component for selecting between options using a dropdown or similar user experiences. -[Field.Option](/uilib/extensions/forms/base-fields/Option/) is a related component. - 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. + ```tsx import { Field } from '@dnb/eufemia/extensions/forms' + render( @@ -20,6 +21,25 @@ render( ) ``` +You can also use the `dataPath` property to provide the data to the component: + +```jsx +import { Field } from '@dnb/eufemia/extensions/forms' + +render( + + + , +) +``` + ## About the Autocomplete variant The autocomplete variant (`variant="autocomplete"`) is a special easy drop-in version – basically as an replacement for the Dropdown variant, but with a search capability. 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 efb82f739dc..8e88a94beef 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 { Flex, Slider } from '@dnb/eufemia/src' +import { Anchor, Card, Flex, Slider } from '@dnb/eufemia/src' export const Default = () => { return ( @@ -227,3 +227,212 @@ export const CombineErrorMessages = () => {
) } + +export const InlineHelpButtonVerticalLabel = () => { + return ( + + + + Dette er hvor mye du har tenkt å låne{' '} + totalt. + + ), + }} + onChange={async () => { + await new Promise((resolve) => setTimeout(resolve, 1000)) + }} + /> + + + + ) +} + +export const InlineHelpButtonLabelDescription = () => { + return ( + + + { + await new Promise((resolve) => setTimeout(resolve, 1000)) + }} + /> + + + + ) +} + +export const InlineHelpButtonHTML = () => { + return ( + + + Ønsket lånebeløp} + labelDescription={ + +
+ Label description with a Anchor +
+ } + help={{ + open: true, + title: Help title, + content: ( + <> + Help content with a Anchor. + + ), + }} + onChange={async () => { + await new Promise((resolve) => setTimeout(resolve, 1000)) + }} + /> +
+
+ ) +} + +export const InlineHelpButtonVerticalLabelDescription = () => { + return ( + + + { + await new Promise((resolve) => setTimeout(resolve, 1000)) + }} + /> + + + + ) +} + +export const InlineHelpButtonHorizontalLabel = () => { + return ( + + + { + await new Promise((resolve) => setTimeout(resolve, 1000)) + }} + /> + + + + + ) +} + +export const InlineHelpButtonCompositionFields = () => { + return ( + + + + + + + + + + + ) +} diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/create-component/FieldBlock/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/create-component/FieldBlock/demos.mdx index b097fcc8fc6..eac89f63974 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/create-component/FieldBlock/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/create-component/FieldBlock/demos.mdx @@ -34,12 +34,36 @@ import * as Examples from './Examples' -### Widths - - - ### Combine error messages Error messages from all fields inside the FieldBlock are combined as one message below the whole block + +### Inline help button (vertical only) + + + +### Inline help button (with label description) + + + +### Inline help button (vertical label description) + + + +### Inline help button (horizontal label) + + + +### Inline help button (composition fields) + + + +### Inline help button with HTML + + + +### Widths + + diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PostalCodeAndCity/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PostalCodeAndCity/properties.mdx index 2f6f1b63ac4..c139c48cca1 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PostalCodeAndCity/properties.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PostalCodeAndCity/properties.mdx @@ -15,7 +15,10 @@ import { PostalCodeAndCityProperties } from '@dnb/eufemia/src/extensions/forms/F ### General properties - + ## Translations diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Slider/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Slider/Examples.tsx index 7a2e5ff4600..a2e996a85ec 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Slider/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Slider/Examples.tsx @@ -145,3 +145,25 @@ export const PathValues = () => {
) } + +export const WithHelp = () => { + return ( + + + + + + ) +} diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Slider/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Slider/demos.mdx index 9d0d7c498a6..69f2a8e43c8 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Slider/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/more-fields/Slider/demos.mdx @@ -25,3 +25,7 @@ import * as Examples from './Examples' ### Path usage for min, max and step + +### With help + + diff --git a/packages/dnb-eufemia/src/components/button/__tests__/__snapshots__/Button.test.tsx.snap b/packages/dnb-eufemia/src/components/button/__tests__/__snapshots__/Button.test.tsx.snap index 0d1fe94fd10..44536daf93e 100644 --- a/packages/dnb-eufemia/src/components/button/__tests__/__snapshots__/Button.test.tsx.snap +++ b/packages/dnb-eufemia/src/components/button/__tests__/__snapshots__/Button.test.tsx.snap @@ -567,6 +567,8 @@ exports[`Button scss has to match theme css for sbanken 1`] = ` * Utilities */ /* + * Utilities + */ /* * Button mixins * */ @@ -574,6 +576,8 @@ exports[`Button scss has to match theme css for sbanken 1`] = ` * Utilities */ /* + * Utilities + */ /* * Button mixins * */ @@ -999,6 +1003,8 @@ exports[`Button scss has to match theme css for ui 1`] = ` * Utilities */ /* + * Utilities + */ /* * Button mixins * */ @@ -1006,6 +1012,8 @@ exports[`Button scss has to match theme css for ui 1`] = ` * Utilities */ /* + * Utilities + */ /* * Button mixins * */ diff --git a/packages/dnb-eufemia/src/components/button/style/themes/button-mixins.scss b/packages/dnb-eufemia/src/components/button/style/themes/button-mixins.scss index fa4b8953f2a..e82ff9445b3 100644 --- a/packages/dnb-eufemia/src/components/button/style/themes/button-mixins.scss +++ b/packages/dnb-eufemia/src/components/button/style/themes/button-mixins.scss @@ -1,3 +1,5 @@ +@import '../../../../style/core/utilities.scss'; + /* * Button mixins * diff --git a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-nested-section.snap.png b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-nested-section.snap.png index 976f9b612f7..789c8900a05 100644 Binary files a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-nested-section.snap.png and b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-for-ui-have-to-match-nested-section.snap.png differ diff --git a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-ui-have-to-match-nested-section.snap.png b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-ui-have-to-match-nested-section.snap.png index 20d28de0a95..4ace84d0cb3 100644 Binary files a/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-ui-have-to-match-nested-section.snap.png and b/packages/dnb-eufemia/src/components/card/__tests__/__image_snapshots__/card-small-screen-for-ui-have-to-match-nested-section.snap.png differ 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 49d9720e8f0..5627d916408 100644 --- a/packages/dnb-eufemia/src/components/card/style/dnb-card.scss +++ b/packages/dnb-eufemia/src/components/card/style/dnb-card.scss @@ -119,3 +119,20 @@ --outline-width: 0.125rem; } } + +.dnb-card--auto-indent:has( + + .dnb-card, + + * + .dnb-card,/* e.g. one paragraph */ + + * + * + .dnb-card,/* e.g. two paragraphs */ + + .dnb-help-button__content + .dnb-section + .dnb-card +) { + &: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/card/style/themes/dnb-card-theme-ui.scss b/packages/dnb-eufemia/src/components/card/style/themes/dnb-card-theme-ui.scss index c8905d31d58..91d22efaced 100644 --- a/packages/dnb-eufemia/src/components/card/style/themes/dnb-card-theme-ui.scss +++ b/packages/dnb-eufemia/src/components/card/style/themes/dnb-card-theme-ui.scss @@ -1,8 +1,10 @@ -.dnb-card { +:root { --card-outline-color: var(--border-color, var(--color-lavender)); --card-outline-width: 0.25rem; --card-background-color: var(--color-white); +} +.dnb-card { // Nested Cards & .dnb-card { --card-outline-color: var(--color-black-8); 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 0cda407e68f..7bd9ba47689 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 @@ -581,9 +581,112 @@ button.dnb-button::-moz-focus-inner { /* * HelpButton component * - * TODO: Set spacing in the theme file, as theme comes later in the stack + */ +/* + * Utilities + */ /* +* Button mixins +* +*/ +/* + * HelpButton component * */ +/* + * Utilities + */ /* +* Button mixins +* +*/ +.dnb-help-button.dnb-help-button__inline--open { + --border-color: var(--color-emerald-green); + --border-width: 0.125rem; + box-shadow: 0 0 0 var(--border-width) var(--border-color); + border-color: transparent; +} + +.dnb-help-button__inline svg { + will-change: transform; +} +.dnb-help-button__inline svg:nth-of-type(2) { + position: absolute; + inset: 0; + margin: auto; + opacity: 0; +} +.dnb-help-button__inline--open svg:nth-of-type(1) { + opacity: 0; +} +.dnb-help-button__inline--open svg:nth-of-type(2) { + animation: rotate-icon-in 400ms var(--easing-default) forwards; +} +.dnb-help-button__inline:not(.dnb-help-button__inline--open).dnb-help-button__inline--was-open svg:nth-of-type(1) { + opacity: 0; + animation: animate-question 400ms var(--easing-default) 200ms forwards; +} +.dnb-help-button__inline:not(.dnb-help-button__inline--open).dnb-help-button__inline--was-open svg:nth-of-type(2) { + animation: rotate-icon-out 400ms var(--easing-default) forwards; +} +.dnb-help-button__inline:not(.dnb-help-button__inline--user-intent) svg, html[data-visual-test] .dnb-help-button__inline svg { + animation-duration: 0ms; +} +@keyframes rotate-icon-in { + from { + opacity: 0; + transform: rotate(0deg); + } + to { + opacity: 1; + transform: rotate(90deg); + } +} +@keyframes rotate-icon-out { + 0% { + opacity: 1; + transform: rotate(90deg); + } + 30% { + opacity: 1; + } + 100% { + opacity: 0; + transform: rotate(0deg); + } +} +@keyframes animate-question { + from { + opacity: 0; + transform: rotate(10deg); + } + to { + opacity: 1; + transform: rotate(0deg); + } +} + +.dnb-help-button__content { + --help-button-indent-width: var(--card-outline-width); +} +.dnb-help-button__content .dnb-section { + --background-color: var(--help-button-content-background); + --rounded-corner--value: calc(var(--card-outline-width) + 0.5rem); +} +.dnb-help-button__content .dnb-section .dnb-p { + max-width: 60ch; + transition: transform 400ms var(--easing-default) 40ms; + 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; +} +.dnb-help-button__content.dnb-height-animation--parallax .dnb-section .dnb-p { + transform: translate3d(0, 0, 0); +} + .dnb-help-button .dnb-button__bounding { transform: scale(1.5); } 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 b4ea4a99c8d..6f087ae70f5 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 @@ -582,9 +582,112 @@ button.dnb-button::-moz-focus-inner { /* * HelpButton component * - * TODO: Set spacing in the theme file, as theme comes later in the stack + */ +/* + * Utilities + */ /* +* Button mixins +* +*/ +/* + * HelpButton component * */ +/* + * Utilities + */ /* +* Button mixins +* +*/ +.dnb-help-button.dnb-help-button__inline--open { + --border-color: var(--color-emerald-green); + --border-width: 0.125rem; + box-shadow: 0 0 0 var(--border-width) var(--border-color); + border-color: transparent; +} + +.dnb-help-button__inline svg { + will-change: transform; +} +.dnb-help-button__inline svg:nth-of-type(2) { + position: absolute; + inset: 0; + margin: auto; + opacity: 0; +} +.dnb-help-button__inline--open svg:nth-of-type(1) { + opacity: 0; +} +.dnb-help-button__inline--open svg:nth-of-type(2) { + animation: rotate-icon-in 400ms var(--easing-default) forwards; +} +.dnb-help-button__inline:not(.dnb-help-button__inline--open).dnb-help-button__inline--was-open svg:nth-of-type(1) { + opacity: 0; + animation: animate-question 400ms var(--easing-default) 200ms forwards; +} +.dnb-help-button__inline:not(.dnb-help-button__inline--open).dnb-help-button__inline--was-open svg:nth-of-type(2) { + animation: rotate-icon-out 400ms var(--easing-default) forwards; +} +.dnb-help-button__inline:not(.dnb-help-button__inline--user-intent) svg, html[data-visual-test] .dnb-help-button__inline svg { + animation-duration: 0ms; +} +@keyframes rotate-icon-in { + from { + opacity: 0; + transform: rotate(0deg); + } + to { + opacity: 1; + transform: rotate(90deg); + } +} +@keyframes rotate-icon-out { + 0% { + opacity: 1; + transform: rotate(90deg); + } + 30% { + opacity: 1; + } + 100% { + opacity: 0; + transform: rotate(0deg); + } +} +@keyframes animate-question { + from { + opacity: 0; + transform: rotate(10deg); + } + to { + opacity: 1; + transform: rotate(0deg); + } +} + +.dnb-help-button__content { + --help-button-indent-width: var(--card-outline-width); +} +.dnb-help-button__content .dnb-section { + --background-color: var(--help-button-content-background); + --rounded-corner--value: calc(var(--card-outline-width) + 0.5rem); +} +.dnb-help-button__content .dnb-section .dnb-p { + max-width: 60ch; + transition: transform 400ms var(--easing-default) 40ms; + 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; +} +.dnb-help-button__content.dnb-height-animation--parallax .dnb-section .dnb-p { + transform: translate3d(0, 0, 0); +} + .dnb-help-button .dnb-button__bounding { transform: scale(1.5); } diff --git a/packages/dnb-eufemia/src/components/flex/__tests__/Container.test.tsx b/packages/dnb-eufemia/src/components/flex/__tests__/Container.test.tsx index 8fd81fb3626..e614807d107 100644 --- a/packages/dnb-eufemia/src/components/flex/__tests__/Container.test.tsx +++ b/packages/dnb-eufemia/src/components/flex/__tests__/Container.test.tsx @@ -819,7 +819,7 @@ describe('Flex.Container', () => { class="dnb-space dnb-flex-container dnb-flex-container--direction-vertical dnb-flex-container--justify-flex-start dnb-flex-container--align-flex-start dnb-flex-container--spacing-small dnb-flex-container--wrap dnb-flex-container--divider-space" >

Heading

@@ -886,7 +886,7 @@ describe('Flex.Container', () => { class="dnb-space dnb-flex-container dnb-flex-container--direction-vertical dnb-flex-container--justify-flex-start dnb-flex-container--align-flex-start dnb-flex-container--spacing-small dnb-flex-container--wrap dnb-flex-container--divider-space" >

Heading

diff --git a/packages/dnb-eufemia/src/components/flex/__tests__/__image_snapshots__/flexstack-have-to-match-flex-stack-card-two-headings.snap.png b/packages/dnb-eufemia/src/components/flex/__tests__/__image_snapshots__/flexstack-have-to-match-flex-stack-card-two-headings.snap.png index 721dff5afa1..d22d4c28744 100644 Binary files a/packages/dnb-eufemia/src/components/flex/__tests__/__image_snapshots__/flexstack-have-to-match-flex-stack-card-two-headings.snap.png and b/packages/dnb-eufemia/src/components/flex/__tests__/__image_snapshots__/flexstack-have-to-match-flex-stack-card-two-headings.snap.png differ diff --git a/packages/dnb-eufemia/src/components/form-label/FormLabel.tsx b/packages/dnb-eufemia/src/components/form-label/FormLabel.tsx index 763589e6224..9b8374598c0 100644 --- a/packages/dnb-eufemia/src/components/form-label/FormLabel.tsx +++ b/packages/dnb-eufemia/src/components/form-label/FormLabel.tsx @@ -140,16 +140,46 @@ export default function FormLabel(localProps: FormLabelAllProps) { forElem?.closest('.dnb-input__border') if (target) { - const enter = () => target.classList.add('hover') - const leave = () => target.classList.remove('hover') - const elem = ref.current + + const buttonEnter = () => { + target.classList.add('no-hover') + leave() + } + const buttonLeave = () => { + target.classList.remove('no-hover') + enter() + } + + const enter = () => { + target.classList.add('hover') + + // Remove the style from interactive elements (e.g. HelpButton) + const button = elem.querySelector('button') + button?.addEventListener?.('mouseenter', buttonEnter, { + once: true, + }) + button?.addEventListener?.('mouseleave', buttonLeave, { + once: true, + }) + } + const leave = () => { + target.classList.remove('hover') + + elem + .querySelector('button') + ?.removeEventListener?.('mouseenter', buttonEnter) + } + elem?.addEventListener?.('mouseenter', enter) elem?.addEventListener?.('mouseleave', leave) return () => { elem?.removeEventListener?.('mouseenter', enter) elem?.removeEventListener?.('mouseleave', leave) + elem + .querySelector('button') + ?.removeEventListener?.('mouseleave', buttonLeave) } } }, [forId, ref]) diff --git a/packages/dnb-eufemia/src/components/form-label/__tests__/FormLabel.test.tsx b/packages/dnb-eufemia/src/components/form-label/__tests__/FormLabel.test.tsx index 451793043bc..9aa0c5ade92 100644 --- a/packages/dnb-eufemia/src/components/form-label/__tests__/FormLabel.test.tsx +++ b/packages/dnb-eufemia/src/components/form-label/__tests__/FormLabel.test.tsx @@ -5,7 +5,7 @@ import React from 'react' import { axeComponent, loadScss } from '../../../core/jest/jestSetup' -import { render } from '@testing-library/react' +import { fireEvent, render } from '@testing-library/react' import FormLabel from '../FormLabel' import Input from '../../input/Input' import { Provider } from '../../../shared' @@ -306,6 +306,101 @@ describe('FormLabel component', () => { ) expect(await axeComponent(Comp)).toHaveNoViolations() }) + + describe('hover handling', () => { + it('should set hover class when hovered', () => { + render( + <> + + + + ) + + const label = document.querySelector('label') + const input = document.querySelector('input') + expect(input).not.toHaveClass('hover') + + fireEvent.mouseEnter(label) + expect(input).toHaveClass('hover') + + fireEvent.mouseLeave(label) + expect(input).not.toHaveClass('hover') + }) + + it('should not set hover class when "dnb-input__border" is not present', () => { + render( + <> + + + + ) + + const label = document.querySelector('label') + const input = document.querySelector('input') + expect(input).not.toHaveClass('hover') + + fireEvent.mouseEnter(label) + expect(input).not.toHaveClass('hover') + }) + + it('should remove hover class when hovering on a button inside the label', () => { + render( + <> + + + + + + ) + + const label = document.querySelector('label') + const input = document.querySelector('input') + const button = document.querySelector('button') + expect(input).not.toHaveClass('hover') + + fireEvent.mouseEnter(label) + expect(input).toHaveClass('hover') + + fireEvent.mouseEnter(button) + expect(input).not.toHaveClass('hover') + + fireEvent.mouseLeave(button) + expect(input).toHaveClass('hover') + + fireEvent.mouseLeave(label) + expect(input).not.toHaveClass('hover') + }) + + it('should remove events from label and button when unmounting', () => { + const { unmount } = render( + <> + + + + + + ) + + const label = document.querySelector('label') + const input = document.querySelector('input') + const button = document.querySelector('button') + expect(input).not.toHaveClass('hover') + + fireEvent.mouseEnter(label) + expect(input).toHaveClass('hover') + + fireEvent.mouseEnter(button) + expect(input).not.toHaveClass('hover') + + unmount() + + // When emitting these events, "hover" would normally be set. + fireEvent.mouseEnter(label) + expect(input).not.toHaveClass('hover') + fireEvent.mouseLeave(button) + expect(input).not.toHaveClass('hover') + }) + }) }) describe('FormLabel scss', () => { diff --git a/packages/dnb-eufemia/src/components/help-button/HelpButtonInline.tsx b/packages/dnb-eufemia/src/components/help-button/HelpButtonInline.tsx new file mode 100644 index 00000000000..cfdebcdcd73 --- /dev/null +++ b/packages/dnb-eufemia/src/components/help-button/HelpButtonInline.tsx @@ -0,0 +1,225 @@ +import React, { useCallback, useContext, useEffect, useRef } from 'react' +import classnames from 'classnames' +import { HelpButtonProps } from './HelpButton' +import HelpButtonInstance from './HelpButtonInstance' +import HeightAnimation from '../HeightAnimation' +import { useSharedState } from '../../shared/helpers/useSharedState' +import { convertJsxToString } from '../../shared/component-helper' +import useId from '../../shared/helpers/useId' +import Section from '../Section' +import { P } from '../../elements' +import Flex from '../Flex' +import CardContext from '../card/CardContext' +import { SpacingProps } from '../space/types' +import Dialog from '../Dialog' +import { question as QuestionIcon, close as CloseIcon } from '../../icons' + +export type HelpProps = { + title?: React.ReactNode + content?: React.ReactNode + renderAs?: 'inline' | 'dialog' + /** Only for the "inline" variant */ + open?: boolean + /** Only for the "inline" variant */ + breakout?: boolean +} + +export type HelpButtonInlineProps = HelpButtonProps & { + contentId?: string + help?: HelpProps +} + +export type HelpButtonInlineSharedStateDataProps = { + isOpen: boolean + isUserIntent?: boolean + buttonRef?: React.RefObject +} + +export default function HelpButtonInline(props: HelpButtonInlineProps) { + const { contentId, size, help, className, children, ...rest } = props + const controlId = useId(contentId) + + const { data, update } = + useSharedState(controlId, { + isOpen: help?.open ?? false, + }) + const { isOpen, isUserIntent } = data || {} + const wasOpenRef = useRef(undefined) + const buttonRef = useRef(null) + + const onClickHandler = useCallback( + ({ event }: { event: React.MouseEvent }) => { + event.preventDefault() // Because when used inside a FormLabel + update({ isOpen: !isOpen, isUserIntent: !isOpen, buttonRef }) + wasOpenRef.current = !isOpen + }, + [isOpen, update] + ) + + return ( + <> + + + {!contentId && ( + + {children} + + )} + + ) +} + +export type HelpButtonInlineContentProps = SpacingProps & { + contentId: string + className?: string + children?: React.ReactNode + help?: HelpProps + breakout?: boolean +} + +export function HelpButtonInlineContent( + props: HelpButtonInlineContentProps +) { + const { + contentId, + className, + children, + help: helpProp, + breakout = true, + ...rest + } = props + const { data, update } = + useSharedState(contentId) + const { isOpen, isUserIntent, buttonRef } = data || {} + const { + open, + title, + content, + renderAs, + breakout: breakoutProp = true, + } = helpProp || {} + + const innerRef = useRef(null) + const cardContext = useContext(CardContext) + const breakoutFromLayout = + Boolean(cardContext) && breakout && breakoutProp + + useEffect(() => { + if (isOpen && isUserIntent) { + window.requestAnimationFrame(() => { + innerRef.current?.focus({ preventScroll: true }) + }) + } + }, [isOpen, isUserIntent]) + + const onClose = useCallback(() => { + update({ isOpen: false, isUserIntent: false }) + }, [update]) + + const onKeyDown = useCallback( + (event: React.KeyboardEvent) => { + if (event.currentTarget === event.target) { + // Firefox returns a whitespace (" ") on event.key when pressing space, + // therefore using .trim() can help normalize this. + // While userEvent.keyboard('{Space}') might return 'Unknown' on event.code, + // making a direct comparison less reliable across all platforms. + switch (event.key.trim() || event.code) { + case 'Enter': + case 'Space': + case 'Escape': + event.preventDefault() + window.requestAnimationFrame(() => { + onClose() + buttonRef.current?.focus() + }) + break + } + } + }, + [buttonRef, onClose] + ) + + if (renderAs === 'dialog') { + return ( + + {content} + {children} + + ) + } + + return ( + +
+ + {title &&

{title}

} + {content &&

{content}

} +
+ {children} +
+
+ ) +} + +function HelpButtonIcon() { + return ( + <> + + + + ) +} + +HelpButtonInline._supportsSpacingProps = true +HelpButtonInlineContent._supportsSpacingProps = true diff --git a/packages/dnb-eufemia/src/components/help-button/__tests__/HelpButtonInline.test.tsx b/packages/dnb-eufemia/src/components/help-button/__tests__/HelpButtonInline.test.tsx new file mode 100644 index 00000000000..f94dcb1bb09 --- /dev/null +++ b/packages/dnb-eufemia/src/components/help-button/__tests__/HelpButtonInline.test.tsx @@ -0,0 +1,305 @@ +import React from 'react' +import { render, waitFor } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import HelpButtonInline, { + HelpButtonInlineContent, +} from '../HelpButtonInline' + +describe('HelpButtonInline', () => { + it('should render without title and content', () => { + render() + expect( + document.querySelector('.dnb-help-button__content') + ).toBeInTheDocument() + }) + + it('should toggle open state when clicked', async () => { + render() + + const button = document.querySelector('button') + + await userEvent.click(button) + expect(button).toHaveClass('dnb-help-button__inline--open') + + await userEvent.click(button) + expect(button).not.toHaveClass('dnb-help-button__inline--open') + }) + + it('should toggle open state when Space key gets pressed', async () => { + render() + + expect(document.body).toHaveFocus() + + await userEvent.tab() + expect(document.querySelector('button')).toHaveFocus() + + await userEvent.type(document.querySelector('button'), '{Space}') + await waitFor(() => { + expect(document.querySelector('section')).toHaveFocus() + }) + + await userEvent.keyboard('{Space}') + await waitFor(() => { + expect(document.querySelector('button')).toHaveFocus() + }) + }) + + it('should toggle open state when Enter key gets pressed', async () => { + render() + + expect(document.body).toHaveFocus() + + await userEvent.tab() + expect(document.querySelector('button')).toHaveFocus() + + await userEvent.keyboard('{Enter}') + await waitFor(() => { + expect(document.querySelector('section')).toHaveFocus() + }) + + await userEvent.keyboard('{Enter}') + await waitFor(() => { + expect(document.querySelector('button')).toHaveFocus() + }) + }) + + it('should set focus on the button when closing with Escape key', async () => { + render() + + expect(document.body).toHaveFocus() + + await userEvent.tab() + expect(document.querySelector('button')).toHaveFocus() + + await userEvent.keyboard('{Enter}') + await waitFor(() => { + expect(document.querySelector('section')).toHaveFocus() + }) + + await userEvent.keyboard('{Escape}') + await waitFor(() => { + expect(document.querySelector('button')).toHaveFocus() + }) + }) + + it('should close when Escape key on the button gets pressed', async () => { + render() + + const button = document.querySelector('button') + + await userEvent.type(button, '{Space}') + expect(button).toHaveClass('dnb-help-button__inline--open') + + await userEvent.type(button, '{Escape}') + expect(button).not.toHaveClass('dnb-help-button__inline--open') + }) + + it('should display title when open', async () => { + render() + + const button = document.querySelector('button') + expect( + document.querySelector('.dnb-help-button__content') + ).not.toBeInTheDocument() + + await userEvent.click(button) + expect( + document.querySelector('.dnb-help-button__content') + ).toHaveTextContent('Help title') + }) + + it('should respect size props', () => { + render() + + const button = document.querySelector('button') + expect(button).toHaveClass('dnb-button--icon-size-medium') + }) + + it('should not render content when contentId was given', () => { + render( + + ) + + expect( + document.querySelector('.dnb-help-button__content .dnb-section') + ).not.toBeInTheDocument() + }) + + it('should have aria-controls attribute', () => { + render( + <> + + + + ) + + const button = document.querySelector('button') + expect(button).toHaveAttribute('aria-controls', 'unique-content') + expect( + document.querySelector('.dnb-help-button__content .dnb-section') + ).toHaveAttribute('id', 'unique-content') + }) + + it('should have aria-label attribute', () => { + render() + + expect( + document.querySelector('.dnb-help-button__content .dnb-section') + ).toHaveAttribute('aria-label', 'Help title') + }) + + it('should have aria-label attribute when title is HTML', () => { + render( + Help title, + }} + /> + ) + + expect( + document.querySelector('.dnb-help-button__content .dnb-section') + ).toHaveAttribute('aria-label', 'Help title') + }) + + it('should support HTML as title and content', () => { + render( + Help title, + content: Some content, + }} + /> + ) + + expect( + document.querySelector('.dnb-help-button__content .dnb-section') + .innerHTML + ).toContain(`Help title`) + + expect( + document.querySelector('.dnb-help-button__content .dnb-section') + .innerHTML + ).toContain(`Some content`) + }) + + it('should render dialog when renderAs is "dialog"', async () => { + render( + + ) + + await userEvent.click(document.querySelector('button')) + expect(document.querySelector('.dnb-dialog')).toBeInTheDocument() + expect( + document.querySelector('.dnb-help-button__content') + ).not.toBeInTheDocument() + }) + + it('should set focus on the content when open', async () => { + render() + + expect(document.body).toHaveFocus() + + await userEvent.click(document.querySelector('button')) + await waitFor(() => { + expect( + document.querySelector('.dnb-help-button__content .dnb-section') + ).toHaveFocus() + }) + + await userEvent.click(document.querySelector('button')) + expect(document.querySelector('button')).toHaveFocus() + + await userEvent.click(document.querySelector('button')) + await waitFor(() => { + const section = document.querySelector( + '.dnb-help-button__content .dnb-section' + ) + expect(section).toHaveFocus() + expect(section).toHaveClass('dnb-no-focus') + }) + }) + + it('should not set focus on the content when open is true', async () => { + render() + + expect(document.body).toHaveFocus() + + await userEvent.click(document.querySelector('button')) + expect(document.querySelector('button')).toHaveFocus() + + await userEvent.click(document.querySelector('button')) + await waitFor(() => { + expect( + document.querySelector('.dnb-help-button__content .dnb-section') + ).toHaveFocus() + }) + }) +}) + +describe('HelpButtonInlineContent Component', () => { + it('should render content when open', () => { + render( + + ) + + expect( + document.querySelector('.dnb-help-button__content') + ).toBeInTheDocument() + }) + + it('should have tabindex with -1', () => { + render( + + ) + + expect( + document.querySelector('.dnb-help-button__content .dnb-section') + ).toHaveAttribute('tabindex', '-1') + }) + + it('should not render content when closed', () => { + render() + + expect( + document.querySelector('.dnb-help-button__content') + ).not.toBeInTheDocument() + }) + + it('should respect spacing props', () => { + render( + + ) + + expect(document.querySelector('.dnb-section')).toHaveClass( + 'dnb-space__top--large' + ) + }) +}) 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 dc31ef8413c..787af0cfc6e 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 @@ -567,9 +567,112 @@ button.dnb-button::-moz-focus-inner { /* * HelpButton component * - * TODO: Set spacing in the theme file, as theme comes later in the stack + */ +/* + * Utilities + */ /* +* Button mixins +* +*/ +/* + * HelpButton component * */ +/* + * Utilities + */ /* +* Button mixins +* +*/ +.dnb-help-button.dnb-help-button__inline--open { + --border-color: var(--color-emerald-green); + --border-width: 0.125rem; + box-shadow: 0 0 0 var(--border-width) var(--border-color); + border-color: transparent; +} + +.dnb-help-button__inline svg { + will-change: transform; +} +.dnb-help-button__inline svg:nth-of-type(2) { + position: absolute; + inset: 0; + margin: auto; + opacity: 0; +} +.dnb-help-button__inline--open svg:nth-of-type(1) { + opacity: 0; +} +.dnb-help-button__inline--open svg:nth-of-type(2) { + animation: rotate-icon-in 400ms var(--easing-default) forwards; +} +.dnb-help-button__inline:not(.dnb-help-button__inline--open).dnb-help-button__inline--was-open svg:nth-of-type(1) { + opacity: 0; + animation: animate-question 400ms var(--easing-default) 200ms forwards; +} +.dnb-help-button__inline:not(.dnb-help-button__inline--open).dnb-help-button__inline--was-open svg:nth-of-type(2) { + animation: rotate-icon-out 400ms var(--easing-default) forwards; +} +.dnb-help-button__inline:not(.dnb-help-button__inline--user-intent) svg, html[data-visual-test] .dnb-help-button__inline svg { + animation-duration: 0ms; +} +@keyframes rotate-icon-in { + from { + opacity: 0; + transform: rotate(0deg); + } + to { + opacity: 1; + transform: rotate(90deg); + } +} +@keyframes rotate-icon-out { + 0% { + opacity: 1; + transform: rotate(90deg); + } + 30% { + opacity: 1; + } + 100% { + opacity: 0; + transform: rotate(0deg); + } +} +@keyframes animate-question { + from { + opacity: 0; + transform: rotate(10deg); + } + to { + opacity: 1; + transform: rotate(0deg); + } +} + +.dnb-help-button__content { + --help-button-indent-width: var(--card-outline-width); +} +.dnb-help-button__content .dnb-section { + --background-color: var(--help-button-content-background); + --rounded-corner--value: calc(var(--card-outline-width) + 0.5rem); +} +.dnb-help-button__content .dnb-section .dnb-p { + max-width: 60ch; + transition: transform 400ms var(--easing-default) 40ms; + 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; +} +.dnb-help-button__content.dnb-height-animation--parallax .dnb-section .dnb-p { + transform: translate3d(0, 0, 0); +} + .dnb-help-button .dnb-button__bounding { transform: scale(1.5); }" @@ -577,7 +680,10 @@ button.dnb-button::-moz-focus-inner { exports[`HelpButton scss have to match default theme snapshot 1`] = ` "/* - * Np theme is provided + * Help button theme for UI * - */" + */ +.dnb-help-button__content { + --help-button-content-background: var(--color-lavender); +}" `; 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 new file mode 100644 index 00000000000..89353661fc5 --- /dev/null +++ b/packages/dnb-eufemia/src/components/help-button/style/dnb-help-button-inline.scss @@ -0,0 +1,128 @@ +/* + * HelpButton component + * + */ + +@import '../../button/style/themes/button-mixins.scss'; + +.dnb-help-button { + &.dnb-help-button__inline { + &--open { + @include buttonHoverStyle(null, null, var(--color-emerald-green)); + } + } +} + +.dnb-help-button__inline { + svg { + // To avoid the animations from jumping in Safari + will-change: transform; + } + + svg:nth-of-type(2) { + position: absolute; + inset: 0; + margin: auto; + opacity: 0; + } + + &--open { + svg:nth-of-type(1) { + opacity: 0; + } + svg:nth-of-type(2) { + animation: rotate-icon-in 400ms var(--easing-default) forwards; + } + } + + &:not(#{&}--open)#{&}--was-open { + svg:nth-of-type(1) { + opacity: 0; + animation: animate-question 400ms var(--easing-default) 200ms + forwards; + } + svg:nth-of-type(2) { + animation: rotate-icon-out 400ms var(--easing-default) forwards; + } + } + + &:not(#{&}--user-intent), + html[data-visual-test] & { + svg { + animation-duration: 0ms; + } + } + + @keyframes rotate-icon-in { + from { + opacity: 0; + transform: rotate(0deg); + } + to { + opacity: 1; + transform: rotate(90deg); + } + } + + @keyframes rotate-icon-out { + 0% { + opacity: 1; + transform: rotate(90deg); + } + 30% { + opacity: 1; + } + 100% { + opacity: 0; + transform: rotate(0deg); + } + } + + @keyframes animate-question { + from { + opacity: 0; + transform: rotate(10deg); + } + to { + opacity: 1; + transform: rotate(0deg); + } + } +} + +.dnb-help-button__content { + .dnb-section { + --background-color: var(--help-button-content-background); + + // Because we don't use/need an outline, we need to add to get a wider corner radius. + --rounded-corner--value: calc(var(--card-outline-width) + 0.5rem); + + .dnb-p { + max-width: 60ch; // to enhance readability; + + transition: transform 400ms var(--easing-default) 40ms; + transform: translate3d(0, -0.5rem, 0); + } + } + + // Defines the negative margin (extra border) to align on UX design. + --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; + } + + &.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.scss b/packages/dnb-eufemia/src/components/help-button/style/dnb-help-button.scss index ebb469e2b44..11037d95618 100644 --- a/packages/dnb-eufemia/src/components/help-button/style/dnb-help-button.scss +++ b/packages/dnb-eufemia/src/components/help-button/style/dnb-help-button.scss @@ -1,10 +1,11 @@ /* * HelpButton component * - * TODO: Set spacing in the theme file, as theme comes later in the stack - * */ +@import '../../button/style/themes/button-mixins.scss'; +@import './dnb-help-button-inline.scss'; + .dnb-help-button { & .dnb-button__bounding { transform: scale(1.5); diff --git a/packages/dnb-eufemia/src/components/help-button/style/themes/dnb-help-button-theme-sbanken.scss b/packages/dnb-eufemia/src/components/help-button/style/themes/dnb-help-button-theme-sbanken.scss new file mode 100644 index 00000000000..9276a91d99b --- /dev/null +++ b/packages/dnb-eufemia/src/components/help-button/style/themes/dnb-help-button-theme-sbanken.scss @@ -0,0 +1,10 @@ +/* + * Help button theme for Sbanken + * + */ + +.dnb-help-button { + &__content { + --help-button-content-background: var(--sb-color-green); + } +} diff --git a/packages/dnb-eufemia/src/components/help-button/style/themes/dnb-help-button-theme-ui.scss b/packages/dnb-eufemia/src/components/help-button/style/themes/dnb-help-button-theme-ui.scss index 923c1e6d8c9..d65f67aed7d 100644 --- a/packages/dnb-eufemia/src/components/help-button/style/themes/dnb-help-button-theme-ui.scss +++ b/packages/dnb-eufemia/src/components/help-button/style/themes/dnb-help-button-theme-ui.scss @@ -1,4 +1,10 @@ /* - * Np theme is provided + * Help button theme for UI * */ + +.dnb-help-button { + &__content { + --help-button-content-background: var(--color-lavender); + } +} diff --git a/packages/dnb-eufemia/src/components/input/__tests__/__image_snapshots__/input-for-sbanken-have-to-match-stretched-input-with-status.snap.png b/packages/dnb-eufemia/src/components/input/__tests__/__image_snapshots__/input-for-sbanken-have-to-match-stretched-input-with-status.snap.png index ae065a6bce4..dbda5c6fa88 100644 Binary files a/packages/dnb-eufemia/src/components/input/__tests__/__image_snapshots__/input-for-sbanken-have-to-match-stretched-input-with-status.snap.png and b/packages/dnb-eufemia/src/components/input/__tests__/__image_snapshots__/input-for-sbanken-have-to-match-stretched-input-with-status.snap.png differ diff --git a/packages/dnb-eufemia/src/components/input/__tests__/__image_snapshots__/input-for-ui-have-to-match-stretched-input-with-status.snap.png b/packages/dnb-eufemia/src/components/input/__tests__/__image_snapshots__/input-for-ui-have-to-match-stretched-input-with-status.snap.png index 7a94769c14f..72a32389e08 100644 Binary files a/packages/dnb-eufemia/src/components/input/__tests__/__image_snapshots__/input-for-ui-have-to-match-stretched-input-with-status.snap.png and b/packages/dnb-eufemia/src/components/input/__tests__/__image_snapshots__/input-for-ui-have-to-match-stretched-input-with-status.snap.png differ 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 bf0217c5469..3472c02b451 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 @@ -574,9 +574,112 @@ button.dnb-button::-moz-focus-inner { /* * HelpButton component * - * TODO: Set spacing in the theme file, as theme comes later in the stack + */ +/* + * Utilities + */ /* +* Button mixins +* +*/ +/* + * HelpButton component * */ +/* + * Utilities + */ /* +* Button mixins +* +*/ +.dnb-help-button.dnb-help-button__inline--open { + --border-color: var(--color-emerald-green); + --border-width: 0.125rem; + box-shadow: 0 0 0 var(--border-width) var(--border-color); + border-color: transparent; +} + +.dnb-help-button__inline svg { + will-change: transform; +} +.dnb-help-button__inline svg:nth-of-type(2) { + position: absolute; + inset: 0; + margin: auto; + opacity: 0; +} +.dnb-help-button__inline--open svg:nth-of-type(1) { + opacity: 0; +} +.dnb-help-button__inline--open svg:nth-of-type(2) { + animation: rotate-icon-in 400ms var(--easing-default) forwards; +} +.dnb-help-button__inline:not(.dnb-help-button__inline--open).dnb-help-button__inline--was-open svg:nth-of-type(1) { + opacity: 0; + animation: animate-question 400ms var(--easing-default) 200ms forwards; +} +.dnb-help-button__inline:not(.dnb-help-button__inline--open).dnb-help-button__inline--was-open svg:nth-of-type(2) { + animation: rotate-icon-out 400ms var(--easing-default) forwards; +} +.dnb-help-button__inline:not(.dnb-help-button__inline--user-intent) svg, html[data-visual-test] .dnb-help-button__inline svg { + animation-duration: 0ms; +} +@keyframes rotate-icon-in { + from { + opacity: 0; + transform: rotate(0deg); + } + to { + opacity: 1; + transform: rotate(90deg); + } +} +@keyframes rotate-icon-out { + 0% { + opacity: 1; + transform: rotate(90deg); + } + 30% { + opacity: 1; + } + 100% { + opacity: 0; + transform: rotate(0deg); + } +} +@keyframes animate-question { + from { + opacity: 0; + transform: rotate(10deg); + } + to { + opacity: 1; + transform: rotate(0deg); + } +} + +.dnb-help-button__content { + --help-button-indent-width: var(--card-outline-width); +} +.dnb-help-button__content .dnb-section { + --background-color: var(--help-button-content-background); + --rounded-corner--value: calc(var(--card-outline-width) + 0.5rem); +} +.dnb-help-button__content .dnb-section .dnb-p { + max-width: 60ch; + transition: transform 400ms var(--easing-default) 40ms; + 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; +} +.dnb-help-button__content.dnb-height-animation--parallax .dnb-section .dnb-p { + transform: translate3d(0, 0, 0); +} + .dnb-help-button .dnb-button__bounding { transform: scale(1.5); } diff --git a/packages/dnb-eufemia/src/components/table/__tests__/__snapshots__/Table.test.tsx.snap b/packages/dnb-eufemia/src/components/table/__tests__/__snapshots__/Table.test.tsx.snap index 39491f7462b..4b8f5219b7a 100644 --- a/packages/dnb-eufemia/src/components/table/__tests__/__snapshots__/Table.test.tsx.snap +++ b/packages/dnb-eufemia/src/components/table/__tests__/__snapshots__/Table.test.tsx.snap @@ -16,6 +16,8 @@ exports[`Table scss has to match style dependencies css 1`] = ` * Utilities */ /* + * Utilities + */ /* * Button mixins * */ @@ -307,6 +309,8 @@ html[data-whatinput=keyboard] .dnb-table > thead > tr > th.dnb-table--active .dn * */ /* + * Utilities + */ /* * Button mixins * */ @@ -329,6 +333,8 @@ html[data-whatinput=keyboard] .dnb-table > thead > tr > th.dnb-table--active .dn * */ /* + * Utilities + */ /* * Button mixins * */ @@ -448,6 +454,8 @@ html[data-whatinput=keyboard] .dnb-table > thead > tr > th.dnb-table--active .dn * */ /* + * Utilities + */ /* * Button mixins * */ diff --git a/packages/dnb-eufemia/src/components/tabs/__tests__/__snapshots__/Tabs.test.tsx.snap b/packages/dnb-eufemia/src/components/tabs/__tests__/__snapshots__/Tabs.test.tsx.snap index 9255f056b0c..bc3e69bc6a5 100644 --- a/packages/dnb-eufemia/src/components/tabs/__tests__/__snapshots__/Tabs.test.tsx.snap +++ b/packages/dnb-eufemia/src/components/tabs/__tests__/__snapshots__/Tabs.test.tsx.snap @@ -16,6 +16,8 @@ exports[`Tabs scss has to match style dependencies css 1`] = ` * Utilities */ /* + * Utilities + */ /* * Button mixins * */ @@ -347,6 +349,8 @@ exports[`Tabs scss have to match default theme snapshot 1`] = ` * Utilities */ /* + * Utilities + */ /* * Button mixins * */ diff --git a/packages/dnb-eufemia/src/components/tag/__tests__/__snapshots__/Tag.test.tsx.snap b/packages/dnb-eufemia/src/components/tag/__tests__/__snapshots__/Tag.test.tsx.snap index 9d685118493..42fafb0c614 100644 --- a/packages/dnb-eufemia/src/components/tag/__tests__/__snapshots__/Tag.test.tsx.snap +++ b/packages/dnb-eufemia/src/components/tag/__tests__/__snapshots__/Tag.test.tsx.snap @@ -572,6 +572,8 @@ button.dnb-button::-moz-focus-inner { * Utilities */ /* + * Utilities + */ /* * Button mixins * */ diff --git a/packages/dnb-eufemia/src/components/textarea/Textarea.js b/packages/dnb-eufemia/src/components/textarea/Textarea.js index eb4da6fb14d..6b2d4c8702b 100644 --- a/packages/dnb-eufemia/src/components/textarea/Textarea.js +++ b/packages/dnb-eufemia/src/components/textarea/Textarea.js @@ -436,7 +436,11 @@ export default class Textarea extends React.PureComponent { : {} const textareaParams = { - className: classnames('dnb-textarea__textarea', textarea_class), + className: classnames( + 'dnb-textarea__textarea', + 'dnb-input__border', + textarea_class + ), role: 'textbox', value: hasValue ? value : '', id, diff --git a/packages/dnb-eufemia/src/components/textarea/__tests__/__snapshots__/Textarea.test.tsx.snap b/packages/dnb-eufemia/src/components/textarea/__tests__/__snapshots__/Textarea.test.tsx.snap index a09776ead38..b813b4c496d 100644 --- a/packages/dnb-eufemia/src/components/textarea/__tests__/__snapshots__/Textarea.test.tsx.snap +++ b/packages/dnb-eufemia/src/components/textarea/__tests__/__snapshots__/Textarea.test.tsx.snap @@ -401,7 +401,7 @@ exports[`Textarea scss have to match default theme snapshot 1`] = ` background-color: var(--color-mint-green); text-shadow: none; } -.dnb-textarea__textarea:not([disabled]):not(.dnb-textarea--disabled):focus ~ .dnb-textarea__state, .dnb-textarea__textarea:not([disabled]):not(.dnb-textarea--disabled):hover ~ .dnb-textarea__state { +.dnb-textarea__textarea:not([disabled]):not(.dnb-textarea--disabled):focus ~ .dnb-textarea__state, .dnb-textarea__textarea:not([disabled]):not(.dnb-textarea--disabled):not(.no-hover):hover ~ .dnb-textarea__state { --textarea-border-color: var(--textarea-border-color--hover); --textarea-border-width: var(--textarea-border-width--hover); --textarea-border-inset: ; diff --git a/packages/dnb-eufemia/src/components/textarea/style/themes/dnb-textarea-theme-ui.scss b/packages/dnb-eufemia/src/components/textarea/style/themes/dnb-textarea-theme-ui.scss index edc86d52b5b..06b189dff4e 100644 --- a/packages/dnb-eufemia/src/components/textarea/style/themes/dnb-textarea-theme-ui.scss +++ b/packages/dnb-eufemia/src/components/textarea/style/themes/dnb-textarea-theme-ui.scss @@ -24,7 +24,8 @@ } &__textarea:not([disabled]):not(#{&}--disabled):focus ~ &__state, - &__textarea:not([disabled]):not(#{&}--disabled):hover ~ &__state { + &__textarea:not([disabled]):not(#{&}--disabled):not(.no-hover):hover + ~ &__state { --textarea-border-color: var(--textarea-border-color--hover); --textarea-border-width: var(--textarea-border-width--hover); --textarea-border-inset: ; diff --git a/packages/dnb-eufemia/src/components/timeline/__tests__/__snapshots__/Timeline.test.tsx.snap b/packages/dnb-eufemia/src/components/timeline/__tests__/__snapshots__/Timeline.test.tsx.snap index fabf5e4d60e..58b2fe0c4e6 100644 --- a/packages/dnb-eufemia/src/components/timeline/__tests__/__snapshots__/Timeline.test.tsx.snap +++ b/packages/dnb-eufemia/src/components/timeline/__tests__/__snapshots__/Timeline.test.tsx.snap @@ -61,6 +61,8 @@ exports[`Timeline scss have to match default theme snapshot 1`] = ` * Utilities */ /* + * Utilities + */ /* * Button mixins * */ diff --git a/packages/dnb-eufemia/src/components/upload/__tests__/__snapshots__/Upload.test.tsx.snap b/packages/dnb-eufemia/src/components/upload/__tests__/__snapshots__/Upload.test.tsx.snap index d1d376589e0..077c4f8f28c 100644 --- a/packages/dnb-eufemia/src/components/upload/__tests__/__snapshots__/Upload.test.tsx.snap +++ b/packages/dnb-eufemia/src/components/upload/__tests__/__snapshots__/Upload.test.tsx.snap @@ -744,6 +744,7 @@ button .dnb-form-status__text { --upload-background--active: lightgray; --upload-border: gray; --upload-border--active: black; + --upload-border-width: 2px; --upload-list-border: black; --upload-icon--default: black; --upload-icon--warning: red; @@ -775,8 +776,8 @@ button .dnb-form-status__text { } .dnb-upload--active .dnb-upload__outline { inset: -1px; - width: calc(100% + 2px); - height: calc(100% + 2px); + width: calc(100% + var(--upload-border-width)); + height: calc(100% + var(--upload-border-width)); stroke: var(--upload-border--active); } .dnb-upload--active .dnb-upload__outline rect { diff --git a/packages/dnb-eufemia/src/components/upload/style/dnb-upload.scss b/packages/dnb-eufemia/src/components/upload/style/dnb-upload.scss index 53bf0314fa7..8f979a744fa 100644 --- a/packages/dnb-eufemia/src/components/upload/style/dnb-upload.scss +++ b/packages/dnb-eufemia/src/components/upload/style/dnb-upload.scss @@ -10,6 +10,7 @@ --upload-background--active: lightgray; --upload-border: gray; --upload-border--active: black; + --upload-border-width: 2px; --upload-list-border: black; --upload-icon--default: black; --upload-icon--warning: red; @@ -49,8 +50,8 @@ &--active &__outline { inset: -1px; - width: calc(100% + 2px); - height: calc(100% + 2px); + width: calc(100% + var(--upload-border-width)); + height: calc(100% + var(--upload-border-width)); stroke: var(--upload-border--active); 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 8a3f439c20c..eebcf937713 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 @@ -1736,7 +1736,7 @@ describe('DataContext.Provider', () => { const input = document.querySelector('input') const indicator = document.querySelector( - 'label .dnb-forms-submit-indicator' + '.dnb-forms-submit-indicator' ) // Use fireEvent over userEvent, because of its sync nature @@ -1744,7 +1744,6 @@ describe('DataContext.Provider', () => { target: { value: '123' }, }) - expect(indicator).toHaveTextContent('My label...') expect(indicator).toHaveClass( 'dnb-forms-submit-indicator--state-pending' ) @@ -1770,7 +1769,7 @@ describe('DataContext.Provider', () => { const input = document.querySelector('input') const indicator = document.querySelector( - 'label .dnb-forms-submit-indicator' + '.dnb-forms-submit-indicator' ) // Use fireEvent over userEvent, because of its sync nature @@ -1778,7 +1777,6 @@ describe('DataContext.Provider', () => { target: { value: '123' }, }) - expect(indicator).toHaveTextContent('My label...') expect(indicator).toHaveClass( 'dnb-forms-submit-indicator--state-pending' ) @@ -2059,15 +2057,15 @@ describe('DataContext.Provider', () => { const events = [] const validator = debounceAsync(async () => { - await wait(1) + await wait(101) events.push('validator') }) const onChangeForm: OnChange = async () => { - await wait(2) + await wait(102) events.push('onChangeForm') } const onChangeField: OnChangeValue = async () => { - await wait(3) + await wait(103) events.push('onChangeField') } @@ -2079,26 +2077,15 @@ describe('DataContext.Provider', () => { onChange={onChangeField} validator={validator} /> - ) const input = document.querySelector('input') const indicator = document.querySelector( - 'label .dnb-forms-submit-indicator' + '.dnb-forms-submit-indicator' ) - // Use fireEvent over userEvent, because of its sync nature - fireEvent.change(input, { - target: { value: '123' }, - }) - - await waitFor(() => { - expect(indicator).toHaveTextContent('My label...') - expect(indicator).toHaveClass( - 'dnb-forms-submit-indicator--state-pending' - ) - }) + await userEvent.type(input, '123') await waitFor(() => { expect(events).toEqual(['validator']) diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/ArraySelection.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/ArraySelection.tsx index 9905abaa1ac..0a2978b9c80 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/ArraySelection.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/ArraySelection.tsx @@ -1,19 +1,15 @@ import React, { useCallback, useContext, useMemo } from 'react' -import { Checkbox, HelpButton, ToggleButton } from '../../../../components' import classnames from 'classnames' +import { convertJsxToString } from '../../../../shared/component-helper' +import { Checkbox, HelpButton, ToggleButton } from '../../../../components' import FieldBlock, { Props as FieldBlockProps } from '../../FieldBlock' import { useFieldProps } from '../../hooks' import { checkForError, ReturnAdditional } from '../../hooks/useFieldProps' -import { - DefaultErrorMessages, - FieldHelpProps, - FieldProps, - Path, -} from '../../types' +import { DefaultErrorMessages, FieldProps, Path } from '../../types' import { pickSpacingProps } from '../../../../components/flex/utils' -import { mapOptions, Data } from '../Selection' -import { HelpButtonProps } from '../../../../components/HelpButton' import ToggleButtonGroupContext from '../../../../components/toggle-button/ToggleButtonGroupContext' +import { HelpProps } from '../../../../components/help-button/HelpButtonInline' +import { mapOptions, Data } from '../Selection' import DataContext from '../../DataContext/Context' import useDataValue from '../../hooks/useDataValue' import { FormError } from '../../utils' @@ -23,7 +19,7 @@ type OptionProps = React.ComponentProps< value: number | string error: Error | FormError | undefined title: React.ReactNode - help: HelpButtonProps + help: HelpProps className: string children: React.ReactNode handleSelect: () => void @@ -32,28 +28,27 @@ type OptionProps = React.ComponentProps< type OptionValue = string | number -export type Props = FieldHelpProps & - FieldProps | undefined> & { - children?: React.ReactNode - variant?: 'checkbox' | 'button' | 'checkbox-button' - optionsLayout?: 'horizontal' | 'vertical' - /** - * The path to the context data (Form.Handler). - * The context data object needs to have a `value` and a `title` property. - */ - dataPath?: Path +export type Props = FieldProps | undefined> & { + children?: React.ReactNode + variant?: 'checkbox' | 'button' | 'checkbox-button' + optionsLayout?: 'horizontal' | 'vertical' + /** + * The path to the context data (Form.Handler). + * The context data object needs to have a `value` and a `title` property. + */ + dataPath?: Path - /** - * Data to be used for the component. The object needs to have a `value` and a `title` property. - * The generated options will be placed above given JSX based children. - */ - data?: Data + /** + * Data to be used for the component. The object needs to have a `value` and a `title` property. + * The generated options will be placed above given JSX based children. + */ + data?: Data - errorMessages?: DefaultErrorMessages & { - minItems?: string - maxItems?: string - } + errorMessages?: DefaultErrorMessages & { + minItems?: string + maxItems?: string } +} function ArraySelection(props: Props) { const { @@ -65,10 +60,8 @@ function ArraySelection(props: Props) { variant = 'checkbox', layout = 'vertical', optionsLayout = 'vertical', - labelDescription, value, hasError, - help, info, warning, disabled, @@ -90,24 +83,11 @@ function ArraySelection(props: Props) { variant === 'checkbox' ? 'checkbox' : 'button' }`, `dnb-forms-field-array-selection--layout-${layout}`, - `dnb-forms-field-array-selection--options-layout-${optionsLayout}`, + `dnb-forms-field-array-selection--options-layout--${optionsLayout}`, className ), contentClassName: 'dnb-forms-field-array-selection__options', - labelDescription: ( - <> - {labelDescription} - {help ? ( - - {help.content} - - ) : undefined} - - ), + labelHeight: 'small', disableStatusSummary: true, ...pickSpacingProps(props), } @@ -133,14 +113,10 @@ function ArraySelection(props: Props) { switch (variant) { case 'checkbox': - return ( - - {options} - - ) + return {options} default: return ( - + + {help.content} ) : undefined 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 13a53f7aebb..4e2161383b1 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 @@ -131,7 +131,7 @@ describe('ArraySelection', () => { '.dnb-forms-field-array-selection' ) expect(element).toHaveClass( - `dnb-forms-field-array-selection--options-layout-${optionsLayout}` + `dnb-forms-field-array-selection--options-layout--${optionsLayout}` ) }) diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-button-have-to-match-button-help.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-button-have-to-match-button-help.snap.png index 659f9a86992..e60a28d0fc4 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-button-have-to-match-button-help.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-button-have-to-match-button-help.snap.png differ diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-checkbox-button-have-to-match-simple-checkbox-button.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-checkbox-button-have-to-match-simple-checkbox-button.snap.png index 11c2ae590e6..d6e8546dfcc 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-checkbox-button-have-to-match-simple-checkbox-button.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-checkbox-button-have-to-match-simple-checkbox-button.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-help.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-checkbox-have-to-match-checkbox-help.snap.png index 59b6fa105cc..adc4a9c9f9b 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-checkbox-have-to-match-checkbox-help.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-checkbox-have-to-match-checkbox-help.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-options-vertical.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-checkbox-have-to-match-checkbox-options-vertical.snap.png index 71037f98c3c..3f17da9947a 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-checkbox-have-to-match-checkbox-options-vertical.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-sbanken-checkbox-have-to-match-checkbox-options-vertical.snap.png differ diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-button-have-to-match-button-help.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-button-have-to-match-button-help.snap.png index ac116ac22b4..016f0ce25f5 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-button-have-to-match-button-help.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-button-have-to-match-button-help.snap.png differ diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-button-have-to-match-button-options-vertical.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-button-have-to-match-button-options-vertical.snap.png index b2cfee0dcf5..02d011bac03 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-button-have-to-match-button-options-vertical.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-button-have-to-match-button-options-vertical.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-simple-checkbox-button.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-checkbox-button-have-to-match-simple-checkbox-button.snap.png index aabed7f2444..91c9ce5e76b 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-checkbox-button-have-to-match-simple-checkbox-button.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-checkbox-button-have-to-match-simple-checkbox-button.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-help.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-checkbox-have-to-match-checkbox-help.snap.png index 71e288a3e40..a550ce58207 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-checkbox-have-to-match-checkbox-help.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-checkbox-have-to-match-checkbox-help.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-options-vertical.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-checkbox-have-to-match-checkbox-options-vertical.snap.png index 2b05aa40fa0..01c5da8fef9 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-checkbox-have-to-match-checkbox-options-vertical.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/ArraySelection/__tests__/__image_snapshots__/arrayselection-field-for-ui-checkbox-have-to-match-checkbox-options-vertical.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 5f9e5092117..f6f8ec241b9 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 @@ -16,7 +16,14 @@ export function NestingWithLogic() { path="/mySelection" // defaultValue={['showInput']} > - + } & NeverBooleanProps -export type Props = FieldHelpProps & SharedFieldProps & BooleanProps +export type Props = SharedFieldProps & BooleanProps function BooleanComponent(props: Props | IndeterminateProps) { const { trueText, falseText, ...restProps } = props diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Boolean/__tests__/Boolean.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/Boolean/__tests__/Boolean.test.tsx index 692206beadd..f094a905c35 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/Boolean/__tests__/Boolean.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/Boolean/__tests__/Boolean.test.tsx @@ -34,7 +34,7 @@ describe('Field.Boolean', () => { ) expect( document.querySelector('input').getAttribute('aria-describedby') - ).toBe(document.querySelector('.dnb-checkbox__suffix').id) + ).toBe(document.querySelector('.dnb-help-button').id) expect( document .querySelector('.dnb-help-button') @@ -219,12 +219,14 @@ describe('Field.Boolean', () => { /> ) expect(document.querySelectorAll('.dnb-help-button')).toHaveLength(1) - expect(document.querySelector('button')).toHaveAttribute( - 'aria-describedby' - ) expect( - document.querySelector('button').getAttribute('aria-describedby') - ).toBe(document.querySelector('.dnb-toggle-button__suffix').id) + document.querySelector('.dnb-toggle-button__button') + ).toHaveAttribute('aria-describedby') + expect( + document + .querySelector('.dnb-toggle-button__button') + .getAttribute('aria-describedby') + ).toBe(document.querySelector('.dnb-help-button').id) expect( document .querySelector('.dnb-help-button') @@ -368,12 +370,14 @@ describe('Field.Boolean', () => { /> ) expect(document.querySelectorAll('.dnb-help-button')).toHaveLength(1) - expect(document.querySelector('button')).toHaveAttribute( - 'aria-describedby' - ) expect( - document.querySelector('button').getAttribute('aria-describedby') - ).toBe(document.querySelector('.dnb-toggle-button__suffix').id) + document.querySelector('.dnb-toggle-button__button') + ).toHaveAttribute('aria-describedby') + expect( + document + .querySelector('.dnb-toggle-button__button') + .getAttribute('aria-describedby') + ).toBe(document.querySelector('.dnb-help-button').id) expect( document .querySelector('.dnb-help-button') diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Composition/Composition.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/Composition/Composition.tsx index ff65186e1c1..9856e8e38c8 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/Composition/Composition.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/Composition/Composition.tsx @@ -1,7 +1,22 @@ import React from 'react' import FieldBlock, { Props as FieldBlockProps } from '../../FieldBlock' -function CompositionField(props: FieldBlockProps) { +function CompositionField( + props: Pick< + FieldBlockProps, + | 'label' + | 'labelDescription' + | 'labelSrOnly' + | 'width' + | 'align' + | 'contentWidth' + | 'disabled' + | 'error' + | 'warning' + | 'info' + | 'children' + > +) { return } diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Composition/CompositionDocs.ts b/packages/dnb-eufemia/src/extensions/forms/Field/Composition/CompositionDocs.ts index e040f710c86..f786670af48 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/Composition/CompositionDocs.ts +++ b/packages/dnb-eufemia/src/extensions/forms/Field/Composition/CompositionDocs.ts @@ -2,12 +2,23 @@ import { PropertiesTableProps } from '../../../../shared/types' import { FieldBlockProperties } from '../../FieldBlock/FieldBlockDocs' export const CompositionProperties: PropertiesTableProps = { - ...FieldBlockProperties, + label: FieldBlockProperties.label, + labelDescription: FieldBlockProperties.labelDescription, + labelSrOnly: FieldBlockProperties.labelSrOnly, + width: FieldBlockProperties.width, + contentWidth: FieldBlockProperties.contentWidth, + disabled: FieldBlockProperties.disabled, + error: FieldBlockProperties.error, + warning: FieldBlockProperties.warning, + info: FieldBlockProperties.info, align: { doc: '`center` or `bottom` for aligning the contents vertically. Defaults to `bottom`.', type: ['string', 'false'], status: 'optional', }, - asFieldset: undefined, - composition: undefined, + '[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/extensions/forms/Field/Composition/__tests__/__image_snapshots__/composition-for-sbanken-have-to-match-composition-small-screen.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/Composition/__tests__/__image_snapshots__/composition-for-sbanken-have-to-match-composition-small-screen.snap.png index 2918e99bb40..f69da1d2ab3 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/Composition/__tests__/__image_snapshots__/composition-for-sbanken-have-to-match-composition-small-screen.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/Composition/__tests__/__image_snapshots__/composition-for-sbanken-have-to-match-composition-small-screen.snap.png differ diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Composition/__tests__/__image_snapshots__/composition-for-sbanken-have-to-match-composition.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/Composition/__tests__/__image_snapshots__/composition-for-sbanken-have-to-match-composition.snap.png index a4686123eff..955311785bf 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/Composition/__tests__/__image_snapshots__/composition-for-sbanken-have-to-match-composition.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/Composition/__tests__/__image_snapshots__/composition-for-sbanken-have-to-match-composition.snap.png differ diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Composition/__tests__/__image_snapshots__/composition-for-ui-have-to-match-composition-small-screen.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/Composition/__tests__/__image_snapshots__/composition-for-ui-have-to-match-composition-small-screen.snap.png index 6f2be1da741..0e19e131b04 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/Composition/__tests__/__image_snapshots__/composition-for-ui-have-to-match-composition-small-screen.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/Composition/__tests__/__image_snapshots__/composition-for-ui-have-to-match-composition-small-screen.snap.png differ diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Currency/CurrencyDocs.ts b/packages/dnb-eufemia/src/extensions/forms/Field/Currency/CurrencyDocs.ts index 74dea1e73b2..20779ff5422 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/Currency/CurrencyDocs.ts +++ b/packages/dnb-eufemia/src/extensions/forms/Field/Currency/CurrencyDocs.ts @@ -12,10 +12,5 @@ export const currencyProperties: PropertiesTableProps = { type: ['code', 'symbol', 'narrowSymbol', 'name'], status: 'optional', }, - help: { - doc: 'Provide a help button. Object consisting of `title` and `content`.', - type: 'object', - status: 'optional', - }, ...numberProperties, } diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Date/Date.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/Date/Date.tsx index 76c8023304c..d06cf400841 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/Date/Date.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/Date/Date.tsx @@ -1,11 +1,7 @@ import React, { useCallback, useContext, useMemo } from 'react' -import { DatePicker, HelpButton } from '../../../../components' +import { DatePicker } from '../../../../components' import { useFieldProps } from '../../hooks' -import { - FieldProps, - FieldHelpProps, - AllJSONSchemaVersions, -} from '../../types' +import { FieldProps, AllJSONSchemaVersions } from '../../types' import { pickSpacingProps } from '../../../../components/flex/utils' import classnames from 'classnames' import FieldBlock, { Props as FieldBlockProps } from '../../FieldBlock' @@ -15,17 +11,16 @@ import useTranslation from '../../hooks/useTranslation' import { DatePickerEvent } from '../../../../components/DatePicker' import { formatDate } from '../../Value/Date' -export type Props = FieldHelpProps & - FieldProps & { - // Validation - pattern?: string - /** - * Defines if the Date field should support a value of two dates (starting and ending date). - * The value needs to be a string containing two dates, separated by a pipe character (`|`) i.e. (`01-09-2024|30-09-2024`) when this is set to `true`. - * Defaults to `false`. - */ - range?: boolean - } +export type Props = FieldProps & { + // Validation + pattern?: string + /** + * Defines if the Date field should support a value of two dates (starting and ending date). + * The value needs to be a string containing two dates, separated by a pipe character (`|`) i.e. (`01-09-2024|30-09-2024`) when this is set to `true`. + * Defaults to `false`. + */ + range?: boolean +} function DateComponent(props: Props) { const translations = useTranslation() @@ -79,7 +74,6 @@ function DateComponent(props: Props) { className, label, value: valueProp, - help, hasError, disabled, htmlAttributes, @@ -131,11 +125,6 @@ function DateComponent(props: Props) { start_date={startDate} end_date={endDate} status={hasError ? 'error' : undefined} - suffix={ - help ? ( - {help.content} - ) : undefined - } range={range} on_change={handleChange} on_reset={handleChange} diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Date/DateDocs.ts b/packages/dnb-eufemia/src/extensions/forms/Field/Date/DateDocs.ts index 69bc83371bd..9b9e744fa77 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/Date/DateDocs.ts +++ b/packages/dnb-eufemia/src/extensions/forms/Field/Date/DateDocs.ts @@ -1,11 +1,6 @@ import { PropertiesTableProps } from '../../../../shared/types' export const DateProperties: PropertiesTableProps = { - help: { - doc: 'Provide a help button. Object consisting of `title` and `content`.', - type: 'object', - status: 'optional', - }, range: { doc: 'Defines if the Date field should support a value of two dates (starting and ending date). ' + diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/Expiry.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/Expiry.tsx index 2cc7b9fe779..30d6f88230b 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/Expiry.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/Expiry.tsx @@ -1,19 +1,17 @@ import React, { useCallback, useMemo } from 'react' -import { FieldHelpProps, FieldProps } from '../../types' +import { FieldProps } from '../../types' import { pickSpacingProps } from '../../../../components/flex/utils' import { useFieldProps } from '../../hooks' import classnames from 'classnames' import FieldBlock, { Props as FieldBlockProps } from '../../FieldBlock' import { MultiInputMask } from '../../../../components/input-masked' import type { MultiInputMaskValue } from '../../../../components/input-masked' -import { HelpButton } from '../../../../components' import { useTranslation as useSharedTranslation } from '../../../../shared' import useTranslation from '../../hooks/useTranslation' type ExpiryValue = MultiInputMaskValue<'month' | 'year'> -export type ExpiryProps = FieldHelpProps & - FieldProps +export type ExpiryProps = FieldProps function Expiry(props: ExpiryProps) { const { @@ -59,7 +57,6 @@ function Expiry(props: ExpiryProps) { hasError, info, warning, - help, disabled, value = '', htmlAttributes, @@ -130,11 +127,6 @@ function Expiry(props: ExpiryProps) { ...htmlAttributes, }, ]} - suffix={ - help ? ( - {help.content} - ) : undefined - } /> ) diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-sbanken-have-to-match-expiry-with-help-button.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-sbanken-have-to-match-expiry-with-help-button.snap.png index bcfbe0fddfb..75f8e9177e7 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-sbanken-have-to-match-expiry-with-help-button.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-sbanken-have-to-match-expiry-with-help-button.snap.png differ diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-sbanken-have-to-match-the-disabled-state.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-sbanken-have-to-match-the-disabled-state.snap.png index e8a69bbe096..d82185a4131 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-sbanken-have-to-match-the-disabled-state.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-sbanken-have-to-match-the-disabled-state.snap.png differ diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-sbanken-have-to-match-the-empty-state.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-sbanken-have-to-match-the-empty-state.snap.png index 74ab8b7ab6b..8658eb61c33 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-sbanken-have-to-match-the-empty-state.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-sbanken-have-to-match-the-empty-state.snap.png differ diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-sbanken-have-to-match-the-input-filled-in-value.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-sbanken-have-to-match-the-input-filled-in-value.snap.png index efaaab03614..a307e4a78a0 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-sbanken-have-to-match-the-input-filled-in-value.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-sbanken-have-to-match-the-input-filled-in-value.snap.png differ diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-ui-have-to-match-expiry-with-help-button.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-ui-have-to-match-expiry-with-help-button.snap.png index 446b83c0835..23edc7ba087 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-ui-have-to-match-expiry-with-help-button.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-ui-have-to-match-expiry-with-help-button.snap.png differ diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-ui-have-to-match-the-disabled-state.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-ui-have-to-match-the-disabled-state.snap.png index 44c5304e82c..c3bbae05647 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-ui-have-to-match-the-disabled-state.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-ui-have-to-match-the-disabled-state.snap.png differ diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-ui-have-to-match-the-empty-state.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-ui-have-to-match-the-empty-state.snap.png index 5acc71a8616..ce539d2b750 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-ui-have-to-match-the-empty-state.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-ui-have-to-match-the-empty-state.snap.png differ diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-ui-have-to-match-the-input-filled-in-value.snap.png b/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-ui-have-to-match-the-input-filled-in-value.snap.png index 23bc55e3993..7b264a57d93 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-ui-have-to-match-the-input-filled-in-value.snap.png and b/packages/dnb-eufemia/src/extensions/forms/Field/Expiry/__tests__/__image_snapshots__/expiry-field-for-ui-have-to-match-the-input-filled-in-value.snap.png differ diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumberDocs.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumberDocs.tsx index 809a0109cc4..65ab2b3140d 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumberDocs.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumberDocs.tsx @@ -6,11 +6,6 @@ export const NationalIdentityNumberProperties: PropertiesTableProps = { type: 'boolean', status: 'optional', }, - help: { - doc: 'Provide a help button. Object consisting of `title` and `content`.', - type: 'object', - status: 'optional', - }, onBlurValidator: { doc: 'Custom validator function that is triggered when the user leaves a field (e.g., blurring a text input or closing a dropdown). 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 }. Defaults to validation of the identification number(national identity numbers and D numbers), using `dnrAndFnrValidator`. Can be disabled using `false`.', type: 'function', diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Number/Number.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/Number/Number.tsx index 2408bbe5e82..a59b015083b 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/Number/Number.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/Number/Number.tsx @@ -5,7 +5,7 @@ import React, { useEffect, useRef, } from 'react' -import { InputMasked, HelpButton, Button } from '../../../../components' +import { InputMasked, Button } from '../../../../components' import { InputMaskedProps } from '../../../../components/InputMasked' import type { InputAlign, @@ -20,44 +20,39 @@ import FieldBlock, { FieldBlockWidth, } from '../../FieldBlock' import { useFieldProps } from '../../hooks' -import { - FieldProps, - FieldHelpProps, - AllJSONSchemaVersions, -} from '../../types' +import { FieldProps, AllJSONSchemaVersions } from '../../types' import { pickSpacingProps } from '../../../../components/flex/utils' import { ButtonProps, ButtonSize } from '../../../../components/Button' import { clamp } from '../../../../components/slider/SliderHelpers' import DataContext from '../../DataContext/Context' -export type Props = FieldHelpProps & - FieldProps & { - innerRef?: React.RefObject - inputClassName?: string - currency?: InputMaskedProps['as_currency'] - currencyDisplay?: 'code' | 'symbol' | 'narrowSymbol' | 'name' - percent?: InputMaskedProps['as_percent'] - mask?: InputMaskedProps['mask'] - step?: number - startWith?: number - // Formatting - decimalLimit?: number - allowNegative?: boolean - disallowLeadingZeroes?: boolean - prefix?: string | ((value: number) => string) - suffix?: string | ((value: number) => string) - // Validation - minimum?: number // aka greater than or equal to - maximum?: number // aka less than or equal to - exclusiveMinimum?: number // aka greater than - exclusiveMaximum?: number // aka less than - multipleOf?: number - // Styling - size?: InputSize - width?: FieldBlockWidth - align?: InputAlign - showStepControls?: boolean - } +export type Props = FieldProps & { + innerRef?: React.RefObject + inputClassName?: string + currency?: InputMaskedProps['as_currency'] + currencyDisplay?: 'code' | 'symbol' | 'narrowSymbol' | 'name' + percent?: InputMaskedProps['as_percent'] + mask?: InputMaskedProps['mask'] + step?: number + startWith?: number + // Formatting + decimalLimit?: number + allowNegative?: boolean + disallowLeadingZeroes?: boolean + prefix?: string | ((value: number) => string) + suffix?: string | ((value: number) => string) + // Validation + minimum?: number // aka greater than or equal to + maximum?: number // aka less than or equal to + exclusiveMinimum?: number // aka greater than + exclusiveMaximum?: number // aka less than + multipleOf?: number + // Styling + size?: InputSize + width?: FieldBlockWidth + align?: InputAlign + showStepControls?: boolean +} const defaultMinimum = Number.MIN_SAFE_INTEGER const defaultMaximum = Number.MAX_SAFE_INTEGER @@ -145,7 +140,6 @@ function NumberComponent(props: Props) { disabled, htmlAttributes, hasError, - help, size, width, align, @@ -357,10 +351,6 @@ function NumberComponent(props: Props) { ...htmlAttributes, status: hasError ? 'error' : undefined, stretch: Boolean(width), - suffix: - help && !showStepControls ? ( - {help.content} - ) : undefined, } Object.assign(inputProps, ariaParams) @@ -372,11 +362,6 @@ function NumberComponent(props: Props) { { ) } diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/SubmitIndicator/SubmitIndicator.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/SubmitIndicator/SubmitIndicator.tsx index e7ea2d6e990..3e6de9c123f 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Form/SubmitIndicator/SubmitIndicator.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Form/SubmitIndicator/SubmitIndicator.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useRef, useState } from 'react' +import React, { useCallback, useMemo, useRef, useState } from 'react' import classnames from 'classnames' import { Icon, Space, Tooltip } from '../../../../components' import type { SpaceProps } from '../../../../components/Space' @@ -35,11 +35,20 @@ function SubmitIndicator(props: Props) { const [willWrap, setWillWrap] = useState(false) const key = useMemo(() => convertJsxToString(children), [children]) + const recalculate = useCallback(() => { + setWillWrap(willWordWrap(childrenRef.current, '. . . ')) + }, [childrenRef]) + useLayoutEffect(() => { - if (children && state) { - setWillWrap(willWordWrap(childrenRef.current, '. . . ')) + if (key) { + recalculate() + + window.addEventListener('resize', recalculate) + return () => { + window.removeEventListener('resize', recalculate) + } } - }, [children, state]) + }, [key, recalculate]) const params = { className: classnames( @@ -58,7 +67,9 @@ function SubmitIndicator(props: Props) { 'aria-busy': true, 'aria-label': translation.ProgressIndicator.indicator_label, } - : {} + : { + 'aria-hidden': true, + } const dot = . const indicator = ( @@ -107,9 +118,14 @@ function willWordWrap(element: HTMLElement, word: string) { const { offsetHeight, innerHTML } = element - element.innerHTML += word - const height = element.offsetHeight - element.innerHTML = innerHTML + const clone = element.cloneNode(true) as HTMLElement + element.parentElement?.insertBefore(clone, element) + + clone.innerHTML += word + const height = clone.offsetHeight + clone.innerHTML = innerHTML + + clone.remove() return height > offsetHeight } diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/SubmitIndicator/__tests__/SubmitIndicator.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/SubmitIndicator/__tests__/SubmitIndicator.test.tsx index 84c115537cc..411032ba549 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Form/SubmitIndicator/__tests__/SubmitIndicator.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Form/SubmitIndicator/__tests__/SubmitIndicator.test.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { render } from '@testing-library/react' +import { render, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { Form } from '../../..' import { axeComponent } from '../../../../../core/jest/jestSetup' @@ -101,6 +101,16 @@ describe('Form.SubmitIndicator', () => { expect(dots).toHaveAttribute('aria-busy', 'true') }) + it('should have aria-hidden when not pending', () => { + render() + + const dots = document.querySelector( + '.dnb-forms-submit-indicator__content' + ) + + expect(dots).toHaveAttribute('aria-hidden', 'true') + }) + it('should have default aria-label', () => { render() @@ -144,24 +154,28 @@ describe('Form.SubmitIndicator', () => { expect(element).toHaveTextContent('...') }) - it('should check if a newline is needed', () => { - Object.defineProperties(HTMLElement.prototype, { - offsetHeight: { - get: () => 10, - }, - }) + it('should remove dots when state is "complete"', async () => { + const { rerender } = render() + const element = document.querySelector('.dnb-forms-submit-indicator') + expect(element).toHaveTextContent('') + + rerender() + + expect(element).toHaveTextContent('') + + rerender() + + expect(element).toHaveTextContent('...') + }) + + it('should check if a newline is needed', () => { const { rerender } = render( Text of a long label 1 ) - const element = document.querySelector('.dnb-forms-submit-indicator') - expect(element).not.toHaveClass( - 'dnb-forms-submit-indicator--inline-wrap' - ) - let count = 0 Object.defineProperties(HTMLElement.prototype, { offsetHeight: { @@ -172,6 +186,11 @@ describe('Form.SubmitIndicator', () => { }, }) + const element = document.querySelector('.dnb-forms-submit-indicator') + expect(element).not.toHaveClass( + 'dnb-forms-submit-indicator--inline-wrap' + ) + rerender( Text of a long label 2 @@ -197,6 +216,37 @@ describe('Form.SubmitIndicator', () => { ) }) + it('should recalculate wrapping on window resize', async () => { + render( + + Text of a long label + + ) + + let count = 0 + Object.defineProperties(HTMLElement.prototype, { + offsetHeight: { + get: () => { + count++ + return 10 * count + }, + }, + }) + + const element = document.querySelector('.dnb-forms-submit-indicator') + expect(element).not.toHaveClass( + 'dnb-forms-submit-indicator--inline-wrap' + ) + + window.dispatchEvent(new Event('resize')) + + await waitFor(() => { + expect(element).toHaveClass( + 'dnb-forms-submit-indicator--inline-wrap' + ) + }) + }) + it('should support HTML content', () => { Object.defineProperties(HTMLElement.prototype, { offsetHeight: { @@ -210,7 +260,7 @@ describe('Form.SubmitIndicator', () => { ) - const element = document.querySelector('.dnb-forms-submit-indicator ') + const element = document.querySelector('.dnb-forms-submit-indicator') expect(element).not.toHaveClass( 'dnb-forms-submit-indicator--inline-wrap' ) @@ -260,6 +310,57 @@ describe('Form.SubmitIndicator', () => { ) }) + it('should not remove event listeners of nested components', async () => { + Object.defineProperties(HTMLElement.prototype, { + offsetHeight: { + get: () => 10, + }, + }) + + const NestedComponent = () => { + const [count, increment] = React.useReducer((state) => state + 1, 1) + return + } + + const { rerender } = render( + + label 1 + + ) + + const element = document.querySelector('.dnb-forms-submit-indicator') + + expect(element).not.toHaveClass( + 'dnb-forms-submit-indicator--inline-wrap' + ) + expect(element.querySelector('span').innerHTML).toBe( + ' label 1' + ) + + let count = 0 + Object.defineProperties(HTMLElement.prototype, { + offsetHeight: { + get: () => { + count++ + return 10 * count + }, + }, + }) + + rerender( + + label 2 + + ) + + await userEvent.click(document.querySelector('button')) + + expect(element).toHaveClass('dnb-forms-submit-indicator--inline-wrap') + expect(element.querySelector('span').innerHTML).toBe( + ' label 2' + ) + }) + it('should update children (label) when it changes', async () => { const MockComponent = () => { const [count, increment] = React.useReducer((state) => state + 1, 1) diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/SubmitIndicator/style/dnb-form-submit-indicator.scss b/packages/dnb-eufemia/src/extensions/forms/Form/SubmitIndicator/style/dnb-form-submit-indicator.scss index 0f5c99a3d2b..4ddd80d5ae1 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Form/SubmitIndicator/style/dnb-form-submit-indicator.scss +++ b/packages/dnb-eufemia/src/extensions/forms/Form/SubmitIndicator/style/dnb-form-submit-indicator.scss @@ -46,12 +46,6 @@ } &--state-success &__content { font-size: 1em; - // opacity: 0; - // font-size: 0; - // white-space: nowrap; - - // animation-name: submit-indicator-success; - // animation-duration: 5s; .dnb-icon { padding-left: var(--padding-left); diff --git a/packages/dnb-eufemia/src/extensions/forms/Tools/__tests__/GenerateSchema.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Tools/__tests__/GenerateSchema.test.tsx index d8edfa4f266..f8a8e8b3fe8 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Tools/__tests__/GenerateSchema.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Tools/__tests__/GenerateSchema.test.tsx @@ -105,7 +105,7 @@ describe('Tools.GenerateSchema', () => { "innerRef": { "current": { "current": { "current": { "current": { "innerRef": { "current": { "current": { "current": { "current": { "innerRef": { "current": { "innerRef": { "current": { "innerRef": { "current": { "innerRef": { "current": { "innerRef": { "current": { "innerRef": { "current": { "innerRef": { "current": { "current": { "current": { "current": { aria-valuenow="0" aria-valuetext="0" class="dnb-input__input" - id="id-r39" + id="id-r3q" inputmode="decimal" - name="id-r39" + name="id-r3q" role="spinbutton" step="1" type="text" @@ -706,9 +706,9 @@ describe('Tools.ListAllProps', () => { aria-valuenow="1" aria-valuetext="1" class="dnb-input__input" - id="id-r3e" + id="id-r40" inputmode="decimal" - name="id-r3e" + name="id-r40" role="spinbutton" step="1" type="text" 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 26eb545e193..3f23b7426c0 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 @@ -13,9 +13,6 @@ beforeEach(() => { if ( !String(args[1]).includes( 'You may wrap Wizard.Container in Form.Handler' - ) && - !String(args[1]).includes( - 'Provide a label when using an async validator or onChange event' ) ) { log(...args) diff --git a/packages/dnb-eufemia/src/extensions/forms/Wizard/style/themes/dnb-wizard-layout-theme-ui.scss b/packages/dnb-eufemia/src/extensions/forms/Wizard/style/themes/dnb-wizard-layout-theme-ui.scss index d6759f8f78d..abdabed701d 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Wizard/style/themes/dnb-wizard-layout-theme-ui.scss +++ b/packages/dnb-eufemia/src/extensions/forms/Wizard/style/themes/dnb-wizard-layout-theme-ui.scss @@ -1,7 +1,7 @@ .dnb-forms-wizard-layout { &__contents { .dnb-card { - --border-color: var(--color-pistachio); + --card-outline-color: var(--color-pistachio); } } } diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/ChildrenWithAge.test.tsx b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/ChildrenWithAge.test.tsx index 6b2f4e52755..20d20cfed1e 100644 --- a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/ChildrenWithAge.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/ChildrenWithAge.test.tsx @@ -101,9 +101,9 @@ describe('ChildrenWithAge', () => { await userEvent.click(document.querySelector('button')) - const countChildrenFieldBlock = screen.queryByText( - translationsNO.ChildrenWithAge.countChildren.fieldLabel - ).parentElement.parentElement.parentElement + const countChildrenFieldBlock = screen + .queryByText(translationsNO.ChildrenWithAge.countChildren.fieldLabel) + .closest('.dnb-forms-field-block') as HTMLElement expect( within(countChildrenFieldBlock).getByTitle('Reduser (0)') @@ -122,12 +122,14 @@ describe('ChildrenWithAge', () => { await userEvent.click(document.querySelector('button')) await userEvent.type(document.querySelector('input'), '1') - const childrenAgeFieldBlock = screen.queryByText( - translationsNO.ChildrenWithAge.childrenAge.fieldLabel.replace( - '{itemNo}', - '1' + const childrenAgeFieldBlock = screen + .queryByText( + translationsNO.ChildrenWithAge.childrenAge.fieldLabel.replace( + '{itemNo}', + '1' + ) ) - ).parentElement.parentElement.parentElement + .closest('.dnb-forms-field-block') as HTMLElement expect( within(childrenAgeFieldBlock).queryByRole('Reduser') 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 3e7c44d8598..830a623a7f3 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 6c8bb66f158..222001187eb 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 80d1d262711..915158655d6 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 80d1d262711..915158655d6 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-sbanken-have-to-match-when-answering-yes-to-all-options.snap.png b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-when-answering-yes-to-all-options.snap.png index 94bcd439999..515c8029729 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-when-answering-yes-to-all-options.snap.png and b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-when-answering-yes-to-all-options.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 9bf222e050b..3fe2169bf6a 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 751115cfbd3..fa470aca492 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 23794cbaa64..dffe647c90b 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 23794cbaa64..dffe647c90b 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/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-when-answering-yes-to-all-options.snap.png b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-when-answering-yes-to-all-options.snap.png index 9970339c829..d219aa1a09b 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-when-answering-yes-to-all-options.snap.png and b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-when-answering-yes-to-all-options.snap.png differ diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-have-to-match-small-screens.snap.png b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-have-to-match-small-screens.snap.png index 4038ab8f06f..c49d6ed6e05 100644 Binary files a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-have-to-match-small-screens.snap.png and b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-have-to-match-small-screens.snap.png differ diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__snapshots__/ChildrenWithAge.test.tsx.snap b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__snapshots__/ChildrenWithAge.test.tsx.snap index e8a459ad2a5..e44bffd96c4 100644 --- a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__snapshots__/ChildrenWithAge.test.tsx.snap +++ b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__snapshots__/ChildrenWithAge.test.tsx.snap @@ -15,9 +15,9 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` aria-placeholder="0" aria-required="true" class="dnb-input__input" - id="id-rdu" + id="id-rg4" inputmode="numeric" - name="id-rdu" + name="id-rg4" type="text" />, }, @@ -125,9 +125,9 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` aria-placeholder="0" aria-required="true" class="dnb-input__input" - id="id-re3" + id="id-rga" inputmode="numeric" - name="id-re3" + name="id-rga" type="text" />, }, @@ -331,7 +331,7 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` aria-valuenow="2" aria-valuetext="2" class="dnb-input__input" - id="id-rdk" + id="id-rfp" inputmode="numeric" name="countChildren" role="spinbutton" diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts index 529969c6fe0..0f723046fc9 100644 --- a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts +++ b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts @@ -1397,7 +1397,7 @@ export default function useFieldProps( } else { await setEventResult(null) } - } else { + } else if (onChangeContext || !asyncBehaviorIsEnabled) { setEventResult( handlePathChangeDataContext?.( identifier @@ -1575,6 +1575,7 @@ export default function useFieldProps( eventName: 'onChange', additionalArgs, }) + setEventResult(onChange?.apply(this, args)) } @@ -2152,6 +2153,14 @@ export default function useFieldProps( } } + const help = props.help + if (help?.title || help?.content) { + htmlAttributes['aria-describedby'] = combineDescribedBy( + htmlAttributes, + `${id}-help` + ) + } + const fieldBlockProps = { /** Documented APIs */ info: !inFieldBlock ? infoRef.current : undefined, @@ -2163,6 +2172,7 @@ export default function useFieldProps( labelSuffix: props.labelSuffix, layout: props.layout, layoutOptions: props.layoutOptions, + help: props.help, /** HTML Attributes */ disabled: diff --git a/packages/dnb-eufemia/src/extensions/forms/types.ts b/packages/dnb-eufemia/src/extensions/forms/types.ts index 11de32e4e5b..b65c5707c4e 100644 --- a/packages/dnb-eufemia/src/extensions/forms/types.ts +++ b/packages/dnb-eufemia/src/extensions/forms/types.ts @@ -427,13 +427,6 @@ export type FieldPropsWithExtraValue< > & DataValueWriteProps -export interface FieldHelpProps { - help?: { - title?: string - content?: React.ReactNode - } -} - export interface ValueProps extends DataValueReadComponentProps { /** diff --git a/packages/dnb-eufemia/src/style/dnb-ui-forms.scss b/packages/dnb-eufemia/src/style/dnb-ui-forms.scss index 703366d8b7b..8c99cd6f76f 100644 --- a/packages/dnb-eufemia/src/style/dnb-ui-forms.scss +++ b/packages/dnb-eufemia/src/style/dnb-ui-forms.scss @@ -8,7 +8,9 @@ @import '../extensions/forms/Field/ArraySelection/style/dnb-array-selection.scss'; @import '../extensions/forms/Field/Expiry/style/dnb-expiry.scss'; @import '../extensions/forms/Field/Number/style/dnb-number.scss'; +@import '../extensions/forms/Field/String/style/dnb-string.scss'; @import '../extensions/forms/Field/Password/style/dnb-password.scss'; +@import '../extensions/forms/Field/Upload/style/dnb-upload.scss'; @import '../extensions/forms/Field/PhoneNumber/style/dnb-phone-number.scss'; @import '../extensions/forms/Field/PostalCodeAndCity/style/dnb-postal-code-and-city.scss'; @import '../extensions/forms/Field/Selection/style/dnb-selection.scss'; diff --git a/packages/dnb-eufemia/src/style/themes/theme-sbanken/sbanken-theme-components.scss b/packages/dnb-eufemia/src/style/themes/theme-sbanken/sbanken-theme-components.scss index d96bc34a35b..b53c3b6efc7 100644 --- a/packages/dnb-eufemia/src/style/themes/theme-sbanken/sbanken-theme-components.scss +++ b/packages/dnb-eufemia/src/style/themes/theme-sbanken/sbanken-theme-components.scss @@ -31,6 +31,7 @@ $fonts-path: '../../../../assets/fonts/dnb' !default; @import '../../../components/form-row/style/themes/dnb-form-row-theme-sbanken.scss'; @import '../../../components/form-status/style/themes/dnb-form-status-theme-sbanken.scss'; @import '../../../components/global-status/style/themes/dnb-global-status-theme-sbanken.scss'; +@import '../../../components/help-button/style/themes/dnb-help-button-theme-sbanken.scss'; @import '../../../components/input/style/themes/dnb-input-theme-sbanken.scss'; @import '../../../components/logo/style/themes/dnb-logo-theme-sbanken.scss'; @import '../../../components/pagination/style/themes/dnb-pagination-theme-sbanken.scss'; @@ -54,7 +55,6 @@ $fonts-path: '../../../../assets/fonts/dnb' !default; @import '../../../components/drawer/style/themes/dnb-drawer-theme-ui.scss'; @import '../../../components/form-set/style/themes/dnb-form-set-theme-ui.scss'; @import '../../../components/global-error/style/themes/dnb-global-error-theme-ui.scss'; -@import '../../../components/help-button/style/themes/dnb-help-button-theme-ui.scss'; @import '../../../components/icon/style/themes/dnb-icon-theme-ui.scss'; @import '../../../components/modal/style/themes/dnb-modal-theme-ui.scss'; @import '../../../components/space/style/themes/dnb-space-theme-ui.scss';