From 2144007c284fbfa84f176152087f405cac968ab2 Mon Sep 17 00:00:00 2001 From: -l Date: Mon, 16 Sep 2024 12:36:05 +0200 Subject: [PATCH 01/37] chore: minor improvement to country list docs --- .../uilib/extensions/forms/feature-fields/PhoneNumber/info.mdx | 2 +- .../extensions/forms/feature-fields/SelectCountry/info.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/info.mdx index 45ce70244d8..fcd1f779afc 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/info.mdx @@ -37,7 +37,7 @@ Therefore, the structure and format is only used when `+47` is selected. You can filter countries with the `countries` property's values `Scandinavia`, `Nordic` or `Europe`. -Countries are sorted in alphabetically order with some prioritized countries on top of the list: +Countries are sorted in alphabetically order, with the following prioritized countries on top of the list: - Norway - Sweden diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/info.mdx index c012f92b4cb..3b8858e5100 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/info.mdx @@ -20,7 +20,7 @@ There is a corresponding [Value.SelectCountry](/uilib/extensions/forms/Value/Sel You can filter countries with the `countries` property's values `Scandinavia`, `Nordic` or `Europe`. -Countries are sorted in alphabetically order with some prioritized countries on top of the list: +Countries are sorted in alphabetically order, with the following prioritized countries on top of the list: - Norway - Sweden From 2f0f9790f2b88d5395fc962a89f0da4bdbcaea31 Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 16 Sep 2024 14:32:46 +0200 Subject: [PATCH 02/37] chore: minor spelling improvement witch -> any (#3945) --- .../uilib/extensions/forms/feature-fields/PhoneNumber/info.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/info.mdx index fcd1f779afc..4f84024a570 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/info.mdx @@ -50,7 +50,7 @@ By default it has no validation. But you can enable it by giving a `required`, ` ### Norwegian mobile numbers -E.g. the following pattern will strictly match Norwegian mobile numbers, which are defined as having a "+47" country code, followed by a number starting with 4 or 9, and exactly 7 more digits. If the country code is set to any other two-digit code, the pattern will match witch 6 digits after the country code. +E.g. the following pattern will strictly match Norwegian mobile numbers, which are defined as having a "+47" country code, followed by a number starting with 4 or 9, and exactly 7 more digits. If the country code is set to any other two-digit code, the pattern will match any 6 digits after the country code. ```jsx From c74ee1d4abf6a0772b09539f302a34049ffc914b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Tue, 17 Sep 2024 07:57:27 +0200 Subject: [PATCH 03/37] fix(Visibility): prioritize `visible` with a booean over other path assetion (#3949) --- .../forms/Form/Visibility/VisibilityDocs.ts | 2 +- .../Visibility/__tests__/useVisibility.test.tsx | 16 ++++++++++++++++ .../forms/Form/Visibility/useVisibility.tsx | 6 +++--- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/VisibilityDocs.ts b/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/VisibilityDocs.ts index 018e83569ed..15046254e1e 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/VisibilityDocs.ts +++ b/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/VisibilityDocs.ts @@ -47,7 +47,7 @@ export const VisibilityProperties: PropertiesTableProps = { status: 'optional', }, visible: { - doc: 'Control visibility directly by boolean value.', + doc: 'Control visibility directly using the `visible` prop. When used alongside other conditions, the `visible` prop takes precedence.', type: 'boolean', status: 'optional', }, diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/__tests__/useVisibility.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/__tests__/useVisibility.test.tsx index 2d97adcde91..6f90af85770 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/__tests__/useVisibility.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/__tests__/useVisibility.test.tsx @@ -22,6 +22,22 @@ describe('useVisibility', () => { ) expect(result.current.check()).toBe(false) }) + + it('renders children when target path is falsy, but visible prop is true', () => { + const { result } = renderHook( + () => + useVisibility({ + visible: true, + pathTruthy: '/isTruthy', + }), + { + wrapper: ({ children }) => ( + {children} + ), + } + ) + expect(result.current.check()).toBe(true) + }) }) describe('pathDefined', () => { diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/useVisibility.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/useVisibility.tsx index b9754a8385e..0169d5360be 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/useVisibility.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/useVisibility.tsx @@ -34,8 +34,8 @@ export default function useVisibility(props?: Partial) { filterData, }: Partial = propsRef.current ) => { - if (visible === false) { - return false + if (typeof visible === 'boolean') { + return visible } const data = @@ -120,7 +120,7 @@ export default function useVisibility(props?: Partial) { return true }, - [makePath, filterDataHandler, originalData] + [filterDataHandler, originalData, makePath, makeIteratePath] ) return { check } From 3a9fb75fb18f52f928014a4c37c5f108a9720545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Tue, 17 Sep 2024 09:08:25 +0200 Subject: [PATCH 04/37] chore(Forms): housekeeping demos/examples (#3952) --- .../forms/Form/Section/Examples.tsx | 2 +- .../forms/Form/Visibility/Examples.tsx | 41 ++++++++++++++----- .../Visibility/stories/Visibility.stories.tsx | 3 +- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Section/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Section/Examples.tsx index 4a16f32d4d6..0e466ad18e2 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Section/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Section/Examples.tsx @@ -373,7 +373,7 @@ export const WithVisibility = () => { variant="buttons" path="/iAmSure" /> - + { - + + ) } - const Log = () => { + const Output = () => { const { filterData } = Form.useData() - console.log(filterData(filterDataHandler)) - return null + const filteredData = filterData(filterDataHandler) + + return ( +
+ {JSON.stringify(filteredData || {}, null, 4)} +
+ ) } return @@ -207,8 +218,9 @@ export const FilterData = () => { label="Visible" variant="button" path="/isVisible" + defaultValue={false} /> - + { - + ) } - const Log = () => { + const Output = () => { const { filterData } = Form.useData() - console.log(filterData(filterDataPaths)) - return null + const filteredData = filterData(filterDataPaths) + return ( +
+ {JSON.stringify(filteredData || {}, null, 4)} +
+ ) } return diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/stories/Visibility.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/stories/Visibility.stories.tsx index c5464f83a82..55e75db802a 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/stories/Visibility.stories.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/stories/Visibility.stories.tsx @@ -54,8 +54,9 @@ export const RadioDisabled = () => { label="Visible" variant="button" path="/isVisible" + defaultValue={false} /> - + Date: Tue, 17 Sep 2024 09:35:38 +0200 Subject: [PATCH 05/37] chore: split out ChildrenWithAge into its own docs (#3946) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TODO: - [x] Check if there's any links that's now outdated(as I've made own Pages for info, demos, properties). - [x] Display the translations for this block? Not sure if that's easy, as the translations is sort of "on the side" in the file `ChildrenWithAgeTranslations.ts`, and it’s structured a bit differently than other translations. --------- Co-authored-by: Tobias Høegh --- .../forms/blocks/ChildrenWithAge.mdx | 55 +++++-------------- .../blocks/{ => ChildrenWithAge}/Examples.tsx | 2 +- .../forms/blocks/ChildrenWithAge/demos.mdx | 31 +++++++++++ .../forms/blocks/ChildrenWithAge/info.mdx | 12 ++++ .../blocks/ChildrenWithAge/properties.mdx | 16 ++++++ .../src/shared/parts/TranslationsTable.tsx | 19 ++++--- .../ChildrenWithAge.screenshot.test.ts | 4 +- 7 files changed, 85 insertions(+), 54 deletions(-) rename packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/{ => ChildrenWithAge}/Examples.tsx (98%) create mode 100644 packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/demos.mdx create mode 100644 packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/info.mdx create mode 100644 packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/properties.mdx diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge.mdx index 72dca39ea8f..4343853a651 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge.mdx @@ -1,7 +1,15 @@ --- title: 'ChildrenWithAge' +description: '`ChildrenWithAge` is a block for displaying children with age.' hideInMenu: true -showTabs: false +showTabs: true +tabs: + - title: Info + key: '/info' + - title: Demos + key: '/demos' + - title: Properties + key: '/properties' breadcrumb: - text: Forms href: /uilib/extensions/forms/ @@ -10,45 +18,8 @@ breadcrumb: - text: ChildrenWithAge --- -import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable' -import { ChildrenWithAgeProperties } from '@dnb/eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAgeDocs' -import * as Examples from './Examples' +import Info from 'Docs/uilib/extensions/forms/blocks/ChildrenWithAge/info' +import Demos from 'Docs/uilib/extensions/forms/blocks/ChildrenWithAge/demos' -# ChildrenWithAge - -`ChildrenWithAge` is a block for displaying children with age. Check out the [source code](https://github.com/dnbexperience/eufemia/tree/main/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge) for more information. - -## Usage - -```tsx -import { ChildrenWithAge } from '@dnb/eufemia/extensions/forms/blocks' -render() -``` - -## In Wizard - -All features are enabled in this example. - - - -## Basic - - - -## With `joint-responsibility` question - - - -## With `daycare` question - - - -## Properties - - - - - - + + diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/Examples.tsx similarity index 98% rename from packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/Examples.tsx rename to packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/Examples.tsx index 783bdb20b0d..c4cb29c384e 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/Examples.tsx @@ -1,5 +1,5 @@ import { useMemo, useRef } from 'react' -import ComponentBox from '../../../../../shared/tags/ComponentBox' +import ComponentBox from '../../../../../../shared/tags/ComponentBox' import { Flex, HelpButton, Lead, Section } from '@dnb/eufemia/src' import { Field, diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/demos.mdx new file mode 100644 index 00000000000..138f3f6159a --- /dev/null +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/demos.mdx @@ -0,0 +1,31 @@ +--- +showTabs: true +--- + +import * as Examples from './Examples' + +## Demos + +### In Wizard + +All features are enabled in this example. + + + +### Basic + + + +### With `joint-responsibility` question + + + +### With `daycare` question + + + + + + diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/info.mdx new file mode 100644 index 00000000000..fce0003276c --- /dev/null +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/info.mdx @@ -0,0 +1,12 @@ +--- +showTabs: true +--- + +## Description + +`ChildrenWithAge` is a block for displaying children with age. Check out the [source code](https://github.com/dnbexperience/eufemia/tree/main/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge) for more information. + +```tsx +import { ChildrenWithAge } from '@dnb/eufemia/extensions/forms/blocks' +render() +``` diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/properties.mdx new file mode 100644 index 00000000000..02b8d04ecb8 --- /dev/null +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/properties.mdx @@ -0,0 +1,16 @@ +--- +showTabs: true +--- + +import TranslationsTable from 'dnb-design-system-portal/src/shared/parts/TranslationsTable' +import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable' +import { ChildrenWithAgeProperties } from '@dnb/eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAgeDocs' +import { translations } from '@dnb/eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAgeTranslations' + +## Properties + + + +## Translations + + diff --git a/packages/dnb-design-system-portal/src/shared/parts/TranslationsTable.tsx b/packages/dnb-design-system-portal/src/shared/parts/TranslationsTable.tsx index cd919a1d191..2001c793832 100644 --- a/packages/dnb-design-system-portal/src/shared/parts/TranslationsTable.tsx +++ b/packages/dnb-design-system-portal/src/shared/parts/TranslationsTable.tsx @@ -4,6 +4,7 @@ import { extendDeep, warn } from '@dnb/eufemia/src/shared/component-helper' import globalTranslations from '@dnb/eufemia/src/shared/locales' import formsTranslations from '@dnb/eufemia/src/extensions/forms/constants/locales' import { FormattedCode } from './PropertiesTable' +import { Translation } from '@dnb/eufemia/src/shared/Context' const StyledTable = styled(Table)` td { @@ -11,17 +12,17 @@ const StyledTable = styled(Table)` } ` -const allTranslations = extendDeep( - {}, - globalTranslations, - formsTranslations, -) - export default function TranslationsTable({ localeKey, + source = null, }: { localeKey?: string | Array + source?: Record }) { + if (!source) { + source = extendDeep({}, globalTranslations, formsTranslations) + } + const entries = {} const allowList = {} const localeKeys = ( @@ -37,7 +38,7 @@ export default function TranslationsTable({ return key }) - function addToEntries(key, translation, locale, localeKey) { + const addToEntries = (key, translation, locale, localeKey) => { key = `${localeKey}.${key}` if (allowList[localeKey] && !allowList[localeKey].includes(key)) { return @@ -47,7 +48,7 @@ export default function TranslationsTable({ }) } - Object.entries(allTranslations).forEach(([locale, translations]) => { + Object.entries(source).forEach(([locale, translations]) => { localeKeys.forEach((localeKey) => { const translationsObj = translations[localeKey] if (!translationsObj) { @@ -69,7 +70,7 @@ export default function TranslationsTable({ }) }) - const locales = Object.keys(allTranslations) + const locales = Object.keys(source) const tableRows = Object.entries(entries).map(([key, values]) => { return ( diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/ChildrenWithAge.screenshot.test.ts b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/ChildrenWithAge.screenshot.test.ts index 9fc66520692..5cbd84ae0a5 100644 --- a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/ChildrenWithAge.screenshot.test.ts +++ b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/ChildrenWithAge.screenshot.test.ts @@ -11,7 +11,7 @@ import { describe.each(['ui', 'sbanken'])('ChildrenWithAge for %s', (themeName) => { setupPageScreenshot({ themeName, - url: '/uilib/extensions/forms/blocks/ChildrenWithAge', + url: '/uilib/extensions/forms/blocks/ChildrenWithAge/demos', }) it('have to match when answering yes to all options', async () => { @@ -24,7 +24,7 @@ describe.each(['ui', 'sbanken'])('ChildrenWithAge for %s', (themeName) => { describe('ChildrenWithAge', () => { setupPageScreenshot({ - url: '/uilib/extensions/forms/blocks/ChildrenWithAge', + url: '/uilib/extensions/forms/blocks/ChildrenWithAge/demos', pageViewport: { width: 480, // 30rem }, From fbe3b2fc97df2363548cc90163423e3b077aab67 Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 17 Sep 2024 11:17:10 +0200 Subject: [PATCH 06/37] chore: adds screenshot tests for ChildrenWithAge summary (#3954) --- .../forms/blocks/ChildrenWithAge/Examples.tsx | 44 +++++++++++++++++- .../forms/blocks/ChildrenWithAge/demos.mdx | 2 + .../ChildrenWithAge.screenshot.test.ts | 16 +++++++ ...-and-value-when-multiple-children.snap.png | Bin 0 -> 50336 bytes ...-field-and-value-when-no-children.snap.png | Bin 0 -> 19095 bytes ...-and-value-when-multiple-children.snap.png | Bin 0 -> 47185 bytes ...-field-and-value-when-no-children.snap.png | Bin 0 -> 19524 bytes 7 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/Examples.tsx index c4cb29c384e..f77521f4758 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/Examples.tsx @@ -162,7 +162,7 @@ function Output({ title, generateRef, transform = (data) => data }) { ) } -export const ChildrenWithAgePrefilledYes = (props) => { +export const ChildrenWithAgePrefilledYes = () => { return ( { ) } + +export const ChildrenWithAgeSummaryMultipleChildren = () => { + const multipleChildren = { + hasChildren: true, + usesDaycare: true, + countChildren: 2, + children: [ + { + age: 1, + }, + { + age: 2, + hasDaycare: true, + }, + ], + } + + return ( + + + + + ) +} + +export const ChildrenWithAgeSummaryNoChildren = () => { + const noChildren = { + hasChildren: false, + } + return ( + + + + + ) +} diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/demos.mdx index 138f3f6159a..063bf2ccac0 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/demos.mdx @@ -28,4 +28,6 @@ All features are enabled in this example. + + diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/ChildrenWithAge.screenshot.test.ts b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/ChildrenWithAge.screenshot.test.ts index 5cbd84ae0a5..9192b06160a 100644 --- a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/ChildrenWithAge.screenshot.test.ts +++ b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/ChildrenWithAge.screenshot.test.ts @@ -20,6 +20,22 @@ describe.each(['ui', 'sbanken'])('ChildrenWithAge for %s', (themeName) => { }) expect(screenshot).toMatchImageSnapshot() }) + + it('have to match field and value when multiple children', async () => { + const screenshot = await makeScreenshot({ + selector: + '[data-visual-test="children-with-age-summary-multiple-children"]', + }) + expect(screenshot).toMatchImageSnapshot() + }) + + it('have to match field and value when no children', async () => { + const screenshot = await makeScreenshot({ + selector: + '[data-visual-test="children-with-age-summary-no-children"]', + }) + expect(screenshot).toMatchImageSnapshot() + }) }) describe('ChildrenWithAge', () => { 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 new file mode 100644 index 0000000000000000000000000000000000000000..4fb0a049f726b29646d50ba9ea2e4f17d3c2722f GIT binary patch literal 50336 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

F5B=ek+c$>aY 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¨j&s4 z>!n0yDhGSf-Dzv$oX~tf_;onpX~g{MnU%j-n~DoUk9~JOZ91@d#rXchn|oLRh4%(0 z2GoWI(uVnN3?brO=vnts2*1$ZgU*CUJ)yscI?r?u+O(e2XGM4q1s|1A4Bz1$Dk{B& z1bBE<@Q@SZ{~!F1vr!qp21*~b;>qzLiUj0u?_n9mg&$b?Z;w84n&%Af>gwV|Cm06j z+bH@20DJdOH`8ak0`IpaTz2-!`bW-G-BkbsS4yM1z#8+@2WZmw(8igyi~=Hmobzax zDg1Ry^-@LP;0(sZ&$fG-w77HS0B2?7Py3L$E7`_!}1&$35=RhlOl8qyJ^-93J<4KhT4B;#+BJYH&upv8558c9Pe?@BM$^;Jf>-$#)7= Tnl&N!z&~Yq4Y>vxi#PuXMaQ=H literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..6c517d9d4879d9665ff042fca69eaaeb8a82a158 GIT binary patch literal 19095 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..eca647ffd69b82ef534b0d5d2dd01670380195c6 GIT binary patch literal 47185 zcmeGEWmJ@395xCIf`CYeN+TuRB}m5r0y+#xgS4c8fOH86NOy}2AS2Dt-CZIe-5_1k zb@u$9=UMBlv)(W7d)9l_S?ioHI-`5`-uI4QT)*qOLtm>Y;yt8zc<)_Ct8-905aY1k*joeZ3Yf$_V$g^B2loiIW&`WQy~UnV8bJrha8 zVmWe2<->D-ME4oWu&S6x{IJTAaiv$OD&N1|aQ9glz8R+GKO34AbKW2Dn%}269?7`y z8ajHhar|O`*I6w!TtQBr=rsl@nmipNHvA0~4<}Oh%d>CT&(X=zm^3r6!~DRP&xB}9 z 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..b46d081906e4d79b6eef1f10bede8cb056abc582 GIT binary patch literal 19524 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 f7FN>zm9w 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 Gp$PyE8>4{$ literal 0 HcmV?d00001 From 350c060d69e2694edaee8cf1f6da38f80a8471b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Tue, 17 Sep 2024 11:58:03 +0200 Subject: [PATCH 07/37] feat(Forms): add `useCountry` with `getCountryNameByIso` to Value.SelectCountry (#3953) --- .../forms/Iterate/Array/Examples.tsx | 91 ++++++++++++------- .../forms/Value/SelectCountry/info.mdx | 12 +++ .../extensions/forms/Iterate/Array/Array.tsx | 5 +- .../forms/Iterate/stories/Iterate.stories.tsx | 14 +++ .../Value/SelectCountry/SelectCountry.tsx | 24 +---- .../__tests__/useCountry.test.tsx | 43 +++++++++ .../forms/Value/SelectCountry/useCountry.ts | 27 ++++++ 7 files changed, 162 insertions(+), 54 deletions(-) create mode 100644 packages/dnb-eufemia/src/extensions/forms/Value/SelectCountry/__tests__/useCountry.test.tsx create mode 100644 packages/dnb-eufemia/src/extensions/forms/Value/SelectCountry/useCountry.ts diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Array/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Array/Examples.tsx index 5d2fa535ff6..fc8d1405889 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Array/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Array/Examples.tsx @@ -369,43 +369,70 @@ export const WithVisibility = () => { export const InitialOpen = () => { return ( - console.log('onSubmit', data)} - onSubmitRequest={() => console.log('onSubmitRequest')} - > - - Statsborgerskap + {() => { + const MyForm = () => { + const { getCountryNameByIso } = Value.SelectCountry.useCountry() - - - - - + return ( + console.log('onSubmit', data)} + onSubmitRequest={() => console.log('onSubmitRequest')} + > + + Statsborgerskap - - - - + + { + const findFirstDuplication = (arr) => + arr.findIndex((e, i) => arr.indexOf(e) !== i) + + const index = findFirstDuplication(arrayValue) + if (index > -1) { + return new Error( + 'You can not have duplicate items: ' + + getCountryNameByIso( + String(arrayValue.at(index)), + ), + ) + } + }} + > + + + + + + + + - - + + - + - - - + + + + ) + } + + return + }} ) } diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/SelectCountry/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/SelectCountry/info.mdx index df2b39f5bf3..a164eab1497 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/SelectCountry/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/SelectCountry/info.mdx @@ -12,3 +12,15 @@ render() ``` There is a corresponding [Field.SelectCountry](/uilib/extensions/forms/feature-fields/SelectCountry) component. + +### The `useCountry` hook + +You can use the `Value.SelectCountry.useCountry` hook to get the country name by ISO code. It reutnrs the country name in the current locale. + +```tsx +import { Value } from '@dnb/eufemia/extensions/forms' + +const MyComponent = () => { + const { getCountryNameByIso } = Value.SelectCountry.useCountry('NO') +} +``` diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/Array/Array.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/Array/Array.tsx index 826a672d2c7..07368000ab9 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/Array/Array.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/Array/Array.tsx @@ -316,11 +316,10 @@ function ArrayComponent(props: Props) { {animate ? {content} : content} {getMessage({ content: error })} diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/Iterate.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/Iterate.stories.tsx index 55d8ef72fda..8d4fba0a6e7 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/Iterate.stories.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/Iterate.stories.tsx @@ -165,6 +165,8 @@ export const InitialOpen = () => { ) }, []) + const { getCountryNameByIso } = Value.SelectCountry.useCountry() + const [count, setCount] = React.useState(0) return ( @@ -181,6 +183,18 @@ export const InitialOpen = () => { path="/countries" // defaultValue={['NO']} defaultValue={[null]} + validator={(arrayValue) => { + const findFirstDuplication = (arr) => + arr.findIndex((e, i) => arr.indexOf(e) !== i) + + const index = findFirstDuplication(arrayValue) + if (index > -1) { + return new Error( + 'You can not have duplicate items: ' + + getCountryNameByIso(arrayValue.at(index) as string) + ) + } + }} > diff --git a/packages/dnb-eufemia/src/extensions/forms/Value/SelectCountry/SelectCountry.tsx b/packages/dnb-eufemia/src/extensions/forms/Value/SelectCountry/SelectCountry.tsx index f2325d85f55..4da78bc8e02 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Value/SelectCountry/SelectCountry.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Value/SelectCountry/SelectCountry.tsx @@ -1,16 +1,13 @@ -import React, { useContext, useMemo } from 'react' +import React from 'react' import classnames from 'classnames' import { useTranslation, useValueProps } from '../../hooks' import { ValueProps } from '../../types' import ValueBlock from '../../ValueBlock' -import SharedContext from '../../../../shared/Context' -import { getCountryData } from '../../Field/SelectCountry' -import { CountryLang } from '../../constants/countries' +import useCountry from './useCountry' export type Props = ValueProps function SelectCountry(props: Props) { - const { locale } = useContext(SharedContext) const translations = useTranslation().SelectCountry const { value, @@ -19,19 +16,7 @@ function SelectCountry(props: Props) { ...rest } = useValueProps(props) - const countryName = useMemo(() => { - if (!value) { - return null - } - - const lang = locale?.split('-')[0] as CountryLang - return getCountryData({ - lang, - filter: (country) => { - return country.iso === value - }, - }).at(0)?.content - }, [locale, value]) + const { getCountryNameByIso } = useCountry() return ( - {countryName} + {getCountryNameByIso(value)} ) } +SelectCountry.useCountry = useCountry SelectCountry._supportsSpacingProps = true export default SelectCountry diff --git a/packages/dnb-eufemia/src/extensions/forms/Value/SelectCountry/__tests__/useCountry.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Value/SelectCountry/__tests__/useCountry.test.tsx new file mode 100644 index 00000000000..c80a1f512f0 --- /dev/null +++ b/packages/dnb-eufemia/src/extensions/forms/Value/SelectCountry/__tests__/useCountry.test.tsx @@ -0,0 +1,43 @@ +import React from 'react' +import { renderHook } from '@testing-library/react' +import SharedContext from '../../../../../shared/Context' +import { getCountryData } from '../../../Field/SelectCountry' +import useCountry from '../useCountry' + +jest.mock('../../../Field/SelectCountry', () => ({ + getCountryData: jest.fn(), +})) + +describe('useCountry', () => { + const mockLocale = 'en-US' + const mockCountryData = [{ iso: 'US', content: 'United States' }] + + beforeEach(() => { + const mock = getCountryData as jest.Mock + mock.mockReturnValue(mockCountryData) + }) + + const wrapper = ({ children }: { children: React.ReactNode }) => ( + + {children} + + ) + + it('should return country name by ISO code', () => { + const { result } = renderHook(() => useCountry(), { wrapper }) + const { getCountryNameByIso } = result.current + + expect(getCountryNameByIso('US')).toBe('United States') + expect(getCountryData).toHaveBeenCalledWith({ + lang: 'en', + filter: expect.any(Function), + }) + }) + + it('should return null if ISO code is not provided', () => { + const { result } = renderHook(() => useCountry(), { wrapper }) + const { getCountryNameByIso } = result.current + + expect(getCountryNameByIso('')).toBeNull() + }) +}) diff --git a/packages/dnb-eufemia/src/extensions/forms/Value/SelectCountry/useCountry.ts b/packages/dnb-eufemia/src/extensions/forms/Value/SelectCountry/useCountry.ts new file mode 100644 index 00000000000..6f36f0e5b71 --- /dev/null +++ b/packages/dnb-eufemia/src/extensions/forms/Value/SelectCountry/useCountry.ts @@ -0,0 +1,27 @@ +import { useCallback, useContext } from 'react' +import SharedContext from '../../../../shared/Context' +import { getCountryData } from '../../Field/SelectCountry' +import { CountryLang, CountryType } from '../../constants/countries' + +export default function useCountry() { + const { locale } = useContext(SharedContext) + + const getCountryNameByIso = useCallback( + (iso: CountryType['iso']) => { + if (!iso) { + return null + } + + const lang = locale?.split('-')[0] as CountryLang + return getCountryData({ + lang, + filter: (country) => { + return country.iso === iso + }, + }).at(0)?.content + }, + [locale] + ) + + return { getCountryNameByIso } +} From a48207f3da239d459bba266b0ffac9c82f6f2783 Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 17 Sep 2024 12:01:26 +0200 Subject: [PATCH 08/37] chore: adds screenshot tests for ChildrenWithAge summary (#3955) --- .../forms/blocks/ChildrenWithAge/Examples.tsx | 33 ++++++++++++++++++ .../forms/blocks/ChildrenWithAge/demos.mdx | 1 + .../ChildrenWithAge.screenshot.test.ts | 8 +++++ ...nd-value-when-multiple-no-answers.snap.png | Bin 0 -> 62236 bytes ...nd-value-when-multiple-no-answers.snap.png | Bin 0 -> 59916 bytes 5 files changed, 42 insertions(+) create mode 100644 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 create mode 100644 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 diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/Examples.tsx index f77521f4758..a5442df1a9b 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/Examples.tsx @@ -187,6 +187,39 @@ export const ChildrenWithAgePrefilledYes = () => { ) } +export const ChildrenWithAgeSummaryMultipleNoAnswers = () => { + const multipleChildrenNoJointAndDaycare = { + hasChildren: true, + hasJointResponsibility: false, + usesDaycare: false, + countChildren: 2, + children: [ + { + age: 0, + }, + { + age: 0, + }, + ], + } + + return ( + + + + + ) +} + export const ChildrenWithAgeSummaryMultipleChildren = () => { const multipleChildren = { hasChildren: true, diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/demos.mdx index 063bf2ccac0..de4f4c216ac 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/demos.mdx @@ -30,4 +30,5 @@ All features are enabled in this example. + diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/ChildrenWithAge.screenshot.test.ts b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/ChildrenWithAge.screenshot.test.ts index 9192b06160a..f8f92ec9362 100644 --- a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/ChildrenWithAge.screenshot.test.ts +++ b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/ChildrenWithAge.screenshot.test.ts @@ -36,6 +36,14 @@ describe.each(['ui', 'sbanken'])('ChildrenWithAge for %s', (themeName) => { }) expect(screenshot).toMatchImageSnapshot() }) + + it('have to match field and value when multiple no answers', async () => { + const screenshot = await makeScreenshot({ + selector: + '[data-visual-test="children-with-age-summary-multiple-no-answers"]', + }) + expect(screenshot).toMatchImageSnapshot() + }) }) describe('ChildrenWithAge', () => { 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 new file mode 100644 index 0000000000000000000000000000000000000000..5c8967cda4e490bcade2af687ba517853bf093f2 GIT binary patch literal 62236 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 literal 0 HcmV?d00001 From 52ca0fb8b24864d3ee72970a4ce0b32cc6ef85d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Tue, 17 Sep 2024 13:20:06 +0200 Subject: [PATCH 09/37] fix(ChildrenWithAge): sync summary and edit container (#3950) --- .../forms/blocks/ChildrenWithAge/Examples.tsx | 17 ++++ .../forms/blocks/ChildrenWithAge/demos.mdx | 1 + .../ChildrenWithAge/ChildrenWithAge.tsx | 80 ++++++++++++------ .../ChildrenWithAge/ChildrenWithAgeDocs.ts | 10 +++ .../ChildrenWithAge.screenshot.test.ts | 8 ++ .../__tests__/ChildrenWithAge.test.tsx | 25 +++++- ...-field-and-value-when-no-children.snap.png | Bin 19095 -> 11333 bytes ...and-value-when-showempty-is-false.snap.png | Bin 0 -> 11333 bytes ...ch-field-and-value-when-showempty.snap.png | Bin 0 -> 19095 bytes ...-field-and-value-when-no-children.snap.png | Bin 19524 -> 11938 bytes ...and-value-when-showempty-is-false.snap.png | Bin 0 -> 11938 bytes ...ch-field-and-value-when-showempty.snap.png | Bin 0 -> 19524 bytes 12 files changed, 115 insertions(+), 26 deletions(-) create mode 100644 packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-field-and-value-when-showempty-is-false.snap.png create mode 100644 packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-field-and-value-when-showempty.snap.png create mode 100644 packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-field-and-value-when-showempty-is-false.snap.png create mode 100644 packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-field-and-value-when-showempty.snap.png diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/Examples.tsx index a5442df1a9b..a644a9d5144 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/Examples.tsx @@ -23,6 +23,7 @@ export const ChildrenWithAge = (props) => { /> @@ -49,6 +50,7 @@ export const ChildrenWithAgeWizard = (props) => { @@ -261,3 +263,18 @@ export const ChildrenWithAgeSummaryNoChildren = () => { ) } + +export const ChildrenWithAgeSummaryShowEmpty = () => { + const data = { + hasChildren: false, + } + return ( + + + + + ) +} diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/demos.mdx index de4f4c216ac..a209cd5dd00 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/demos.mdx @@ -31,4 +31,5 @@ All features are enabled in this example. + diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAge.tsx b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAge.tsx index 2c57b4a2f3a..87fe6168216 100644 --- a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAge.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAge.tsx @@ -4,6 +4,11 @@ import { Card } from '../../../../components' import { Lead } from '../../../../elements' import { Translation, translations } from './ChildrenWithAgeTranslations' import type { SectionProps } from '../../Form/Section' +import { + omitSpacingProps, + pickSpacingProps, +} from '../../../../components/flex/utils' +import { SpacingProps } from '../../../../shared/types' type Mode = 'edit' | 'summary' type Variant = Array<'joint-responsibility' | 'daycare'> @@ -12,32 +17,47 @@ export type Props = SectionProps & { mode?: Mode enableAdditionalQuestions?: Variant toWizardStep?: number -} + showEmpty?: boolean +} & SpacingProps export default function ChildrenWithAge({ mode, + showEmpty, enableAdditionalQuestions, toWizardStep, ...props }: Props) { + const spacingProps = pickSpacingProps(props) + const restProps = omitSpacingProps(props) return ( - + {mode === 'summary' ? ( -

+ ) : ( )} ) } -function EditContent({ enableAdditionalQuestions }: Props) { +function EditContent({ + spacingProps, + enableAdditionalQuestions, +}: Props & { + spacingProps?: SpacingProps +}) { const tr = Form.useTranslation() + const { update } = Form.useData() return ( - + {tr.ChildrenWithAge.hasChildren.title} { + if (value === false) { + update('/countChildren', 0) + } + }} /> @@ -133,22 +158,29 @@ function EditContent({ enableAdditionalQuestions }: Props) { ) } -function Summary({ toWizardStep }: Props) { +function Summary({ + spacingProps, + toWizardStep, + showEmpty, +}: Props & { + spacingProps?: SpacingProps +}) { const tr = Form.useTranslation() - return ( - - {tr.ChildrenWithAge.hasChildren.title} - - + return ( + + + {{tr.ChildrenWithAge.hasChildren.title}} + + + - - - + - {typeof toWizardStep === 'number' ? ( - - ) : null} - + {typeof toWizardStep === 'number' ? ( + + ) : null} + + ) } diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAgeDocs.ts b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAgeDocs.ts index b6900045003..708b3bdc799 100644 --- a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAgeDocs.ts +++ b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAgeDocs.ts @@ -6,6 +6,11 @@ export const ChildrenWithAgeProperties: PropertiesTableProps = { type: 'number', status: 'optional', }, + showEmpty: { + doc: 'Show the `Value.*` version when there are no children. Defaults to `false`.', + type: 'boolean', + status: 'optional', + }, enableAdditionalQuestions: { doc: '[`joint-responsibility`, `daycare`]', type: 'array', @@ -16,4 +21,9 @@ export const ChildrenWithAgeProperties: PropertiesTableProps = { type: 'number', status: 'optional', }, + '[Space](/uilib/layout/space/properties)': { + doc: 'Spacing properties like `top` or `bottom` are supported.', + type: ['string', 'object'], + status: 'optional', + }, } diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/ChildrenWithAge.screenshot.test.ts b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/ChildrenWithAge.screenshot.test.ts index f8f92ec9362..2521b9b985d 100644 --- a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/ChildrenWithAge.screenshot.test.ts +++ b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/ChildrenWithAge.screenshot.test.ts @@ -44,6 +44,14 @@ describe.each(['ui', 'sbanken'])('ChildrenWithAge for %s', (themeName) => { }) expect(screenshot).toMatchImageSnapshot() }) + + it('have to match field and value when showEmpty', async () => { + const screenshot = await makeScreenshot({ + selector: + '[data-visual-test="children-with-age-summary-show-empty"]', + }) + expect(screenshot).toMatchImageSnapshot() + }) }) describe('ChildrenWithAge', () => { 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 25d0939c254..1540c3b897f 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 @@ -100,7 +100,7 @@ describe('ChildrenWithAge', () => { await userEvent.click(document.querySelector('button')) - const childreAgenFieldBlock = screen.queryByText( + const childrenAgeFieldBlock = screen.queryByText( translationsNO.ChildrenWithAge.childrenAge.fieldLabel.replace( '{itemNr}', '1' @@ -108,7 +108,7 @@ describe('ChildrenWithAge', () => { ).parentElement.parentElement.parentElement expect( - within(childreAgenFieldBlock).queryByRole('Reduser') + within(childrenAgeFieldBlock).queryByRole('Reduser') ).not.toBeInTheDocument() expect( @@ -160,6 +160,27 @@ describe('ChildrenWithAge', () => { expect(screen.queryByRole('alert')).not.toBeInTheDocument() }) + it('should reset age when hasChildren changes to false', async () => { + render() + + const [yesButton, noButton] = Array.from( + document.querySelectorAll('button') + ) + + expect( + document.querySelector('.dnb-input__input') + ).not.toBeInTheDocument() + + await userEvent.click(yesButton) + expect(document.querySelector('.dnb-input__input')).toHaveValue('1') + + await userEvent.click(noButton) + expect(document.querySelector('.dnb-input__input')).toHaveValue('0') + + await userEvent.click(yesButton) + expect(document.querySelector('.dnb-input__input')).toHaveValue('0') + }) + it('should replace translations', async () => { const myTranslations = { 'en-GB': { 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 6c517d9d4879d9665ff042fca69eaaeb8a82a158..9c18bdf3afd1df7f84eb186e4f60507b796bc3c3 100644 GIT binary patch literal 11333 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 diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-field-and-value-when-showempty-is-false.snap.png b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-sbanken-have-to-match-field-and-value-when-showempty-is-false.snap.png new file mode 100644 index 0000000000000000000000000000000000000000..9c18bdf3afd1df7f84eb186e4f60507b796bc3c3 GIT binary patch literal 11333 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 literal 0 HcmV?d00001 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 b46d081906e4d79b6eef1f10bede8cb056abc582..61dc823068af2e5169e31a010598f879da98858a 100644 GIT binary patch literal 11938 zcmdsdWmJ^Ww>Kct9Z~{AmqsK@?-8p&tGM{FcdkUr$mg@a zw#zp^y$t26ReI#nm}StI&2fUUq7fX=GqJG~P_W;kkwK-f{_~y@H5w7cn@RroS_^W6 z2RssjqR`n9tUcCxk27Snw*(#^Xsjgp@GPw4H;KRu6maU;;{!WGDX7q%QOy?oI0IPp z$^Wxx1}9<`4C3NaLeED63La-9A|_@?Oia|Nqzov$SQ%9-jpY1e zt*ki)vCOiuUCN+%6UKF0j`lp%jF!1bU0U+AuKi zi~?2%RtA$GV`J403-}*plo274VFyV`>0vvp8R@>&<{pw0G~OK=ie%&pSj+AgR|crN z1W-e{KDfDkmCMM<`S24w&Wq(>p<5tNPOhRZnO&pyZ3K?hmlA~za5|7d`MCS;ogK2v z=U(=5y+3AG#KA%bn92u%ArwLq(%6ahGtRsTA7w^QMY(t-csU~4la)ZE4_|IvDZ~M5 zDvKr)qM@hK*=yzDKGQ&B)($mJq9e{6jB43Pf?dCbrd7uJXN^>rNj zz?0U2w%|wpFF+{T9-nF%i?LmhE1OO57K&IA#GCWhe^nASY^Wq4)Osu!wV$S1?3K80 zCz!kzb0$?MjAQE9@C*a^ljKWAP}#Vx#l@(Lwz`JKkD0GMWcGYCwwjOU7RG2E-k}Y_ z4a*el?00#7>|$Fn*+z?3aA3UVnv-gKq1QnjH!K|si83w#R1B) z_7>U*>=JMo>f%KP!09=lt-TWn zM^a$PaV#y!$MD>z$^VVQ2U&qV24bnsZue_c&wr0@-E8Lx_cyX>d`^gs#fyrGN!BS* zAr~9_bGV@D=2jdeRq5Lt6Y~YQ_KS~*f#m=SFw7w(5+X!-B{Xm zl@Ub?L0claQobwta}LDZ+}y7cRf=FN1$p@)1hk^qvOhYEX~%8mi$$+Bz}Br7UwG)- z`wg5NdT_(|4vYyKJtv3>>@iuY?zZB8wbe~8<@fDkf2L+=ARqpWia`K`x$|V_8&G)b0AjV(AIHUS-Bioi{3W zC@$(?6lZABhwYz9nVB3F`Za0G?Y^oiD$#8KZ@1HaitT!?^`z^E;3t^&Yp|_U{mt20 ztJ^Hk!};BpQkG@({uhZ7w>w32c~QbEe!BKE)m4EJoi)bII-bYN+|A9+hK?;B`>ZD^ z$w^6PN5XQDGR?e~W&t-Pm2c`5ets5Z+8Rzx=BoboS|OJ5y++$ix{uTB7uloEhk$p# zilgx8Bn(Q`G8z9~9jDW{v}vCtw%q+$u=)Gv@N7h2Y=|w-k8yvxiu8*D&GEMlg66+R ztsd~ODDTba&nq!&}+;I()Hc`t4G zg~*f{i=z+Abavnq&3Q}H_Wu3DNiU&K8fxDlWvW@NWtdvFkoxw`w2{lZQ!sz8?x)z; zsd{uV&ja{*C+Lq2D~(= zrh6U!f6t^W!y0IgnNVAac;!|cnGlhvg{_}lP|(Tv)FbgME3QdRX1HqF_{TiM;R=e1 zKg#j~vM3b$v82(-c9*}Ge5p#uQ92=l`jmxD*4`A6Y(X= zd?R&Dfp`2n`@wJb^ZB8Lta&VY&0+nR)Z}Z_Zr&sYL}UYFqdjw^MV^%Zg|vyJnoHqr zKZV&2<#xREJsk7BF{x!rR=?I!ppm35apvX7BMK31p#PhxSZ5QHD-tg@Rb`}4cu%QYKv-$UD!No_+ph)4mvP;TT&u+QfUlaDb2s4#)A=HUqT9mDGp&@N6ko1Z{SYAmLY(ML zqzaRq^%wiXzIHIDJeB>^sD_;JxzS;*VL)Xg&4-K4-8zZ`C~y~W=A}$F;eZ#`V3pDK zsJoo=1fawu;Ca;*hw%U(|5+`laL;3%uv(jUc=#i!L?wz1Y!y^Tj+E zFmuqXSMMzCmpy+t0?_HvFnqt9F>~=ocHaS}F-7(xhVopUjbvJSb~YD%;J;eGt0k|b z&!1mBdb|DV)}LkDZoi6^-EOwhRTUJ%AMYE#&&Auk1$V&3&V7BCylN5|AD{kvG+Q)qFbk(_j^GiHG%(=}#1*x1-Nj;{Z%-#7oxawsX-$06yi%6G90P5s#mN!xa3zhBtuX&aOA&_v1eR#-SK6cy$)N z80E&zbtBornw(IC`?A=w*NXomfKaxzEtR;tn7kcK|LU=?N5yX)neyU|PR#Dl9PzEL z;3v1o>@#saD%#MrrV4mZy-a#1v_cO}v)iZ;?!!bjyJEzR*h!705>RLaj0bI0N=gbE z_%eb%AOV0^IJt>|_vd-k=zUd+@pNv3v|dU85zH(J6+UUGJ<)qHkdy70rx+QHjyz$lB@h*qE zRg3P)5X{h)UMUJ=*Wl{EP<@lF)4Pztu%l}L;1|ECxAmR@{k-lY)!cb`1bFi_JKxRt z?al4Hz-cE1r6*gKT?0zuo(Bn!C|Ss5xpE&(fk`2D+%&`YYK2B~#|;+|6yI4aAIp?~p=WEP3{ICV>71MDPN zPWC3t;v170_JWDz>WA~;oJqMY4-fZOoi>0Ib(=Bq`VOcFJB_e2StKPoCLuiuI#FS0 z@Jl?mHJjcTOLr^dPp@o-$^50!v1eqJPF$hoI$!__q_<^o%F`^}rke$b=6yPSP=Xc2 zq8Idbp_yV8sZBlQLjX2pUwbx7&|$b~C4dWn;*(DZVA`=6oPD&1axW+@6`GIRuX2B% z{!-;p25?Xxkt~da$O8`~;|Q`J!u04;#d5*QJ>NrCcm$jzEMlT-yD&*F#*4j9(Nc^Q zgMB;eKL((FMOlmmFD5-!lu+^_!Pey~#YZ`EPDsMh zQmbkd3CmaENTGw3PBYvzV%^IyHmRT;+g_pT$btg^Z2Nxl_92Dx_Moszoe3|wC;tw& zLlp|+{uEy+&THlv>T8aRmyjdop&~6uS=@)&FS&LVIo4BM)BY#x9&4UkR0DF#+k)A7 zYPkyjKns5@fg@ebG!-e@y$~EpRTxcy&hu2E9#;jh%FVeRLG&<7g%A*vro{MwOsX{S zHwB@z!o#Of3Ih5kFwJn`h?RCgOZh$aAa`d}g*d1mvE&Z$S$C3TyFl&$2zJbUE6c8} z$aDwTZXZ{|Ts#R8rh*^d=cB?DJ2sSzSnp-ie?ezYTws&ouH9Cc;*mzD>-`?Y8QrLw zNb`4_+osYR;P4rOf(R4_s#-bM!NEbv8z#gTNgo{ZAawEd4K$belb+T0!F*ET1Fx9; z+E#D6dj~&!+#b}=C4$0AiQd9SJ6K%`?Z1S5329Aq?!5Elc6;iv>tCB#!Ofc=9bZs} zF58cZ)k9Eo8_iXH(%{YDk+UIB&XaT+LY=0hsyfLNTs9H-k6?)yMh+8QyQC7pTCK_# zkKHvSiVFD!pw_SB{*PfPtafL*H|9bgqM0XE)l0BrZzYztuNowatzRG*fa!v|%me^! zUYO`kT6fF`^^C-z2>Qd&vJl=dU(taGG>d2;EdAX|&-9nluj`fIm31VjjU3ioy*rXA zFz+jyvsmVTxX`3|^3fGt{;F}od6H>fA>imzz|{JJ{~5Poias7XzD@pNuENqCF?5!6 z$wPmk$S9tWhtkR%YwIy=4U{k;t?pJFC4!oHQy1>dt6zb&+oJY>D7HWj=R}9^6ArzD z#0*w@ei<(ipw#q}Sr>&kuH`a_bmzi6B`+D<^g!}2T|dKb#Y%7pC0in4UqD1KQO(aH z1+9m9{(mxoB*5Ow)yH5(GVYuOOSEN*Nbkxz*8P?Xkuifwh}IK!L{HL&;jTVWqChbv zSwMF(blSu846_D>&R*6O%hbG&Pf|UY|M#E|jz_+6TS{)JGZkZGOOeBI?kbDbFrlgC z1qg(3Qhci2`%ol~l}%|8a;`qnqljLBeU(W(5R2`lt!VO(7^>Mg*fm_!d5_R-bfAU_EtPe|n`4nK1d7&v+k&((d4MF`?MNPE$DF85pRn zsW}sJltpyjC+E@!nNU~oqRLI$-y^qrhGH3agw4Y%Ph=V4%x8KL(l`382{iF~hJv2F zE%b7K=IV0k=lKhlZ)@QnFr-Ud(B>1rwW9qaZJLh?)$c85Jg+U8 z%;d?}2=O-WHs~M5HDYr}!N`wr%U7f(TKa@f=!~|1=6E#CnQ8ZbO;n`wW?n(TpreL; zI+>0o*6w?Hdcqip^@Snr#P9yHCUBRsoB;tt<(&;SD0g_hNcONT^_|AMC&uklIU_4B zsu%F!iF{fu_q-0IEhr_Q+j6=yyuKx}B11mkX#G9O?^N&~%@cVnj{Jq#>Q}6la!RqG zN9>`aoZ5nxJ(2!Q&JN6ee&gaNijem_luURiV@MAL6osRSEg^ZNey(05=N`5=V{CAI zr^DD&NrsV^@AE7yT~GTJIwBD{;HmZ_GALmoPHo8G)Yo^qca}vCArbhKw#IB)f9LNM zDX?>KFy0V7go;=u;4r#PO~1w zq4PH8blunR%pvmC9sv&)Jx`xCUl95+&EY@N~v$1GOki=k%&^)YWq zcM|ghG7Rmu<+U$C2wwIULD)D*f(SQuuh1y^NYoa675AtD&L|BFB?93&=i|oAf=o@( zH`hF<3!{U{B)WF)d&R?>BGYO3OtJVRphc?TGsIb^pWuhVa$Qx z)wLRJ7g$XBIh5?L+sBau`KYFhE62! zXPbG1LN->_Arr0Lf6*h*3Z1t%DYdyP6HmtTjjY|5R{36EuLG&_?2}|p@tjO$^YWepyM5cICGyXMCG|2}WGHy3ovIg=nUbl4*>87t3NiD<7wJ<&8 zVi%N_k52VUc{(uzzYZE4to@MOZpDZNX&lvSN-XJ;<$|l&guvY&K3-mTunIV$v-Mo( z-x@lzn<{GKgDYU=q_%hj=i9MwSa~Pj09{4e`?lNLC)+X+K)QAPK!oHcg*A#3(|pkQOWl4ladGxUnJpPqc&|{ zlLodQ)MLPpqM`egyAf#Z){F)kJ<(R9>hN@-GElcm2sRMbi`}L|J zViWRQw9>P>Q}Ix0MfdO(VGS>2Qfy1MvqVTTK>2X*A%EUG@GHs8^#kq3^8+z@UElF# ziNIh#&+CLfmNJ!7fn7tb!wi0_XjLEM*y8laWDTUl&@a@xlFvUnsy!V^_Z_)`WW{=` zN{+H1BxirB+S4hs(v!BcOTlE{e{VKE0y#04J#{)dhG5caDL1+Qn917Fq{3Ql#+LZb zuB5M=EDDnC6d^aPt(aGwrjqda+JI`N`eW?=GWYYWejx*yu8Be)wL{j2ATgZH4<6Ik z;(J3$KM>G*l=tR!|8Aup5^0c$v^o38WGVoaih3Bzf+nk_%;Lvx#C$|CQFhYMf7Jl^ClVSZT<`uwxRn|0_xY81LmS7PD3u17Lq zMR!WgQM<5XUdX_n@r^C#=USy46nh)Q{g8oZu>mWssIv_pL-y78Xqyt}%(^mL7(%!^^SDL()B@%^jLdmo{L3%MB3~jrOZD?{q8l zP);zKBE16c-`uv}w;Tj)5>IYaP-e7Tf8R}lUGOYur1Sp}qk3PPq*Ge#NFxw_3a!td z7ok^-+Djnk+N_K&d#Oj-oRcIrmdDdD6%w}Xd&%GM{S%u;5-ld7%6iml6~91(EO_J~ zh5B1du@1}4`^6&gNL158PLP%SlyFaIQ1iNQCuOt<|rc@-pkI*;*Yi zw}&9p-PebpS2?0`Ndy(norcrjne7GjQa~NIcXxzJ8@N0m5=>IpSa_U4Gmxby$`KR& zx!AiBU_@eRX>B-DUHbLzj6-*8X5~ud-!4S=3^!d(M|3jaK56R5@kn41}u@+ zkopv1=$s>tUSD+Lk3FS*|-gRb&?hR~RTR!$yJ-viKfMQ6o z&E+2WHLurU8UOm-RmsI#5qNmpPxxwQS$p23L}Aj*fK}Y{NOJpEu-7tk6!s~v_$XkNXuGy zkfCUbe8bfb9#yV}h3UwEItME&EC2BFwg5Ni^t=5k^$@suJE|aBCFE1f$day}bo7bldmY2as-ax(`Jf;s z`GyF?7H`G#-@od$i-NM4Q{+8kFyB{p(;l1^-q@WzJq`dZ*Lu>H4AU& zy;i>eut)9rEq$LLF9-|0Gc1E=aQ#ti;J09_R+Rpy6+kIkr_|pk$*W`e;Axc&X60g&h!cdRpfloek9DDjBJxeEndfB<|KKI@?A9_L=`64?W z0tA&jjGbONje^KIF*V5xZ)f|w`7LJ;bsAK_nUY>n2ht~H)vc}Y0Zs-QscSuDt^JcG zP6o+BjEOA4Djo`C)k_}kH<@gequGK99ZjCeWp+G2e^e;(N-TaRH&N5!Oi3JhQ$O-E z>t-$#I3q#5QFY0`o5`rcQLXUO;<9~{zpXPhdHU(^hkg47W=}vg`sJ2yXf-P`+`WK<2+q zG||#V+E+TA9g>^BRPDd_#*d~QR6fTJh5uHNABy56XSSPGQ>EfNA7@#m)RvTbmz08a z^7Hy>KcpT>R&$KBXI2||fE=!Nx}Mv5Hw8ZUSvve-dLqjs?EIOd(D3(oAPeJ6i&;!Y!^O`T^s^RnJ7nXu{LV7t7p}kLQl|ADz@LyD{{47!| z8C`5Hb$=j*UacTR5l6WQ)Z)QQ+7kbTXFg((WQEK7T%8h$UMgM1UD>M7XnJ1Ufxe&o z+P4OSUYqQT9C<==zX0 zNeKp`QsTq#lJ3)1B@fVwzuaG?EtRdUjQJ)(-S>jO%cVr>IzNke8=Au2_j{W)%fJu@ zC<#@bRqHaT_)}wroMM3fmh7dKs&DCCT7i9xXuG_UvoKpW8y7bJ*p9jr&xUpWx<0)f zVRt|L0Pzx4=0@w9s&1C_{S7VJ?+H8WcIkZc5~14YoQgy|7bu0~i22D|LSiQoo5~Pv zJ)ll!J#y!z(k8}czY<9BJIekzH>U(iu5R<|;vXNfxLBUU1W|kdond_X8Zrr88aC5P z)kReLh&U$HFv)vliLqP~Hq>EbZoEnS@@4hc?qtKZsBuo8p}Kl})&o+%PL`If-Oxz@ zw#1UYV@U(=<~RPEG%wPUBKG?hOs?7=1;@gbDBBa-9OkNm=l=XKUT33Yc323R`u?!A zJ(8*!a04Z|uXFZ7gvgU0!p%D47Cr20{0gIYAy>;wii}fylV*YE$&M?v`kb6DwfhZD z%z}dIwqw3fx2b@dU?OPcLubc3QjPnj17(7{SXu^zLFv4{N9|R`gi8epMlJ@A*~^uK zm716?jM?N?_rzdsqu=CFMBSl+4x2@X3oN?*kQyD|7R!4_eGk29Qx8u2v#D}DsOE4* zV<125%d2w31|=Kw%sqemOna)B^m0p;R6eaO(>0owhIB*0iY=p3!#eAbtZ1a{-c*a0 zN_nT1;B@uUU+Xz>hs}eBXPoG-A)1gXkjwJ4qEEwHQ|6y-a~+S>LZUA|_NZOHPcVS= zZq=JiA~|(k>1}a$!p?Xt(<PN0n>vbcYGSnD#K0oG(ucDWg{PrTIUDkDJs8lertZAWiVQRoQOq&?t6x%g z_R(-)VJF1Cz}#n=K?(gi6Jj-HG%Fd&e4S3~{#k`bkPG#RT|E*t$hPzct9qIfrt9bE z`nZypk!!nd%D)V9Ok1}SBS!m1+8mW0@zgsFk5aWnLJa(03#(zix3EEiX8&EC!<=mY z&=G=$^sW+np^sA6V?CqL1M?!h1`AIF336rX_mT$}^s8Lz8%11f5fw-`vAFzNr2S{8 z4YDgC?9Z8~L#Q}4=fuZ|dHvB(4UG6EXNVDfMM%1yxD%Lv%Q=chtc~MT`(%k<`Xqx^Z^gu_| zT$NVR;+<$gmMFpBR=A_+jYpfusjq7rGJUvTI7sr<>2VquBT!I=v~1vj9wUdw4Y zq^l(HD8{F_MMZ%}lddADP!(ek7OIYKLs&{@rYhJp;n-(TwX89!NIRQ6!{zAUCLZLC z?z!*P&jmXWk&Q2>)focGFMHTPR3c;?rLPKRg2GvzlVB9S)gnO{ zj2w;3AP@RfJae$fp_n4F`l6f*p`T7|GpwY3soQES%0b?B&GiReCg;l<&i&%a-{X*W z?Ftr_-8&Iqhmw4M;`60(2?-?-ZPfOj})9IeBrhGm4FMK%7kaaVLnJ z(L)%44m~>-Ha50+o7b<~zbW-FS!YmfDn&+6z>OlTowd7buDHn z+WoU>ER(1qr8vW<4}2E*fP8)o3CYP7l8~!{lERA)Ty6xMcZxREO5)@>Kq3h#g+d+xY4x62&jEeiyxsbPQek*=^Jnso1psUy?t3au<&<4a{ zHs=iP8PGQ2(q`5~4h{KdrIuG&+y3VdKGcgZYXw=v9k0NU2~$`V6;-A^fx{osQ|uUT zUSB}TNZ83xL)on?3!z?}1_eh~?#b^8_p(Gw+5QDCGNz+BWR+(S1N!#{opm-FeInUgcmGtO-8Sog~Rx~|`UMQOiK#>07ngMxyBr>df$gMxwz1OEqMJp{i@ z%jUic_H)5k^TcePkd#;#z(Yz2i-^*HKjjCYw3L2krkCEMWokp9mN-0k}!1Zgm# zR0?x)Hg_cdR2fJpHf>J(Bnc(EFXeX}bV8iSs3@JTt-IIPWXM_xXm=_;0l`q!%9S*C zjw%|n4B8$xy~&4^lmeQro}L0IrffHT*)3VS0x3pKUq;~&?BrY zZ?H{Dv-|G4^Dpf`^!u;DLP3p0xW6Z5(1e$^k>x5P>nwU8KaCdG?f-g9V1w6YD0aL& zlH}#>`@7{&2J4~CT1+|m0?G9qczoYF^d0DPVInu9qPdDEU18@W^8db!qI|VD0d>H< zM2;$S9GkaA?vtYcibV<<^=lqXjP<|Tm4W^lCZ#?+ft;^t=z@Ie5w7UPW+C*=tx_6ez?PmNrxf zPm+oi+#3z9F&sruKfbSWB2c&|J|Y_br+z?3ecp5LBWuGZj}mC5=}XghH=eoLq@IRtlp%M^>@PQ7Voc5o#qVs;_3G5_s-AZ zCB_XExAV1c#tL3!KMyzyVoO57K3DEY%lnv-!T$53@Z(5x1A~k*(^exKa^4*}*)Lx& zPqx`+8=TWI>HM@!;ma89+xf+pv2kokGDVN83X7d)>op(ZQ*Q(=w!eF~Ia6mJ9xDs^ z$uFlTgO2az;i5wBMHt zkEGWG_0Mu)%#4hTFv(Xwh=W0`?B^_s(Ikh@q_l3_%2Bupx4x%x=_8twlakhY zq8|G^OyDsViFf>mLj0W*-GL237{{vlQAJKJxXkdY5-1KE=fmj(s^1IN6?@)b;CmF7h1xV%`ZSDqAt zNucKDRzsFa#$$vfu`c0#^lVsE9?JG2i`~rsB(=<}-IUj?jT-}pB%yh)N&O+-lN8Q{ zZO!(2hiOJFsfz+=iH@Sr$rj6MUu-gya-7QcL^%dQ321BM=hq~ zTSUqU{Uz7%?k-^7wA`r9y6-(@Q8Xtl+CjK_Ixo-kSKD;|tJBR(F1<4LueKv;#l`G5 zcMGROgfMsx97~q}-}(^S+2Xr)GCO%Fp5FRkYI44E{2*u-4{`WLtsRoHbWw|%4!fAy z-46%H)wRU7E{8viqmpZSOm?_kG#_F%<1SV2wZBl~{`UptbC2R$=S5T7@kGOsT}igV)*} zDhhP#;t>!`mZCoqwky~UCC84ZVvKOLx@P~c*jetgjtv82TGHio_(}o_qzfA@-v0{R z)(3-;SO+=;bW%I;$P0VX9Az{;2J`SXLnSEDgB}m65fm(T%a4SVJ$i?tMHOC){tex4 z!;6)alm=~c{r^PfGj%4<2uh)kR-0Z+cYUaw-Q&s|31OVDPb+5Z83mt9;@H6z)({aoUS4KIGXHz(HX zCJbiRbi%li)IdiCTSA~-wAB5Z%{m(X^AF}GV`kWlMw_p6B%Dx~7 zEpE!ZWg>g;5+-nSR%k{7i^jx;D|d+GJ@?##8rE1x&3TMz;_v^WBtj>t{{2zd9%m)+ zHj*>V!2f7~?fGh(--Vm<2_`;u0zS?2jl2FNPI$4w@30KEG`i34X~ZWOTEOTV|KQ6U zLK|>yapTLR5Y+QHP5m3!aAipoE8cy#3AZzMo!se@ZT-ZaBc!h4(U1y0DYt=k3|q~jf#6BU1A>i00|Pf`z7n1+ z^}o4q?!;77rD6E43*X%TZmnx=tj5uZA9r~lfA+gLdVXQW9pzo+iAD z5MEntJ3`w^U8Ym0cKJDIQ#^EjHefj@#1p}ub*uYKh29aZo)B5terxpb5ydACb4uM3 zokAU=pbiE|sAy0Qd zz?smQUG~wTPz!dLjltA0hO1dwz9L1w?MRLfYJg8(blum3;c6^8Q)i=?yVhOuw9gPI z2MhDk2T&tOp@;7Afy(E9bKlt!j9zGR)CQBIR~6uHTgC>59UBa;T?>t)LO>TRX@O|- zk!K!K(Q(32og?7j-sQ>utB)cfch@ z;LeY6DWqVrocaJ>KRoew3S6SW281gO2NyHy|2qpFV|)iD_4~Ev+iv~rTAHy__4(^5 zf8r`BP}jYV=erGyYGA2aTTeGj3&*1}@_V||2E)!0_j-}WV|*{Ff7d%24lq+1U7qYCTV~CLZCvf-aEbx;*tZ!S-MSDHz; z^Y(cE`!ldz+0F}=%w#kSoPyb7Fz>H{IWXn+=IZLVqHesT=T^#(Y;paMqRzZ91XQ5T zsT3vxx=pN^sFrI+}cj==j7fWU2bsS3iM7>DqoS zw<$9l$;rD^sGdIf=VvC1h=@oS-f)%})7iXNuE*|-@J4Wm)Msb9fb-8Je5P}M?N_7N zauOEb-My5$J-3Sgi%%)2aJo0oRjOZk3U~N~|IA|};eIg;C!pg6iyl5yD6P05;O0`N z(o~XC*bXXl<$JIsODO@bl4&_G=KpY;g3fCLWRA{8JiPj!~peK;p8%_be!e`e~x8p+|A zuCW>W(sI~GC1^WLO)2J9Q2TncOIMiT%r_blrbtR_*Zg+(Sn<*R$p5AU@2+-5cXx$> zsMY!l6j(-4F|R?D%&Q+647vWNAJ&GmXHoNf&-N}Cv1s6&s;a6+(8K;rG>GEH_Mc4W zG@$C9zYz1ktbOV@12r3K3nglKBSg`!4ILwdnb5;618z3EsCCAF&J;K9w3%1>>|M=C+7W_%%)Y8aV!1rhtTDezNt4$*v+!* zl4WnKMGCA`?+ter3)SW)bYxXD7943ubQg_w;wCb)_&k1_GFXPWC zi}JzB_Uon+=z5QhUktsP*nHbyTuz)=*a=gUYz(RQSZ>2Da&>WZ6!`yA1utrlB*Sg`u%Y|lu{jif-wB&P!#>by_|@+6bSX_tJ8F$!BI&@L}> z31qum35iWidZS5`%>piZiFCtWTN$;xEO(KJ^o>AVgGB81GY+uZ*S`+TEF5`)Xg zP$zf0*Q4|NS$Dv}WqFFyja&jt+h{DIaFc0%j%DBKwp=mjI9(be^k>B^a8&IL}<;Val8 zcI^Z{RWRTh?*ET64+k{_OMVBBH6uy3gWdy7sBV#Dp~U`A2f^f6p6q*Iz0^sGp*sxb zk7t^E-Gs7{xJ4<$UEy~TqK;VP5^t+Q8f`{$UWx{hQXfF^(vk9?_ChtXjP07V7H8&7lLAh&BeatYbsMY;l6Z zCwCkVnGp|-vA7zk3U$A{Fk1Wl@wcxPldUoR=je#f&1kZ{53suMD=#aW_m1Ae89cLg z6-Y&sTtl9uMaIWZiUu|BNC!l0VzuDRWgT3;Cq+Nd>Os+rLEO6EV|tEd?%KZnC7ur7&|=Fc5BV_11mUj37+l0IhgMk! zP{JY?$9}jry#@pf8j`GFhbwyu0;N79xCF8&usq)O5v&sA4PH9Ej9 zOG{+q#&yd7B!oG8KgrvY&^%LuNJMRcAyO)jbD&fG9*4l*WD={@yolfb(j)$7flYp` z!?F>>v(B8xxAQ+4CEn62;8$fA`!pBekV_UtHdfISHj06jwDikv?z`J>J-xk~97|x1 zE0&Bq#tEg?wZkg47ZA(_%5>tSGbGT(;xYoQ5>;1MqeKA1lV<}{!foy#vbU2fFydnx zc`y})tA>~Qr}rn-dvY|d$wdpB3ys6E^7b=kn#te;21g;86hc^$s1k1^#t+;zFfAT4 zX(TKV^GGNVespodPOYC=Hz0gNIE8r#krD;^-DY*XkpWs8V1CClV>tm2~ZRR8ZB=qClz0ZI74!AzD_KqMdIk7a7z6VRWnLgo4K z|DwfH388oEo}}5*E09|7nwY^intaBUet#Exu_J-+?64MF%y=E7u#oMwP?Al-MIMcy zf+&hS*44HpctC5M%&K`@JFO4ttx5cwYtm9ohvpE;B9VJ{ z{a52B&2Wp$qtDE7)M^Sr5Y)F>FVT;+=*QV@2M8&x!S{3^7jvd4-Vus$gr8= zixi@?j|l0x=^l8!CyE(3bm{ud-jgg4ps^U6QQ3TZA#|ghhd)QOC7U&2u#UnD$GYT1 zV4(&IRIo7n(;Hx!nYUoI2DTE-o`in^m19r3`^o$ShqiV^>Z*+CjcsV`fv<^`iW?(y z&<7O3lMS{CW$uA);bmTpwb)qmp;`wElV$_qAWH1Zh(b?2UJ{sS9!`j*Q3x(x*Mn*q zJ@PRfjge|b6fGBLMbeW_rce}yZ$TW~mLZMaL(Rw*lZPo8#Y)}-cK-EJcDw5olo=HwGf#Q%ah`WK%@UEq{ZfA90_=j-m;CAPb4XJ@ zGE*QGaV2kfOD6c&9@ByyUI6NzH$!ZBIgI7io8IBpY9eY(t-R=IygjyOB!r$^@n>EnVKm@$5V0!8eh4Z zT#3JgP$`MN_`~_qQ}Oc3dYl%0$4Y%U$0LWrqxt;^n}rF-qv@aSbuJe$vZqcR~ zXD9vYbSQa9|NGuo+ci74Bn8vfqFZK_#+g_Ai~_2R7!lMy7t~nW25^c!=(wR+^G`dB+;7@2e8m&lZ6kK6x;{_s?3RZoZX z>pmG(Gp~K;)z5eTj?5bU-Du5W_`5Ze#ARj@8Es{m`#?P~4{=_hw^*=M+O`dCcQB+Z zeM{5cDW|*Lz4euD1fgZ?tefkcQz#YmTWj4@THFOkL z7sU0S_Pf-Htz;Tar#3^@1_us|@kyLuL;P!R9itx~SukPWnI|{fS&X|*%th#G>nm*I zQ~qHbsa(u?udPb?7x*Q1$=YY)8HNNpDxRcVvd`Su{k=BXa;M}%ilzorvf+&v1_{(< zNt;!0?YlUR`j|m1L1S#RBLfF{!09Y9g3rr zIg{GgpFUc)k}-o!L~B`F*qFBex376klz-~&ZDqd}F0SKmg~0D%bc0uN{@;XkG>sn^ z4s$xU?|g^2t#>I#NnUBuXX1Qo;#(Wy8LbvupPH&B&bO@OW=27)CqatXee{!K(QOM@ ziQ@Jph3YM)Ru*+p{zH<>JTO#~5;ckAO7nh^gKaIuV0`wi>!Q%F?YhPnI;a&E;Se!h z9rzgNBwp=Y#{eb&qKm(ui$QC@}#S4+i~{e^R>bJ{_ky8{mgp~e_Cx`Z3PM%OiOdR zdfF#49RFYB9I(p?`jAQu_XMU0t7C3`o!Tm#$I5R|<6#aON$kFr}Y9I!-7PWRB}5xQ~i% z4`y;6t;EzgcA$Lwrp+MDf8jN__A_(c!LKDiol6K;BxLh+!MDbFs_GG6wcI)L>834n zy4njx6M`SJIxwz9MuDxmUyf!sOJv5o&82E7K74myH5boAg^hZpoO#XE0;c`7D@y$_ zfAGQ!Pik-;TuTO^r5k*}o0L15mV@F!E;KcRLJYS?vN)8-U+6~!&s8)F^nr!hVQ1dh zE|vQO1Gwv((Q%zs0}7hLm*fXj3)cS$)`(e7_v&rbeTOA8c z=XZ2kn`%p-9L22IERgIro0tWU_4(m!p`N91+lZlyRkq_{-5|~b=r+e8u6||6-h!{n zpC86f(ZvrD?ZYRgdbA$PD~jvCHqwJfsb{fd?;FvWRpZxCvEcKQ6J)@i!b$didwSRw zMkvTmGq`ekhu#?Vmt=6AD&tvqzAzbicNDebvsLm)__5?UN7Uu^d+X-3gwH+_-U#G= zqvqRycH^b$ip$r@ez)uR{TzD|NCTEvu}{X6j%=J$TD;u`J}<_7Z&xCBCy2#Sv zfa~qb-)gOlvz2chTVw9Y|@p{fWW zyZp@#=8|7xa0DTeDBpJQQ$ThByt+%!v5!&A?O1ExOCJ)eBRA2mj~NBqU*3nx`RK5W zB4DjAojA>O7K~~K8Oo5Qu!Lb6qwfW8aH&0Qu);HwE0td*$AlfJc8!<3(*JqMEeFc_ zq;R6h)ca!nQ)Ukg%4gb&@Y;3uzS@mzYx&fo?C>6spvJA*5_Nmkn^c$gsii`=SZJwd z#zlPgBLnGd#gmJ~F_zq!TI-EZ{cD!jHXojz@O(pvG`}T9iV!StosCzpTAAf=eL>cT zC~eMsOPzabPwD-(=c#H-4s$0Va;=JA%(yIpSu>khTd7RK<9>vXfLxeZNQ+Q_kpNPm zu4fzbM1+{^&%2_x2e}RY&3;qm#yHi&S4lyi$Uz2LS3!93u$`G(%q3S=Kmr!YsTMXn zq^^~Ec&BHxLtgr$wX_(YavT=3E52W@8B}gPV0n}q$3v6Dp@`fmjXwFt1UhN&@jLaq zTb(xZ&QGQo6?xIEm(@qKg?sI%?F;qutq$pBkTRLvy_mnOTxvrVDU)#ij-mehF9$N<#75 za?89yv@P;ayfbpp>H>s667gI123x_~%s+6Sp$^|;G(TSLiyhMA@l;s*_wR|9nUIVb zwvtUZ+7n`ww6{NBkM)hl{sBSy8A)F>UPR2>y%b>tjZhl&#(kw=OP!9n;7lVPa*E!W4PYXLoePMqx2`58kpw1oYMu!di-E3^l9ci+8*};Sg)Jrtzga z$5W4Lnm^d7{`pZg-l&dRlTm@+?#9Qk!K;$FIAcrFPD{Tm*Ri54^`!Zdb1Up{^#M|n zqqwi_vp!#mP76GIro$fb0BMi7K)e3PnFghd;+({np2`bK;dsf|YmfNm){7`Kz$-NQ zK*iEH;6jZBi40+NS?Jm2cJ#>W7Hpk&5m{ws}Q$M#b0^^&! z(s2fjajj(^37g)i?&tfS`)3^4#h%;O;NY3Bre337qHTF8$Kyybgh*luHj#;#+D!kG z64!9ck>Db-b5GUgJ}`M{=7^^xeOQP*558EVtMm3s(RGldaQqtkivf%{45u>8SZ&Ic z#It$w=f@%A$rkn#Dg{rjUayN~LY^S_zBny5n>pTijlSQi(V$6(K5CfIT(?QS?Ba~^ zzn>!FOEnSIhW}_&qX;IxWXdnsbnzOBmR6y)jOVdB>fEf-DX-zzHD+f*@9dYEPmnY1 zF>C2Bw}cneL{qRZ#=a7KPyiWYoVKa5i(n)4_Fqc5x^GJLSm<3vH2qP2{c!8gsIIwU zm-=E^IimBW_rNbmLdVd*^=-fV$j7J;o`wm5g zMzH(kJy}HHKrPt=Pmh}HLd7{bM?)fJ49jEj6gHb*SO0+I77v%c^}|uenX@kq+GsZP z+_W2@S-E*!4FSqjF`bB9ZMV zNadHB1h7HSfeIY7^O>cw+P#B>qNI6lGJea;<9?@@1#x~=r?(B^D?Q2IMxGTPAsXs~ zA^5pcOsPnm5Uwr9>c+9ZXiXt7{=U_cSnHEN6W%A&DX%?$UKh9Bzz{M4T3-&%L>SGD zAz`uASnUx${*N;iD|Pn&VqW$J%9!d?bj#zCsoc1&TfdmCCw{!+Lk%$-Vh24g(4m~e#KJ4h4Td89r&u)sC>f;jW*cI5lnCjrJ z#eAsE5u%tJv^eqMB6c<_+1L8{W^%ZCM23In?PT8+(%w2{#-2gWv5`36loU_@b6S~p zLPAptn_g0i*QI5?l3{@OL@-FvV)Up zY$cS2A^!He&=xD5QG39z*0I_OWqA4SpC73|+2Yh|eYQETQteUaIr&k}!tw`0i+9|F zpQ}|}L06LstyRyQj$b^$mcSj?gm?-t>S35OZJu164XLu=pQnw#WKmZu@w+Ifz0|Fj zK^>s!Ip;b>m&vVK#<^WMnsp9tg{ZF$8b>B2UG@3-adv8L%h=e_5;&=4nRYVqr|E&TkcQpCH zyeAYDWvL*wUj(XGL|7t16OEaW0=&4x#iks@J48V2qpoCG%mKtfItV`%KMd!xX8{>{ z4v@6g#2c{yXaRaSfdBYS47mUapb1j`j&^YTOu+T=gC_k&W&%^Pd<0635du*8}118NaK0h$9k@IiNK-90Uk z0%(E%rxsnNC~~Ve<}fMin(wf&ks@>DzZR%@5~M+dR8Ox^(a}i?v2k#C9~x#OTuV&= z>T({7nadgEJBK*JcjLz>ia6bl#3ij-+}1xbGn;r(Q#?+V2V|c_?sumDXwXn>W>!`; zziZ@D48p#9x+YBB!vl(yZ#jAY*AAhe^K-7GjPreN3FCUKLPs%)+zq$TksQw9h?t$7 zr9VF?SpZ@nB|7{+&{7gC84U)+Fh($r#Yx>c3nJr|>5)0f#v3R!1?Pdbfxj?HJfKIu zo1op{a3;y$#e~ZZSz2Ca23@frUXxT~dOW1%B8FLPNkbshH#(<36bC2nArLhb{1s#= zwo(7>7n{4GCR^*%-G*Yu*06P01&f4)2DZJ(37bg-`ui6%$k)`Opz${d16?dnh^ghU z>~wUtB>b(dMRw^gjjzdIlb}xG(^7`e80uSO1 zPe62R?5T0{H8E^510mq@?m_db5G#Ms37MDhP$RjHFl z-s!c3PT7K+_0jf@6lA?Mktg%--o3Q`m~zit%S8n?8^W~}Qk$V+zim3&{)2nz$3&n4 zR`%Rq>8oh(-^!mHNO}3kJ#X;6($CP2rvKWF8|~Ewl_HYc{=Z9x06Jr|5K4VdB(noN zHatmqiX7bf|Bc_P%8Os*NEoK^zT`xf>Zebdy_+WiD%@AE;$<`I=;&yYxR+D=w!^Pa zK_-nZvVfssb)2n7o;(6rh=}t7H%M%!9j^8_my1hDnQl##m%bBqoOxQJ`z3DCe<=zy z=q(XIa9P110tr-fMhbwkgCtX0R~YV<@~_P@0FDft?#`}vV$!9Mo5DNyFWC_R`KwXP z37hw;{%pNta?k_J=Sye%i)KxLCAP2vSo8Y?(uBZuY_Ob100`xtlbQ3~Z>@1%RRSEx zdv4?UA6!;B65cv1D=T31vLX)`qPGD4vIek4onmcm~t)x-k08XRMOPxc+;xmJ|mdY!XddeFhp`SJ%ELGW~AywC|KhfG?}@+Bfyy zt(&y&4#%r`GscQ)|GpYPFTcNN(b3phFa(vo0P^heu%B+I7?NLLRA=`e;v!%fTK2M8 zj~1k6?;##z2#;P_2DNm6iLWmJW4y4qR%!rAo5t66E@$e!PD+#SsR{j=C8n#E#$B>n z57^=HoHqdJsQtCO*lyOKU#iFRLBJ}!BriGwrDgedp}IPlU2Fi6*mMgpsllD;uPmIL zYIp4oH-ZYJIc&6Ygg*dYH~`X{m0>f4un^ccxAir^nt*m8oyVtr4p891t+U7d%4nPe z)VaZOCMH{kUf4&}1Kh28Y1>n%m+&A5Dd>YQ(*@j6Lqa+xu6D~_oDcjK!4>YHI~e&v zejGr%ySq;~g8&4x`FvTiYc>44#T?}mE!LQ@-h&q>jQZX)0W2Dh6Vu`bglC$R+z-1oSP7`H@f*{4dyS^&hBZp>~ z4K@s(%UxlDAWyz)A#45~t#TK1(MdVfPX1^HL}VRfy&XxU1wda;?wFoq~S}Hd0UR{Ss>L%Az&C1#em2LNME%{Y~DvCOX#XQ zi#Zj<^OSe2`B8#~TqH^_H$>9?&-*!mM>oA_8Gr#)?PlM(?Lx`+_5sM27u`i-Fqv3r z-1!4~Hg!c7ZGosNwiuu(urzK%M$vhX46L6%01EQZJv_Dgu}W%cy+*?pGJ@&djK zpNq$rjaL2mtuD0w>}G8(FK#K+Kj--FiAmBKhEJtr*#AaQJD|36k3uL@Y?(m z-{P#^48CNZ&585njs%}rB@@82CxCKHLw)e@HI6|a6ag)PD$u*AkSuF{V#|Tc#eA;J zi&kSWh#}+o9lX91w71Y|?E5`(GmnJF2+FtK?(e4Rp;9B1zkcK_XF*9O;x9W# zzWti0Z)5uF7t_GpoMOve?iP3Mu87IZiIsQGS7A1%7yt63`bG};F z%#;ZK|9RQ}+Y!q0A3K^nx7TK!+f#vYMFoj~$v0K*%(dUI{rVLqiT7NlhQ|9xPoA>t zRz}=_-Q&^vP$BS=98FopZ?^r&6x9Oag#*m+>0p8Qw!1i9qRRq=h|lo`1282Fj|*78 zT{XveRrMFFl$0rZ*P!tnk%aj zK#ZGH)%j}!$;u*5a});*yI^F_KC+B!rvvmV)DbN8upI9{l+#U~rMEBP@J419mU|6z zZ;;UeqtfFl%GK{;Jzdc6+=ZtMke?F-rfI+KeNJIsb|yQG3@%pZ#tZ&uFF!nell0VK zO4~Hx!X~%$bMfVSZX|XmF%15tQ4avW+Z*ymc}*7G54}&PZR_B10ATd}lJT;`z4h+) zYP~SsRLyG%z`94{x>|4e8UURbAXz2!B2D;Q{$5o8>&-TY!9BYO)^1`NnsUBlAbBpj zwSc^{5IQL^kK&Yo!efQ)CrGe%_S^4rioQgbh<81Q2YPLdtHz(*Gp1E1I0l%wjRcBH@DFLAFuZU_W&nepBS0iZ~v z=mzjQFpG&90@Fz0m;CjibT1Q&^9vv#*E5~lMy-ak#T()toNkWw6U8f1JaAR*@f0Xm z%zn0iPsHXt8>W%;sVy`4rqd8c!7%d#*oQzaZ*mv;o)EW-;^2=9H*jXApPWMo_d4W% zre6|JKl^l%Cv9P2;RY-&b|VD!0K27=fNSHRXGK2rscru@vl;qyM@#Zdes=?m--%ruH-Pt<(FWd4Lm)n f7FN>zm9w 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 Gp$PyE8>4{$ diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-field-and-value-when-showempty-is-false.snap.png b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/__tests__/__image_snapshots__/childrenwithage-for-ui-have-to-match-field-and-value-when-showempty-is-false.snap.png new file mode 100644 index 0000000000000000000000000000000000000000..61dc823068af2e5169e31a010598f879da98858a GIT binary patch literal 11938 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 f7FN>zm9w zgP)RiWN!DFi@f?l@pHAbvMmXWzd^9-(>)oqTs56~0R_A}W3$)(#0tvD19AohV%?|t z)XzOs7TGguI=_bL$57!cVB(g8=DHiC_6})0e#W^igf|jG|!f& z?{2S+-n-}mipdjkiUH6X8_`vtk<(@Aj`llKpQ6jqNxqm??Sa-r=D!Niy;f7x8$a=BINi)UIstXU7XsX60CTGHCTsQ21&!q9! zva1{gL&9)WrP;e1m~YMlO#JbcVAz9$j6=~D4(91c5JhP-zeB}06J;Nmm@Vfz8eJ19 zVt`{2%!s%x{v^y$dcBHXeaHz^Ith{?&@t9)pnZF}&DR-n1pHbY3Zw*zQ$$wYOF@{& z)sgw#PAuwa;{-5yD8p~K%8LwsS?Omj901Ps)X0^3IotQ`Q}zcT+oBHxXQxd94rqHB z~)mz+bQONR#~hWcw4`DecM3q8=$sM$W)h=q~dZ&kKsTY13uoh-|SW5R(c;J6Hv zflNZdxpDa+Pkjbnf&TAir$?EeMqbCTcwm7D|1xTaRVQ8;hjfuQd!~rvZ&7B;_W*75 z!GVO)FSGLF6b)vmbvM&U2ejO}`sCqMcRr;yCBZ2QJQuyR#DXYE!nj8SR#KIuT^}=H zN7Mtgl_zQ4vqTuL(D~Ev=F)Swgi-$Hmpp-(LLJx`UmE-cEeiT+E`+dKp5|4QozF># z@%}<<4}HO9vgJA_};SXq5vQw3s^3M0B|yj}PsbzS&xL zj7m>t*Z~(-!h@;K7Y60aErGNI@)9y&TcjLkb$EB4-JD9J@`O>93=5+8PZ_!s@{svxJK?5feX3$>FhQ_*mJ< zG&s#4r%<;mccH4M5fTXZV2$ba;LexphEkYI95g0`MX){MBORcy{wL7dJ5D#0PwRIe zJI~sKi9st?w0llD+^xgd15aENbia%*;S-#X)C5k!RDz}8Lm9Z=A(R1efRacpFNr1D z@wi8jrALEkqVq==JRb*JQG(gR*QLkU_BB;0kvW^o2@G02K8zJ>zK)gldwJQWWpk|s zrz`Sb9$rgBd@4>AUSBK{%emz+e0DA8us-(-`1-9FH1snAp<^BmY_%G`d69byZFlTL zEo@U7^zl0LXbmXC>QS4zCRhVsbDjx7o zGN!#h$uqdc5RhkoNI+D^(;L#S4+%?nV(&2j@?BM?yKD>1B$Z_`{D7f0Gy=Da8cVVO z*76N>&o*`$mv~|MMLAPwNEpe=BfcM=tGLXDOC=Q5!Wwr=tzvYTsJ(YoqT($o*43@k zeh*edUN2Gqb!gvigwO>z_fRY!cFrokl1#u@i5Kt{Cv(0G4zQx`QzzNXe!y!$heKtp z)Ol_}XGI#-;D7L^>9C%cmRthl50h(!zX2L}dUSwJ|H3M49KI=)jgf-va;cw$KZM;>ru z!Bil~A3Rk0LkMEg!O9@@Au-_q4j=$@AxM4j3q}BMB;o&V(Sep*X;uU=j8U^iZm>7K zKM3HUv;7!HY9*YY1vqs$;b|84U!2Aa*419N%tKdkzd*d;Xo9X~aZMm@)9$_0Z)E(e z3FsWUG5`E`j0e3WV7p*`T@;HZ{cN8Y%FYjHrIaH0;#Zj5&K-oLJa;Ozq27-{(c$X% z4qf2ux+(Ycd+{U(0lAN8%7;I?-*;>{59mT$TRq^^pa#Y7X)#(!pN$c8KGT+G7?^%i zjbMhpHxJ%Oo*paIfp8m|YoF-fL%a*HN6-bLiq+NCHE!&Ha)VOn)prm}T?2~VZ8cUD zXBfl|Kdfc|6aB_Hx5PSu^tJXo#wUwDq5*MQeD452?*{yH-eSi6#9WpF@3kkGH(5Z; zHECnxb?4bSIETl*rQ$A;*xQz9E!v1GTP|G+!V-oP1u2Xd18ST=Q6me1ITyX ztku200|NsyF=6T375ik3eN&3WRivP9?IQ4L3V=Yq-{%zy^7lMaMG!G5fx5*a0QOHF z14Hc66u)?5&xfy#jmQBsMKte~Omz|7;MdN|2M? zm9xKie<1wLYSG{#5fy77C78C{EX5;D7-mp4i)+ z4AbYSOv?DJ)SS)>Ek+sSdOfMCs>(x=)Dp+p9YlxkeS8BSguaZI8R|YHpv_vb zL1WHM`1RcHyvmd|ECCIj{LrY3P)3pjwh+#o;E4^8Qaqt9`D8I!lD-E z{x}|kOqrS^GBB%VSrXnpi?u*7vbB#1rEuw2sLFMD_6G?Fh}|2R|3ygz|CjU2kg>GP z>*+VcYb$ON$>K3?1joyS@pHb7z5lDky5ij$_Y}ee*2+xoFnY`G0 zA*cz>=ITERz3b&k;;1V65KRi6l%a^SN!wD3PJrAYCWHt#Czr+F*J+s?kO`#f)w%b} z@c%MU!=q*94NqX4t_HRTqK*c=Q3uwKs=)SuZlYS*Bw)p_1+4hPHj4ZPo*R|G32aJu z&*+>BI(~o)*fOi@)GM3s?Tcu3Im=F#^CAd=d#Wz Gp$PyE8>4{$ literal 0 HcmV?d00001 From 1aff360bc316847082bd656e4e8c54a99c9137b8 Mon Sep 17 00:00:00 2001 From: Joakim Bjerknes Date: Tue, 17 Sep 2024 15:28:52 +0200 Subject: [PATCH 10/37] feat(Value.ArraySelection): add `variant` prop to allow for list layout (#3947) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Anders Co-authored-by: Tobias Høegh --- .../forms/Value/ArraySelection/Examples.tsx | 60 ++++++++++ .../forms/Value/ArraySelection/demos.mdx | 11 +- .../Value/ArraySelection/ArraySelection.tsx | 65 ++++++++++- .../ArraySelection/ArraySelectionDocs.ts | 20 ++++ .../__tests__/ArraySelection.test.tsx | 109 ++++++++++++++++++ 5 files changed, 256 insertions(+), 9 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/ArraySelection/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/ArraySelection/Examples.tsx index 64788d863b0..31023053022 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/ArraySelection/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/ArraySelection/Examples.tsx @@ -104,3 +104,63 @@ export const Inline = () => { ) } + +export const ListVariants = () => { + return ( + + + + + ) +} + +export const ListTypes = () => { + return ( + + + + + + + + + ) +} diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/ArraySelection/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/ArraySelection/demos.mdx index 4e631174d22..08788b9c1c1 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/ArraySelection/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/ArraySelection/demos.mdx @@ -30,10 +30,13 @@ import * as Examples from './Examples' -{/* This is not yet supported */} -{/* ### Field.ArraySelection with path */} -{/* When using the same `path` as on a `Field.ArraySelection`, the title will be used as the displayed value. */} -{/* */} +### List variants + + + +### List types + + ### Field.Option and Field.ArraySelection diff --git a/packages/dnb-eufemia/src/extensions/forms/Value/ArraySelection/ArraySelection.tsx b/packages/dnb-eufemia/src/extensions/forms/Value/ArraySelection/ArraySelection.tsx index f5d4244512e..81f81eb20ae 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Value/ArraySelection/ArraySelection.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Value/ArraySelection/ArraySelection.tsx @@ -7,6 +7,7 @@ import { LOCALE } from '../../../../shared/defaults' import { convertJsxToString } from '../../../../shared/component-helper' import SharedContext, { InternalLocale } from '../../../../shared/Context' import Context from '../../DataContext/Context' +import { Li, Ol, Ul } from '../../../../elements' export type Props = ValueProps> & { /** @@ -14,14 +15,45 @@ export type Props = ValueProps> & { * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/ListFormat */ format?: Intl.ListFormatOptions + /** + * Defines if the value should be displayed in list format or reuglar text format on one line. + * Default: `text` + */ + variant?: 'ol' | 'ul' | 'text' + /** + * Defines the type of list styling used for list variants. Used on conjuction with variant `ol` and `ul`. + * Variant `ol`: `a`, `A`, `i`, `I` and `1`. + * Variant `ul`: `cirlce`, `disc` and `square`. + * Default: `undefined` + */ + listType?: + | 'a' + | 'A' + | 'i' + | 'I' + | '1' + | 'circle' + | 'disc' + | 'square' + | undefined } function ArraySelection(props: Props) { const { locale } = useContext(SharedContext) const { fieldPropsRef } = useContext(Context) || {} - const { path, value, format, className, ...rest } = useValueProps(props) + const { + path, + value, + format, + className, + variant = 'text', + listType, + ...rest + } = useValueProps(props) const list = useMemo(() => { + const isListVariant = variant !== 'text' + if (path) { const data = fieldPropsRef?.current?.[ path + '/arraySelectionData' @@ -29,18 +61,41 @@ function ArraySelection(props: Props) { value: string title: string | React.ReactNode }> - return data?.map?.(({ title }) => convertJsxToString(title)) || value + + return ( + data?.map?.(({ title }, index) => + isListVariant ? ( +
  • {convertJsxToString(title)}
  • + ) : ( + convertJsxToString(title) + ) + ) || value + ) } - return value - }, [fieldPropsRef, path, value]) + return isListVariant + ? value.map((value, index) => ( +
  • {convertJsxToString(value)}
  • + )) + : value + }, [fieldPropsRef, path, value, variant]) + + const listValue = useMemo(() => { + if (variant === 'text') { + return listFormat(list, { locale, format }) + } + + const ListElement = variant.startsWith('ol') ? Ol : Ul + + return {list} + }, [format, list, locale, variant, listType]) return ( - {listFormat(list, { locale, format })} + {listValue} ) } diff --git a/packages/dnb-eufemia/src/extensions/forms/Value/ArraySelection/ArraySelectionDocs.ts b/packages/dnb-eufemia/src/extensions/forms/Value/ArraySelection/ArraySelectionDocs.ts index 1a35eaf12f5..d9fb477657f 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Value/ArraySelection/ArraySelectionDocs.ts +++ b/packages/dnb-eufemia/src/extensions/forms/Value/ArraySelection/ArraySelectionDocs.ts @@ -6,4 +6,24 @@ export const ArraySelectionProperties: PropertiesTableProps = { type: 'Intl.ListFormatOptions', status: 'optional', }, + variant: { + doc: 'Defines if the value should be displayed in list format, or reuglar text format on one line. Defaults to `text`', + type: ['ol', 'ul', 'text'], + status: 'optional', + }, + listType: { + doc: 'Defines the type of list styling used for list variants. Used on conjuction with variant `ol` and `ul`. Variant `ol`: `a`, `A`, `i`, `I` and `1`. Variant `ul`: `cirlce`, `disc` and `square`. Defaults to `undefined`', + type: [ + 'a', + 'A', + 'i', + 'I', + '1', + 'circle', + 'disc', + 'square', + 'undefined', + ], + status: 'optional', + }, } diff --git a/packages/dnb-eufemia/src/extensions/forms/Value/ArraySelection/__tests__/ArraySelection.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Value/ArraySelection/__tests__/ArraySelection.test.tsx index 74fc2292153..4b2510b30ec 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Value/ArraySelection/__tests__/ArraySelection.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Value/ArraySelection/__tests__/ArraySelection.test.tsx @@ -38,6 +38,115 @@ describe('Value.ArraySelection', () => { ).toHaveTextContent('123, 456 eller 789') }) + it('should render different variants', () => { + const { rerender } = render( + + ) + + const valueBlock = document.querySelector( + '.dnb-forms-value-array-selection .dnb-forms-value-block__content' + ) + + const ol = valueBlock.querySelector('.dnb-ol') as HTMLOListElement + + expect(ol).toBeInTheDocument() + expect(ol.children.length).toBe(3) + expect(ol).toContainHTML( + '
  • 123
  • 456
  • 789
  • ' + ) + rerender() + + const ul = valueBlock.querySelector('.dnb-ul') as HTMLUListElement + + expect(ol).not.toBeInTheDocument() + expect(ul).toBeInTheDocument() + expect(ul.children.length).toBe(3) + expect(ul).toContainHTML( + '
  • 123
  • 456
  • 789
  • ' + ) + + rerender( + + ) + + expect(ol).not.toBeInTheDocument() + expect(ul).not.toBeInTheDocument() + expect(valueBlock).toHaveTextContent('123, 456 og 789') + }) + + it('should render different `listTypes`', () => { + const { rerender } = render( + + ) + + const valueBlock = document.querySelector( + '.dnb-forms-value-array-selection .dnb-forms-value-block__content' + ) + + const list = (type: 'ol' | 'ul') => + valueBlock.querySelector(`.dnb-${type}`) + + expect(list('ol')).toHaveAttribute('type', 'a') + + rerender( + + ) + expect(list('ol')).toHaveAttribute('type', 'A') + + rerender( + + ) + expect(list('ol')).toHaveAttribute('type', 'i') + + rerender( + + ) + expect(list('ol')).toHaveAttribute('type', 'I') + + rerender( + + ) + expect(list('ul')).toHaveAttribute('type', 'circle') + + rerender( + + ) + expect(list('ul')).toHaveAttribute('type', 'disc') + + rerender( + + ) + expect(list('ul')).toHaveAttribute('type', 'square') + }) + it('renders label when showEmpty is true', () => { render() expect(document.querySelector('.dnb-form-label')).toHaveTextContent( From 367889b38d498f5721869d57cfd5b7006fa5c339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Wed, 18 Sep 2024 12:15:08 +0200 Subject: [PATCH 11/37] feat(Forms): add `inheritVisibility` to each `Value.*` component (#3948) This PR adds a new property called `inheritVisibility`. When it is used, it will look for a field with the same path. If it exists, it will sync its visibility to the mounting state of the field. A following up PR will make the same, but for the submit data. - [x] We should test this on the ChildrenWithAge block --------- Co-authored-by: Joakim Bjerknes Co-authored-by: Anders --- .../uilib/extensions/forms/Value/Examples.tsx | 44 +- .../uilib/extensions/forms/Value/demos.mdx | 4 + .../uilib/extensions/forms/Value/info.mdx | 28 +- .../extensions/forms/getting-started.mdx | 8 + .../extensions/forms/DataContext/Context.ts | 22 +- .../forms/DataContext/Provider/Provider.tsx | 15 +- .../forms/Form/Visibility/Visibility.tsx | 16 +- .../Form/Visibility/VisibilityContext.ts | 9 + .../forms/Value/Selection/Selection.tsx | 2 +- .../Value/String/__tests__/String.test.tsx | 163 +------ .../src/extensions/forms/Value/ValueDocs.ts | 5 + .../forms/ValueBlock/ValueBlock.tsx | 16 +- .../__tests__/WizardContainer.test.tsx | 42 ++ .../hooks/__tests__/useFieldProps.test.tsx | 187 ++++++- .../hooks/__tests__/useValueProps.test.tsx | 461 +++++++++++++++++- .../extensions/forms/hooks/useFieldProps.ts | 51 ++ .../extensions/forms/hooks/useValueProps.ts | 69 ++- .../FieldAndValueVisibility.stories.tsx | 138 ++++++ .../dnb-eufemia/src/extensions/forms/types.ts | 5 + 19 files changed, 1079 insertions(+), 206 deletions(-) create mode 100644 packages/dnb-eufemia/src/extensions/forms/Form/Visibility/VisibilityContext.ts create mode 100644 packages/dnb-eufemia/src/extensions/forms/stories/FieldAndValueVisibility.stories.tsx diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/Examples.tsx index 8ffaf2bc8b8..ed2c021bc01 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/Examples.tsx @@ -1,6 +1,7 @@ import React from 'react' import ComponentBox from '../../../../../shared/tags/ComponentBox' -import { Form, Value } from '@dnb/eufemia/src/extensions/forms' +import { Field, Form, Value } from '@dnb/eufemia/src/extensions/forms' +import { Flex } from '@dnb/eufemia/src' export const SummaryList = () => { return ( @@ -31,8 +32,45 @@ export const InheritLabel = () => { return ( - - + + + + + + + ) +} + +export const InheritVisibility = () => { + return ( + + + + + + + + + + + + + + ) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/demos.mdx index baf4d4fc907..53c452d99d1 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/demos.mdx @@ -17,6 +17,10 @@ import * as Examples from './Examples' +### Inherit visibility from fields + + + ### Inherit label diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/info.mdx index 328d84a42fc..db7477d8a0a 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/info.mdx @@ -26,9 +26,35 @@ You can also combine `Value.*` components together by using the value [Compositi +## Inherit visibility from fields based on path + +User-entered data is always stored internally in the data context, even when a [Field](/uilib/extensions/forms/all-fields/) is temporarily shown or hidden (mounted/unmounted). + +By default, `Value.*` components will render the value regardless of the field's visibility. + +To make the visibility of a `Value.*` component match the field with the same path, use `inheritVisibility={true}`: + +```tsx +import { Form, Field, Value } from '@dnb/eufemia/extensions/forms' + +const MyForm = () => { + return ( + + + + + + + + ) +} +``` + +It's recommended to use [Form.Visibility](/uilib/extensions/forms/Form/Visibility/) because it can animate and describes the UI in a clear, declarative way. However, `inheritVisibility` will also work with other methods, such as React's `useState` hook. + ## Inherit labels from fields to values -You can use the `inheritLabel={true}` prop to inherit the label from the field with the same path. +You can use `inheritLabel={true}` to inherit the label from the field with the same path. ```tsx import { Form, Field, Value } from '@dnb/eufemia/extensions/forms' diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx index cb782599759..fa18a0d47b5 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx @@ -237,6 +237,14 @@ In short, field components are interactive components that the user can interact Beside the interactive [Field](/uilib/extensions/forms/all-fields/) components, there is also the static [Value](/uilib/extensions/forms/Value/) components. Use these to show summaries or read-only parts of your application with benefits such as linking to source data and standardized formatting based on the type of data to be displayed. +#### Inherit visibility from fields + +User entered data will always be stored internally in the data context, even if a [Field](/uilib/extensions/forms/all-fields/) is temporarily shown (mounted/unmounted). + +`Value.*` components will render the value regardless of the visibility of the field. + +You can use the `inheritVisibility` property on the [Value.\*](/uilib/extensions/forms/Value/) components to inherit the visibility from the field with the same path. + ### Async form behavior This feature allows you to perform asynchronous operations such as fetching data from an API – without additional state management. diff --git a/packages/dnb-eufemia/src/extensions/forms/DataContext/Context.ts b/packages/dnb-eufemia/src/extensions/forms/DataContext/Context.ts index 7f496c12bc6..421fabdb2dc 100644 --- a/packages/dnb-eufemia/src/extensions/forms/DataContext/Context.ts +++ b/packages/dnb-eufemia/src/extensions/forms/DataContext/Context.ts @@ -19,16 +19,23 @@ type HandleSubmitProps = { formElement?: HTMLFormElement } -export type MountOptions = { +export type MountState = { + isPreMounted?: boolean isMounted?: boolean + isVisible?: boolean + wasStepChange?: boolean } export type EventListenerCall = { path?: Path - type?: 'onSubmit' | 'onPathChange' + type?: 'onSubmit' | 'onPathChange' | 'onMount' callback: (params?: { value: unknown }) => void | Promise } +export type MutateDataHandler = ( + data: Data, + mutate: TransformData +) => Partial export type FilterDataHandler = ( data: Data, filter: FilterData @@ -58,6 +65,7 @@ export type FilterDataPathObject = Record< export type FilterData = | FilterDataPathObject | FilterDataHandlerCallback +export type VisibleData = Partial export type TransformData = FilterDataHandlerCallback export type HandleSubmitCallback = ({ preventSubmit, @@ -86,10 +94,10 @@ export interface ContextState { updateDataValue: (path: Path, value: any) => void setData: (data: any) => void clearData?: () => void - mutateDataHandler?: (data: any, mutate: TransformData) => any - filterDataHandler?: (data: any, filter: FilterData) => any + mutateDataHandler?: MutateDataHandler + filterDataHandler?: FilterDataHandler validateData: () => void - handleSubmit: (props?: HandleSubmitProps) => any + handleSubmit: (props?: HandleSubmitProps) => void scrollToTop: () => void setShowAllErrors: (showAllErrors: boolean) => void hasErrors: () => boolean @@ -97,7 +105,7 @@ export interface ContextState { hasFieldError: (path: Path) => boolean setFieldState: (path: Path, fieldState: SubmitState) => void setFieldError: (path: Path, error: Error | FormError) => void - setMountedFieldState: (path: Path, options: MountOptions) => void + setMountedFieldState: (path: Path, options: MountState) => void setFormState?: (state: SubmitState) => void setSubmitState?: (state: EventStateObject) => void addOnChangeHandler?: (callback: OnChange) => void @@ -126,7 +134,7 @@ export interface ContextState { setHandleSubmit?: (callback: HandleSubmitCallback) => void fieldPropsRef?: React.MutableRefObject> valuePropsRef?: React.MutableRefObject> - mountedFieldsRef?: React.MutableRefObject> + mountedFieldsRef?: React.MutableRefObject> showAllErrors: boolean hasVisibleError: boolean formState: SubmitState diff --git a/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/Provider.tsx b/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/Provider.tsx index e2fa7e90fb1..8142dbfa753 100644 --- a/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/Provider.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/Provider.tsx @@ -40,7 +40,7 @@ import Context, { FilterData, FilterDataHandler, HandleSubmitCallback, - MountOptions, + MountState, TransformData, } from '../Context' @@ -829,11 +829,18 @@ export default function Provider( // - Mounted fields const setMountedFieldState = useCallback( - (path: Path, options: MountOptions) => { + (path: Path, state: MountState) => { if (!mountedFieldsRef.current[path]) { - mountedFieldsRef.current[path] = { ...options } + mountedFieldsRef.current[path] = { ...state } } else { - Object.assign(mountedFieldsRef.current[path], options) + Object.assign(mountedFieldsRef.current[path], state) + } + + for (const itm of fieldEventListenersRef.current) { + if (itm.type === 'onMount' && itm.path === path) { + const { callback } = itm + callback() + } } }, [] diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/Visibility.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/Visibility.tsx index 2472eeb7dfd..f9b8cbca37e 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/Visibility.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/Visibility.tsx @@ -11,6 +11,7 @@ import useVisibility from './useVisibility' import type { Path, UseFieldProps } from '../../types' import type { DataAttributes } from '../../hooks/useFieldProps' import { FilterData } from '../../DataContext' +import VisibilityContext from './VisibilityContext' export type VisibleWhen = | { @@ -120,6 +121,15 @@ function Visibility({ filterData, }) const open = check() + const content = ( + + {children} + + ) if (animate) { const props = !open ? fieldPropsWhenHidden : null @@ -132,7 +142,7 @@ function Visibility({ compensateForGap={compensateForGap} {...rest} > - {children} + {content} ) } @@ -141,12 +151,12 @@ function Visibility({ const props = !open ? fieldPropsWhenHidden : null return ( ) } - return <>{open ? children : null} + return <>{open ? content : null} } Visibility._supportsSpacingProps = 'children' diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/VisibilityContext.ts b/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/VisibilityContext.ts new file mode 100644 index 00000000000..a2876ed6e02 --- /dev/null +++ b/packages/dnb-eufemia/src/extensions/forms/Form/Visibility/VisibilityContext.ts @@ -0,0 +1,9 @@ +import { createContext } from 'react' + +type VisibilityContext = { + isVisible?: boolean +} + +const VisibilityContext = createContext(null) + +export default VisibilityContext diff --git a/packages/dnb-eufemia/src/extensions/forms/Value/Selection/Selection.tsx b/packages/dnb-eufemia/src/extensions/forms/Value/Selection/Selection.tsx index 55fecb08d48..d30f39583d5 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Value/Selection/Selection.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Value/Selection/Selection.tsx @@ -37,7 +37,7 @@ function Selection(props: Props) { return value }, [dataPath, fieldPropsRef, getValueByPath, path, value]) - return + return } Selection._supportsSpacingProps = true diff --git a/packages/dnb-eufemia/src/extensions/forms/Value/String/__tests__/String.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Value/String/__tests__/String.test.tsx index ed1b6499381..5a288d3c89b 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Value/String/__tests__/String.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Value/String/__tests__/String.test.tsx @@ -1,10 +1,6 @@ import React from 'react' import { screen, render } from '@testing-library/react' -import { Field, Form, Value, Wizard } from '../../..' -import userEvent from '@testing-library/user-event' -import nbNO from '../../../constants/locales/nb-NO' - -const nb = nbNO['nb-NO'] +import { Value } from '../../..' describe('Value.String', () => { it('renders value', () => { @@ -41,163 +37,6 @@ describe('Value.String', () => { expect(screen.getByText('The label')).toBeInTheDocument() }) - describe('inheritLabel', () => { - it('renders label from field with same path', () => { - render( - - - - - ) - expect( - document.querySelector('.dnb-forms-field-string') - ).toHaveTextContent('The label') - expect( - document.querySelector('.dnb-forms-value-string') - ).toHaveTextContent('The label') - }) - - it('should only show optional label on the field label if required={false}', () => { - render( - - - - - ) - expect( - document.querySelector('.dnb-forms-field-string') - ).toHaveTextContent(`The label ${nb.Field.optionalLabelSuffix}`) - expect( - document.querySelector('.dnb-forms-value-string') - ).toHaveTextContent('The label') - expect( - document.querySelector('.dnb-forms-value-string') - ).not.toHaveTextContent(nb.Field.optionalLabelSuffix) - }) - - it('should not use label from field with same path when label is false', () => { - render( - - - - - ) - expect( - document.querySelector('.dnb-forms-field-string') - ).toHaveTextContent('The label') - expect( - document.querySelector('.dnb-forms-value-string') - ).not.toHaveTextContent('The label') - }) - - it('should render different label from field with same path', () => { - render( - - - - - ) - expect( - document.querySelector('.dnb-forms-field-string') - ).toHaveTextContent('A field') - expect( - document.querySelector('.dnb-forms-value-string') - ).toHaveTextContent('A value') - }) - - it('renders label from field with same path inside a Section', () => { - render( - - - - - - - ) - expect( - document.querySelector('.dnb-forms-field-string') - ).toHaveTextContent('The label') - expect( - document.querySelector('.dnb-forms-value-string') - ).toHaveTextContent('The label') - }) - - it('renders label from field with same path inside a Wizard', async () => { - render( - - - - - - - - - - - - ) - - expect( - document.querySelector('.dnb-forms-field-string') - ).toHaveTextContent('The label') - - await userEvent.click( - document.querySelector('.dnb-forms-next-button') - ) - - expect( - document.querySelector('.dnb-forms-value-string') - ).toHaveTextContent('The label') - }) - - it('should not inherit labelSuffix in value label', () => { - render( - - - - - ) - - expect(document.querySelector('label').textContent).toBe( - `The label${' '}${nb.Field.optionalLabelSuffix}` - ) - expect( - document.querySelector('.dnb-forms-value-string').textContent - ).toBe('The label') - }) - }) - it('renders default class', () => { render() expect( diff --git a/packages/dnb-eufemia/src/extensions/forms/Value/ValueDocs.ts b/packages/dnb-eufemia/src/extensions/forms/Value/ValueDocs.ts index 3940aa1fe3f..d04fdf59e49 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Value/ValueDocs.ts +++ b/packages/dnb-eufemia/src/extensions/forms/Value/ValueDocs.ts @@ -21,6 +21,11 @@ export const ValueProperties: PropertiesTableProps = { type: 'boolean', status: 'optional', }, + inheritVisibility: { + doc: 'Use `true` to inherit the visibility from a field with the same path. You can find more info in the [Value section](/uilib/extensions/forms/Value/#inherit-visibility-from-fields-based-on-path).', + type: 'boolean', + status: 'optional', + }, showEmpty: { doc: 'Shows the value even if it is empty.', type: 'boolean', diff --git a/packages/dnb-eufemia/src/extensions/forms/ValueBlock/ValueBlock.tsx b/packages/dnb-eufemia/src/extensions/forms/ValueBlock/ValueBlock.tsx index b9070fff69b..c2b56c6a2b2 100644 --- a/packages/dnb-eufemia/src/extensions/forms/ValueBlock/ValueBlock.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/ValueBlock/ValueBlock.tsx @@ -37,9 +37,8 @@ export type Props = Omit, 'value'> & { function ValueBlock(props: Props) { const summaryListContext = useContext(SummaryListContext) const valueBlockContext = useContext(ValueBlockContext) - const dataContext = useContext(DataContext) - const iterateItemContext = useContext(IterateElementContext) - const { index: iterateIndex } = iterateItemContext ?? {} + const { prerenderFieldProps } = useContext(DataContext) || {} + const { index: iterateIndex } = useContext(IterateElementContext) || {} const { className, @@ -69,13 +68,14 @@ function ValueBlock(props: Props) { const ref = useRef(null) useNotInSummaryList(valueBlockContext?.composition ? null : ref, label) - if ( + const hide = + prerenderFieldProps || ((children === undefined || children === null || children === false) && !showEmpty && - !placeholder) || - dataContext?.prerenderFieldProps - ) { - return null + !placeholder) + + if (hide) { + return <> } let content = null 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 34eb1fd3834..61f2106aaf7 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 @@ -520,6 +520,48 @@ describe('Wizard.Container', () => { expect(output()).toHaveTextContent('Step 3') }) + it('should keep current step on rerender', async () => { + const onStepChange = jest.fn() + + render( + + + + Step 1 + + + + + + Step 2 + + + + ) + + expect(output()).toHaveTextContent('Step 1') + + await userEvent.click(document.querySelector('#not-submit')) + + expect(output()).toHaveTextContent('Step 1') + expect(onStepChange).toHaveBeenCalledTimes(0) + + await userEvent.click(document.querySelector('#submit')) + + expect(output()).toHaveTextContent('Step 2') + expect(onStepChange).toHaveBeenCalledTimes(1) + }) + describe('dynamic steps', () => { it('should not render inactive steps', () => { render( diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx index 141aacf7939..5fc239ad58a 100644 --- a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx @@ -9,7 +9,8 @@ import { } from '@testing-library/react' import userEvent from '@testing-library/user-event' import useFieldProps from '../useFieldProps' -import { Provider } from '../../DataContext' +import { Context, ContextState, Provider } from '../../DataContext' +import WizardContext from '../../Wizard/Context' import Field, { FieldBlock, Form, @@ -3919,4 +3920,188 @@ describe('useFieldProps', () => { }) }) }) + + describe('setMountedFieldState', () => { + it('should mount and unmount when the field is removed from the DOM', () => { + const setMountedFieldState = jest.fn() + + const { unmount } = renderHook((props) => useFieldProps(props), { + initialProps: { + path: '/foo', + }, + wrapper: ({ children }) => { + const value = { + setMountedFieldState, + } as unknown as ContextState + return ( + {children} + ) + }, + }) + + expect(setMountedFieldState).toHaveBeenCalledTimes(2) + expect(setMountedFieldState).toHaveBeenNthCalledWith(1, '/foo', { + isPreMounted: true, + }) + expect(setMountedFieldState).toHaveBeenNthCalledWith(2, '/foo', { + isMounted: true, + }) + + unmount() + + expect(setMountedFieldState).toHaveBeenCalledTimes(3) + expect(setMountedFieldState).toHaveBeenLastCalledWith('/foo', { + isMounted: false, + isPreMounted: false, + }) + }) + + it('should set isVisible when within a visibility context', () => { + const setMountedFieldState = jest.fn() + + const { unmount } = renderHook((props) => useFieldProps(props), { + initialProps: { + path: '/foo', + }, + wrapper: ({ children }) => { + const value = { + setMountedFieldState, + } as unknown as ContextState + return ( + + {children} + + ) + }, + }) + + expect(setMountedFieldState).toHaveBeenCalledTimes(3) + expect(setMountedFieldState).toHaveBeenNthCalledWith(1, '/foo', { + isPreMounted: true, + }) + expect(setMountedFieldState).toHaveBeenNthCalledWith(2, '/foo', { + isVisible: true, + }) + expect(setMountedFieldState).toHaveBeenNthCalledWith(3, '/foo', { + isMounted: true, + }) + + unmount() + + expect(setMountedFieldState).toHaveBeenCalledTimes(4) + expect(setMountedFieldState).toHaveBeenNthCalledWith(4, '/foo', { + isMounted: false, + isPreMounted: false, + }) + }) + + it('should set isVisible when within a visibility context with a negative visibility', () => { + const setMountedFieldState = jest.fn() + + const { unmount } = renderHook((props) => useFieldProps(props), { + initialProps: { + path: '/foo', + }, + wrapper: ({ children }) => { + const value = { + setMountedFieldState, + } as unknown as ContextState + return ( + + + {children} + + + ) + }, + }) + + expect(setMountedFieldState).toHaveBeenCalledTimes(3) + expect(setMountedFieldState).toHaveBeenNthCalledWith(1, '/foo', { + isPreMounted: true, + }) + expect(setMountedFieldState).toHaveBeenNthCalledWith(2, '/foo', { + isVisible: false, + }) + expect(setMountedFieldState).toHaveBeenNthCalledWith(3, '/foo', { + isMounted: true, + }) + + unmount() + + expect(setMountedFieldState).toHaveBeenCalledTimes(4) + expect(setMountedFieldState).toHaveBeenNthCalledWith(4, '/foo', { + isMounted: false, + isPreMounted: false, + }) + }) + + it('should set isMounted to true when Wizard step has changed', () => { + const originalConsoleLog = console.log + const log = jest + .spyOn(console, 'log') + .mockImplementation((...message) => { + if (!message[0].includes('Eufemia')) { + originalConsoleLog(...message) + } + }) + const setMountedFieldState = jest.fn() + + let activeIndex = 0 + const { rerender, unmount } = renderHook( + (props) => useFieldProps(props), + { + initialProps: { path: '/foo' }, + wrapper: ({ children }) => { + const value = { + setMountedFieldState, + } as unknown as ContextState + activeIndex++ + return ( + + + {children} + + + ) + }, + } + ) + + expect(setMountedFieldState).toHaveBeenCalledTimes(2) + expect(setMountedFieldState).toHaveBeenNthCalledWith(1, '/foo', { + isPreMounted: true, + }) + expect(setMountedFieldState).toHaveBeenNthCalledWith(2, '/foo', { + isMounted: true, + }) + + rerender({ path: '/bar' }) + + expect(setMountedFieldState).toHaveBeenCalledTimes(5) + expect(setMountedFieldState).toHaveBeenNthCalledWith(3, '/bar', { + isPreMounted: true, + }) + expect(setMountedFieldState).toHaveBeenNthCalledWith(4, '/foo', { + isMounted: false, + isPreMounted: false, + }) + expect(setMountedFieldState).toHaveBeenNthCalledWith(5, '/bar', { + isMounted: true, + }) + + unmount() + + expect(setMountedFieldState).toHaveBeenCalledTimes(6) + expect(setMountedFieldState).toHaveBeenNthCalledWith(2, '/foo', { + isMounted: true, + }) + + log.mockRestore() + }) + }) }) diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useValueProps.test.tsx b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useValueProps.test.tsx index d6d280dab01..b6f71a8f03d 100644 --- a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useValueProps.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useValueProps.test.tsx @@ -1,7 +1,13 @@ import React from 'react' -import { renderHook } from '@testing-library/react' +import { render, renderHook, waitFor } from '@testing-library/react' +import userEvent from '@testing-library/user-event' import useValueProps from '../useValueProps' import Provider from '../../DataContext/Provider' +import { Field, Form, Value, Wizard } from '../..' + +import nbNO from '../../constants/locales/nb-NO' + +const nb = nbNO['nb-NO'] describe('useValueProps', () => { it('should call use "transformIn" to prepare value', () => { @@ -187,4 +193,457 @@ describe('useValueProps', () => { expect(result.current.value).toBe(2) }) + + describe('inheritLabel', () => { + it('renders label from field with same path', () => { + render( + + + + + ) + expect( + document.querySelector('.dnb-forms-field-string') + ).toHaveTextContent('The label') + expect( + document.querySelector('.dnb-forms-value-string') + ).toHaveTextContent('The label') + }) + + it('renders label from field with same path when rendered before the field', async () => { + render( + + + + + ) + + expect( + document.querySelector('.dnb-forms-field-string') + ).toHaveTextContent('The label') + + await waitFor(() => { + expect( + document.querySelector('.dnb-forms-value-string') + ).toHaveTextContent('The label') + }) + + expect( + document.querySelector('.dnb-forms-value-string') + .nextElementSibling.className + ).toContain('dnb-forms-field-string') + }) + + it('should only show optional label on the field label if required={false}', () => { + render( + + + + + ) + expect( + document.querySelector('.dnb-forms-field-string') + ).toHaveTextContent(`The label ${nb.Field.optionalLabelSuffix}`) + expect( + document.querySelector('.dnb-forms-value-string') + ).toHaveTextContent('The label') + expect( + document.querySelector('.dnb-forms-value-string') + ).not.toHaveTextContent(nb.Field.optionalLabelSuffix) + }) + + it('should not use label from field with same path when label is false', () => { + render( + + + + + ) + expect( + document.querySelector('.dnb-forms-field-string') + ).toHaveTextContent('The label') + expect( + document.querySelector('.dnb-forms-value-string') + ).not.toHaveTextContent('The label') + }) + + it('should render different label from field with same path', () => { + render( + + + + + ) + expect( + document.querySelector('.dnb-forms-field-string') + ).toHaveTextContent('A field') + expect( + document.querySelector('.dnb-forms-value-string') + ).toHaveTextContent('A value') + }) + + it('renders label from field with same path inside a Section', () => { + render( + + + + + + + ) + expect( + document.querySelector('.dnb-forms-field-string') + ).toHaveTextContent('The label') + expect( + document.querySelector('.dnb-forms-value-string') + ).toHaveTextContent('The label') + }) + + it('renders label from field with same path inside a Wizard', async () => { + render( + + + + + + + + + + + + ) + + expect( + document.querySelector('.dnb-forms-field-string') + ).toHaveTextContent('The label') + + await userEvent.click( + document.querySelector('.dnb-forms-next-button') + ) + + expect( + document.querySelector('.dnb-forms-value-string') + ).toHaveTextContent('The label') + }) + + it('should not inherit labelSuffix in value label', () => { + render( + + + + + ) + + expect(document.querySelector('label').textContent).toBe( + `The label${' '}${nb.Field.optionalLabelSuffix}` + ) + expect( + document.querySelector('.dnb-forms-value-string').textContent + ).toBe('The label') + }) + }) + + describe('inheritVisibility', () => { + it('renders value when visibility of field is initially true', () => { + render( + + + + + + + + + + + + + + + ) + + const [valueElementBefore, valueElementAfter] = Array.from( + document.querySelectorAll('.dnb-forms-value-string') + ) + + expect( + valueElementBefore.querySelector('.dnb-forms-value-block__content') + ).toHaveTextContent('Foo') + expect( + valueElementAfter.querySelector('.dnb-forms-value-block__content') + ).toHaveTextContent('Foo') + }) + + it('renders value visibility when field visibility changes', async () => { + render( + + + + + + + + + + + + + + + ) + + { + const [valueElementBefore, valueElementAfter] = Array.from( + document.querySelectorAll('.dnb-forms-value-string') + ) + + expect(valueElementBefore).toBeInTheDocument() + expect(valueElementAfter).toBeInTheDocument() + } + + await userEvent.click(document.querySelector('button')) + + { + const [valueElementBefore, valueElementAfter] = Array.from( + document.querySelectorAll('.dnb-forms-value-string') + ) + + expect(valueElementBefore).toBeInTheDocument() + expect(valueElementAfter).toBeInTheDocument() + + await waitFor(() => { + expect(valueElementBefore).not.toBeInTheDocument() + expect(valueElementAfter).not.toBeInTheDocument() + }) + } + + await userEvent.click(document.querySelector('button')) + + await waitFor(() => { + const [valueElementBefore, valueElementAfter] = Array.from( + document.querySelectorAll('.dnb-forms-value-string') + ) + expect(valueElementBefore).toBeInTheDocument() + expect(valueElementAfter).toBeInTheDocument() + }) + }) + + it('renders value visibility changes without Form.Visibility', async () => { + const MyForm = () => { + const [count, increment] = React.useReducer( + (state) => state + 1, + 1 + ) + + return ( + + + + + + {count % 2 ? ( + + + + + ) : null} + + + + ) + } + render() + + { + const [valueElementBefore, valueElementAfter] = Array.from( + document.querySelectorAll('.dnb-forms-value-string') + ) + + expect(valueElementBefore).toBeInTheDocument() + expect(valueElementAfter).toBeInTheDocument() + } + + await userEvent.click(document.querySelector('button')) + + { + const [valueElementBefore, valueElementAfter] = Array.from( + document.querySelectorAll('.dnb-forms-value-string') + ) + + expect(valueElementBefore).toBeInTheDocument() + expect(valueElementAfter).toBeInTheDocument() + + await waitFor(() => { + expect(valueElementBefore).not.toBeInTheDocument() + expect(valueElementAfter).not.toBeInTheDocument() + }) + } + + await userEvent.click(document.querySelector('button')) + + await waitFor(() => { + const [valueElementBefore, valueElementAfter] = Array.from( + document.querySelectorAll('.dnb-forms-value-string') + ) + expect(valueElementBefore).toBeInTheDocument() + expect(valueElementAfter).toBeInTheDocument() + }) + }) + + it('renders value visibility inside a Wizard', async () => { + render( + + + + Step 1 + + + + + + + + + + + + + + + Step 2 + + + + + + + ) + + const output = () => document.querySelector('output') + + expect(output()).toHaveTextContent('Step 1') + + await userEvent.click( + document.querySelector('.dnb-forms-next-button') + ) + expect(output()).toHaveTextContent('Step 2') + + { + const valueElements = document.querySelectorAll( + '.dnb-forms-value-string' + ) + expect(valueElements).toHaveLength(1) + expect(valueElements[0]).toBeInTheDocument() + expect(valueElements[0]).toHaveTextContent('Foo') + } + + await userEvent.click( + document.querySelector('.dnb-forms-previous-button') + ) + expect(output()).toHaveTextContent('Step 1') + + // Change the visibility + await userEvent.click( + document.querySelector('.dnb-toggle-button button') + ) + + await userEvent.click( + document.querySelector('.dnb-forms-next-button') + ) + expect(output()).toHaveTextContent('Step 2') + + { + const valueElements = document.querySelectorAll( + '.dnb-forms-value-string' + ) + expect(valueElements).toHaveLength(0) + } + + await userEvent.click( + document.querySelector('.dnb-forms-previous-button') + ) + expect(output()).toHaveTextContent('Step 1') + + // Change the visibility + await userEvent.click( + document.querySelector('.dnb-toggle-button button') + ) + + await userEvent.click( + document.querySelector('.dnb-forms-next-button') + ) + expect(output()).toHaveTextContent('Step 2') + + { + const valueElements = document.querySelectorAll( + '.dnb-forms-value-string' + ) + expect(valueElements).toHaveLength(1) + expect(valueElements[0]).toBeInTheDocument() + expect(valueElements[0]).toHaveTextContent('Foo') + } + }) + }) }) diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts index f93746b4108..a715a42dcab 100644 --- a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts +++ b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts @@ -31,6 +31,8 @@ import FieldBlockContext from '../FieldBlock/FieldBlockContext' import IterateElementContext from '../Iterate/IterateItemContext' import SectionContext from '../Form/Section/SectionContext' import FieldBoundaryContext from '../DataContext/FieldBoundary/FieldBoundaryContext' +import VisibilityContext from '../Form/Visibility/VisibilityContext' +import WizardContext from '../Wizard/Context' import useProcessManager from './useProcessManager' import usePath from './usePath' import { @@ -140,6 +142,9 @@ export default function useFieldProps( const iterateItemContext = useContext(IterateElementContext) const sectionContext = useContext(SectionContext) const fieldBoundaryContext = useContext(FieldBoundaryContext) + const wizardContext = useContext(WizardContext) + const { isVisible } = useContext(VisibilityContext) || {} + const translation = useTranslation() const transformers = useRef({ @@ -1403,6 +1408,46 @@ export default function useFieldProps( // Put props into the surrounding data context as early as possible setPropsDataContext?.(identifier, props) + const { activeIndex, activeIndexRef } = wizardContext || {} + const activeIndexTmpRef = useRef(activeIndex) + useEffect(() => { + activeIndexTmpRef.current = activeIndex + }, [activeIndex]) // We want to watch for step changes + + useMemo(() => { + setMountedFieldStateDataContext(identifier, { + isPreMounted: true, + }) + + if (typeof isVisible === 'boolean') { + setMountedFieldStateDataContext(identifier, { isVisible }) + } + }, [setMountedFieldStateDataContext, identifier, isVisible]) + + useEffect(() => { + if (typeof activeIndexRef?.current === 'number') { + setMountedFieldStateDataContext(identifier, { + wasStepChange: false, + }) + + return () => { + const wasStepChange = + typeof activeIndex === 'number' && + // eslint-disable-next-line react-hooks/exhaustive-deps + activeIndexRef.current !== activeIndexTmpRef.current + + setMountedFieldStateDataContext(identifier, { + wasStepChange, + }) + } + } + }, [ + activeIndex, + activeIndexRef, + identifier, + setMountedFieldStateDataContext, + ]) + useEffect(() => { // Mount procedure. setMountedFieldStateDataContext(identifier, { @@ -1413,7 +1458,13 @@ export default function useFieldProps( return () => { setMountedFieldStateDataContext(identifier, { isMounted: false, + isPreMounted: false, }) + } + }, [identifier, setMountedFieldStateDataContext]) + + useEffect(() => { + return () => { setFieldErrorDataContext?.(identifier, undefined) setFieldErrorBoundary?.(identifier, undefined) localErrorRef.current = undefined diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/useValueProps.ts b/packages/dnb-eufemia/src/extensions/forms/hooks/useValueProps.ts index a566549ca88..553ba2c9f9e 100644 --- a/packages/dnb-eufemia/src/extensions/forms/hooks/useValueProps.ts +++ b/packages/dnb-eufemia/src/extensions/forms/hooks/useValueProps.ts @@ -1,5 +1,11 @@ -import { useContext, useRef } from 'react' -import { ValueProps } from '../types' +import { + useCallback, + useContext, + useEffect, + useReducer, + useRef, +} from 'react' +import { Path, ValueProps } from '../types' import useExternalValue from './useExternalValue' import usePath from './usePath' import DataContext from '../DataContext/Context' @@ -10,12 +16,15 @@ export default function useValueProps< Value = unknown, Props extends ValueProps = ValueProps, >(props: Props): Props & ValueProps { + const [, forceUpdate] = useReducer(() => ({}), {}) + const { path: pathProp, + value: valueProp, itemPath, - value, defaultValue, inheritLabel, + inheritVisibility, transformIn = (value: Value) => value, toInput = (value: Value) => value, fromExternal = (value: Value) => value, @@ -33,22 +42,52 @@ export default function useValueProps< useExternalValue({ path, itemPath, - value, + value: valueProp, transformers, }) ?? defaultValue - const dataContext = useContext(DataContext) - dataContext?.setValueProps?.(path, props) + const { + fieldPropsRef, + mountedFieldsRef, + setValueProps, + setFieldEventListener, + } = useContext(DataContext) || {} + setValueProps?.(path, props) + + useEffect(() => { + if (inheritLabel || inheritVisibility) { + setFieldEventListener?.(path, 'onMount', () => { + // This is needed to make values, rendered before the field, to get the correct visibility state + requestAnimationFrame(forceUpdate) + }) + } + }, [setFieldEventListener, path, inheritVisibility, inheritLabel]) + + const shouldBeVisible = useCallback( + (path: Path): boolean => { + const item = mountedFieldsRef?.current?.[path] - const label = inheritLabel - ? dataContext?.fieldPropsRef?.current?.[path]?.label + if (!item || !inheritVisibility) { + return true + } + + return ( + item.isVisible !== false && + (item.isPreMounted !== false || item.wasStepChange === true) + ) + }, + [inheritVisibility, mountedFieldsRef] + ) + + const value = shouldBeVisible(path) + ? transformers.current.transformIn( + transformers.current.toInput(externalValue) + ) : undefined - return { - ...props, - label: props.label ?? label, - value: transformers.current.transformIn( - transformers.current.toInput(externalValue) - ), - } + const label = + props.label ?? + (inheritLabel ? fieldPropsRef?.current?.[path]?.label : undefined) + + return { ...props, label, value } } diff --git a/packages/dnb-eufemia/src/extensions/forms/stories/FieldAndValueVisibility.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/stories/FieldAndValueVisibility.stories.tsx new file mode 100644 index 00000000000..21505a11e60 --- /dev/null +++ b/packages/dnb-eufemia/src/extensions/forms/stories/FieldAndValueVisibility.stories.tsx @@ -0,0 +1,138 @@ +import React from 'react' +import { Field, Form, Value, Wizard } from '..' +import { Flex, Card } from '../../../components' + +export default { + title: 'Eufemia/Extensions/Forms/FieldAndValueVisibility', +} + +export const ValueVisibility = () => { + const [count, increment] = React.useReducer((state) => state + 1, 1) + return ( + console.log('onChange', data)} + // onSubmit={(data) => console.log('onSubmit', data)} + > + + + + + + + + + + + + + + + {/* {count % 2 && ( + + + + + )} */} + + + + + + + ) +} + +export const ValueVisibilityInWizard = () => { + const [count, increment] = React.useReducer((state) => state + 1, 1) + return ( + console.log('onChange', data)} + // onSubmit={(data) => console.log('onSubmit', data)} + > + + + + + + + + + + + + + {count % 2 ? ( + + + + + ) : null} + + + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/packages/dnb-eufemia/src/extensions/forms/types.ts b/packages/dnb-eufemia/src/extensions/forms/types.ts index a44ca318a37..179afaa2c36 100644 --- a/packages/dnb-eufemia/src/extensions/forms/types.ts +++ b/packages/dnb-eufemia/src/extensions/forms/types.ts @@ -446,6 +446,11 @@ export interface ValueProps */ inheritLabel?: boolean + /** + * Use `true` to inherit the visibility from a field with the same path. + */ + inheritVisibility?: boolean + /** * Shows the value even if it is empty. */ From 4fb5485a9b950650d7ebbdd59e17a662a22b2ccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20R=C3=B8en?= Date: Wed, 18 Sep 2024 15:31:12 +0200 Subject: [PATCH 12/37] docs: fix markdown link to text counter component --- .../src/docs/uilib/components/input/info.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/input/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/input/info.mdx index 256a8e1ca54..69533437e83 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/components/input/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/components/input/info.mdx @@ -17,7 +17,7 @@ You may consider to use [InputMasked](/uilib/components/input-masked/) for forma ### Accessibility -Please avoid using the `maxlength` attribute when possible, as it may lower good accessibility. You can instead, use the [TextCounter][/uilib/components/fragments/text-counter/] component. +Please avoid using the `maxlength` attribute when possible, as it may lower good accessibility. You can instead, use the [TextCounter](/uilib/components/fragments/text-counter/) component. But you may also consider to use a multiline input with a `characterCounter`: From fb71a48561001e968a555b7e3477354e8f91ab5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Wed, 18 Sep 2024 22:41:02 +0200 Subject: [PATCH 13/37] fix(Forms): ensure `defaultValue` works in React.StrictMode (#3959) This can be experienced e.g. in the ChildrenWithAge block rendered in React.StrictMode. Then non of the default values are selected. Screenshot 2024-09-18 at 22 02 08 --- .../hooks/__tests__/useFieldProps.test.tsx | 25 ++++++++++++++++++- .../extensions/forms/hooks/useFieldProps.ts | 5 ++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx index 5fc239ad58a..833db6c504d 100644 --- a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx @@ -82,6 +82,27 @@ describe('useFieldProps', () => { }) }) + it('should support ReactStrict mode', () => { + const defaultValue = 'include this' + + const { result } = renderHook( + () => useFieldProps({ path: '/foo', defaultValue }), + { + wrapper: ({ children }) => { + return ( + + {children} + + ) + }, + } + ) + + expect(result.current.dataContext.data).toEqual({ + foo: defaultValue, + }) + }) + it('given "defaultValue" should not take precedence over data context value', () => { const givenValue = 'given value' const defaultValue = 'include this' @@ -90,7 +111,9 @@ describe('useFieldProps', () => { () => useFieldProps({ path: '/foo', defaultValue }), { wrapper: (props) => ( - + + + ), } ) diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts index a715a42dcab..4950727f7ed 100644 --- a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts +++ b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts @@ -27,6 +27,7 @@ import FieldPropsContext from '../Form/FieldProps/FieldPropsContext' import { combineDescribedBy, warn } from '../../../shared/component-helper' import useId from '../../../shared/helpers/useId' import useUpdateEffect from '../../../shared/helpers/useUpdateEffect' +import useMountEffect from '../../../shared/helpers/useMountEffect' import FieldBlockContext from '../FieldBlock/FieldBlockContext' import IterateElementContext from '../Iterate/IterateItemContext' import SectionContext from '../Form/Section/SectionContext' @@ -200,6 +201,10 @@ export default function useFieldProps( }) const defaultValueRef = useRef(defaultValue) + useMountEffect(() => { + // To support ReactStrict mode, we also need to add it from inside a useEffect + defaultValueRef.current = defaultValue + }) const externalValue = useExternalValue({ path, From db24256211116d8f7bc65e88913575eede07d6c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Thu, 19 Sep 2024 06:35:12 +0200 Subject: [PATCH 14/37] feat(Forms): add `reduceToVisibleFields` to the `useData` hook and Form.Handler `onSubmit` (#3957) This PR adds a new method that lets a dev remove all data which is not given by a mounted/visible field. Quick example: ```tsx { const myData = reduceToVisibleFields(data) }} > ... ``` --------- Co-authored-by: Anders --- .../forms/Form/Handler/Examples.tsx | 37 ++ .../extensions/forms/Form/Handler/demos.mdx | 20 +- .../extensions/forms/Form/Handler/info.mdx | 51 ++- .../extensions/forms/Form/getData/info.mdx | 40 +- .../forms/Form/useData/Examples.tsx | 57 ++- .../extensions/forms/Form/useData/demos.mdx | 6 + .../extensions/forms/Form/useData/info.mdx | 72 +++- .../extensions/forms/getting-started.mdx | 34 +- .../extensions/forms/DataContext/Context.ts | 9 + .../forms/DataContext/Provider/Provider.tsx | 61 ++- .../DataContext/Provider/ProviderDocs.ts | 2 +- .../Provider/__tests__/Provider.test.tsx | 378 ++++++++++++++++++ .../Field/Upload/__tests__/Upload.test.tsx | 4 + .../extensions/forms/Form/Handler/Handler.tsx | 2 + .../Form/Handler/__tests__/Handler.test.tsx | 3 + .../data-context/__tests__/getData.test.tsx | 50 +++ .../data-context/__tests__/useData.test.tsx | 328 ++++++++++++++- .../forms/Form/data-context/getData.tsx | 13 +- .../forms/Form/data-context/useData.tsx | 33 +- .../hooks/__tests__/useFieldProps.test.tsx | 6 + .../extensions/forms/hooks/useFieldProps.ts | 1 + .../FieldAndValueVisibility.stories.tsx | 24 ++ .../dnb-eufemia/src/extensions/forms/types.ts | 15 +- 23 files changed, 1192 insertions(+), 54 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/Examples.tsx index 2727e272a38..9395fb53f47 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/Examples.tsx @@ -297,6 +297,43 @@ export const Locale = () => { ) } +export const VisibleData = () => { + return ( + + { + const myData = reduceToVisibleFields(data, { + removePaths: ['/isVisible'], + }) + console.log('Result of reduceToVisibleFields: ', myData) + }} + > + + + + + + + + + + + + ) +} + export const FilterData = () => { return ( diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/demos.mdx index 79aa7645246..28a15bf58b4 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/demos.mdx @@ -44,15 +44,11 @@ This example is only for demo purpose and will NOT redirect to a new location. I -### Filter your data +### Visible data -By using the `filterData` method from the `onSubmit` event callback you can filter out data that you don't want to send to your server. +You can use the `reduceToVisibleFields` function to get only the data of visible (mounted) fields. -More info about `filterData` can be found in the [Getting Started](/uilib/extensions/forms/getting-started/#filter-data) section. - -In this example we filter out all fields that are disabled. - - + ### With session storage @@ -88,3 +84,13 @@ This example demonstrates how to use async validation with an async `onSubmit` a - To access the `date` "in sync" – you can use the [useData](/uilib/extensions/forms/Form/useData/) hook. + +### Filter your data + +By using the `filterData` method from the `onSubmit` event callback you can filter out data that you don't want to send to your server. + +More info about `filterData` can be found in the [Getting Started](/uilib/extensions/forms/getting-started/#filter-data) section. + +In this example we filter out all fields that are disabled. + + diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/info.mdx index ae0d53d882c..639ab480cce 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/info.mdx @@ -30,17 +30,25 @@ import { Form } from '@dnb/eufemia/extensions/forms' function MyForm() { const { - getValue, // Method to get a single value - update, // Method to update a single value - set, // Method to set the whole dataset - data, // The whole dataset (unvalidated) - filterData, // Method to filter data with your own logic + getValue, + update, + set, + data, + filterData, + reduceToVisibleFields, } = Form.useData('unique') return ... } ``` +- `getValue` will return the value of the given path. +- `update` will update the value of the given path. +- `set` will set the whole dataset. +- `data` will return the whole dataset (unvalidated). +- `filterData` will filter the data based on your own logic. +- `reduceToVisibleFields` will use the provided data and remove data entries of hidden (unmounted) fields. + More examples can be found in the [useData](/uilib/extensions/forms/Form/useData/) hook docs. ### TypeScript support @@ -127,9 +135,30 @@ Ensure you only use this feature for non-sensitive data. It will flush the storage once the form gets submitted. +## Visible data + +You can use the `reduceToVisibleFields` function to get only the data of visible (mounted) fields. + +```jsx +import { Form } from '@dnb/eufemia/extensions/forms' + +render( + { + const myData = reduceToVisibleFields(data, { + keepPaths: ['/foo'], + removePaths: ['/bar'], + }) + }} + > + + , +) +``` + ## Filter data -You can use the `filterData` method to filter your `onSubmit` data. It might be useful, for example, to **exclude disabled fields** or filter out empty fields. The callback function receives the following arguments: +You can use the `filterData` function to filter your `onSubmit` data. It might be useful, for example, to **exclude disabled fields** or filter out empty fields. The callback function receives the following arguments: 1. `path` as the first argument. 2. `value` as the second argument. @@ -150,9 +179,10 @@ You can filter arrays by using the `filterData` method. You can find more inform ### onSubmit parameters -The `onSubmit` event returns additional methods you can call: +The `onSubmit` event returns additional functions you can call: - `filterData` Filters the given/internal data set. +- `reduceToVisibleFields` Reduces the given data set to only contain the visible fields (mounted fields). - `resetForm` Deletes `sessionStorage` and browser stored autocomplete data. - `clearData` Empties the given/internal data set. @@ -168,11 +198,14 @@ const myFilter = { const MyForm = () => { return ( { + onSubmit={( + data, + { filterData, reduceToVisibleFields, resetForm, clearData }, + ) => { resetForm() clearData() - const myData = filterData(myFilter) + const myData = reduceToVisibleFields(filterData(myFilter)) }} sessionStorageId="session-key" > diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/getData/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/getData/info.mdx index 91fa594d637..a8da923b662 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/getData/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/getData/info.mdx @@ -14,13 +14,15 @@ function Component() { } // Later, when there is data available -const { - getValue, // Method to get a single value - data, // The whole dataset (unvalidated) - filterData, // Method to filter data with your own logic -} = Form.getData('unique-id') +const { getValue, data, filterData, reduceToVisibleFields } = + Form.getData('unique-id') ``` +- `getValue` will return the value of the given path. +- `data` will return the whole dataset (unvalidated). +- `filterData` will filter the data based on your own logic. +- `reduceToVisibleFields` will use the provided data and remove data entries of hidden (unmounted) fields. + You link them together via the `id` (string) property. TypeScript support: @@ -35,9 +37,31 @@ Related helpers: - [setData](/uilib/extensions/forms/Form/setData/) - [useData](/uilib/extensions/forms/Form/useData/) +## Visible data + +You can use the `reduceToVisibleFields` function to get only the data of visible (mounted) fields. + +```tsx +import { Form } from '@dnb/eufemia/extensions/forms' + +const MyForm = () => { + return ( + + + + + + ) +} + +// Later, when there is data available +const { data, reduceToVisibleFields } = Form.getData('unique-id') +const visibleData = reduceToVisibleFields(data) +``` + ## Filter data -You can use the `filterData` method to filter your data. +You can use the `filterData` function to filter your data. You simply give it the [same kind of filter](/uilib/extensions/forms/Form/Handler/demos/#filter-your-data) as you would within the `onSubmit` event callback. @@ -55,7 +79,7 @@ It returns the filtered form data. ```tsx const MyForm = () => { return ( - + ) @@ -68,6 +92,6 @@ const filterDataHandler = ({ path, value, data, props, internal }) => { } // Later, when there is data available -const { filterData } = Form.getData(id) +const { filterData } = Form.getData('unique-id') const filteredData = filterData(filterDataHandler) ``` diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useData/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useData/Examples.tsx index aa0ff9cb98f..aa5bbe1fb04 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useData/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useData/Examples.tsx @@ -1,7 +1,7 @@ import React from 'react' import ComponentBox from '../../../../../../shared/tags/ComponentBox' import { Button, Flex, Section } from '@dnb/eufemia/src' -import { Form, Field } from '@dnb/eufemia/src/extensions/forms' +import { Form, Field, Value } from '@dnb/eufemia/src/extensions/forms' import { ScrollView } from '@dnb/eufemia/src/fragments' export function Default() { @@ -187,3 +187,58 @@ export function FilterData() { ) } + +export const VisibleData = () => { + return ( + + {() => { + const MyForm = () => { + const { data, reduceToVisibleFields } = Form.useData() + + // Use useEffect to ensure we get the latest data + React.useEffect(() => { + console.log( + 'Result of reduceToVisibleFields:\n', + reduceToVisibleFields(data, { + removePaths: ['/isVisible'], + }), + ) + }, [data, reduceToVisibleFields]) + + return ( + + + + + + + + + + + + + + + ) + } + + return + }} + + ) +} diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useData/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useData/demos.mdx index 1cc62ec7d85..1fdfaed67af 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useData/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useData/demos.mdx @@ -20,6 +20,12 @@ The update function `update('/count', (count) => count + 1)` has TypeScript supp +### Get only data of visible fields + +You can use the `reduceToVisibleFields` function to get only the data of visible (mounted) fields. + + + ### Filter your data This example uses the `keepInDOM` property to keep the field in the DOM. diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useData/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useData/info.mdx index c6be2a7cffb..4b79b13946a 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useData/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useData/info.mdx @@ -9,15 +9,35 @@ With the `Form.useData` hook, you can manage your form data from nested componen The hook returns an object with the following properties: ```tsx -const { - getValue, // Method to get a single value - update, // Method to update a single value - set, // Method to set the whole dataset - data, // The whole dataset (unvalidated) - filterData, // Method to filter data with your own logic -} = Form.useData('optional-id') +import { Form } from '@dnb/eufemia/extensions/forms' + +function MyComponent() { + const { + getValue, + update, + set, + data, + filterData, + reduceToVisibleFields, + } = Form.useData() + + return <>MyComponent +} + +render( + + + , +) ``` +- `getValue` will return the value of the given path. +- `update` will update the value of the given path. +- `set` will set the whole dataset. +- `data` will return the whole dataset (unvalidated). +- `filterData` will filter the data based on your own logic. +- `reduceToVisibleFields` will use the provided data and remove data entries of hidden (unmounted) fields. + ## Usage You can use the `Form.useData` hook with or without an `id` (string) property, which is optional and can be used to link the data to a specific [Form.Handler](/uilib/extensions/forms/Form/Handler/) component. @@ -32,9 +52,7 @@ import { Form } from '@dnb/eufemia/extensions/forms' function MyForm() { return ( - ... - ... ) } @@ -131,9 +149,43 @@ function MyForm() { } ``` +## Visible data + +You can use the `reduceToVisibleFields` function to get only the data of visible (mounted) fields. Check out the [example](/uilib/extensions/forms/Form/Handler/demos/#visible-data) in the demo section. + +```tsx +import { Form } from '@dnb/eufemia/extensions/forms' + +function MyComponent() { + const { data, reduceToVisibleFields } = Form.useData() + + // Use useEffect to ensure we get the latest data + React.useEffect(() => { + console.log(reduceToVisibleFields(data)) + }, [data]) + + return <>MyComponent +} + +render( + + + , +) +``` + +In addition, you can include or exclude paths by using the `keepPaths` and `removePaths` options. + +```ts +reduceToVisibleFields(data, { + keepPaths: ['/foo'], + removePaths: ['/bar'], +}) +``` + ## Filter data -You can use the `filterData` method to filter your data. Check out [the example below](#filter-your-data). +You can use the `filterData` function to filter your data. Check out [the example below](#filter-your-data). You simply give it the [same kind of filter](/uilib/extensions/forms/Form/Handler/demos/#filter-your-data) as you would within the `onSubmit` event callback. diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx index fa18a0d47b5..70c69928de7 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx @@ -114,11 +114,12 @@ function MyForm() { function MyComponent() { const { - getValue, // Method to get a single value - update, // Method to update a single value - set, // Method to set the whole dataset - data, // The whole dataset (unvalidated) - filterData, // Method to filter data with your own logic + getValue, + update, + set, + data, + filterData, + reduceToVisibleFields, } = Form.useData() // optionally provide an id (unique-id) } @@ -126,17 +127,30 @@ function MyComponent() { Form.setData('unique-id', { companyName: 'DNB' }) // ... and the getData – method when ever you need to: -const { - getValue, // Method to get a single value - data, // The whole dataset (unvalidated) - filterData, // Method to filter data with your own logic -} = Form.getData('unique-id') +const { getValue, data, filterData, reduceToVisibleFields } = + Form.getData('unique-id') ``` +- `getValue` will return the value of the given path. +- `update` will update the value of the given path. +- `set` will set the whole dataset. +- `data` will return the whole dataset (unvalidated). +- `filterData` will filter the data based on your own logic. +- `reduceToVisibleFields` will use the provided data and remove data entries of hidden (unmounted) fields. + As you can see in the code above, you can even handle the state outside the `Form.Handler` context. You find more details on this topic in the [useData](/uilib/extensions/forms/Form/useData/) documentation. +#### Visible data + +User entered data will always be stored internally in the data context, even if a [Field](/uilib/extensions/forms/all-fields/) is temporarily shown (mounted/unmounted). + +When submitting data to the server, you might want to exclude data that has been hidden (unmounted) by the user. You have two built-in options to achieve this: + +- You can use the `reduceToVisibleFields` function to get only the data of visible (mounted) fields. Check out the [example](/uilib/extensions/forms/Form/Handler/demos/#visible-data) in the demo section. +- Or you may use the `filterData` function to filter your data. More details in the next section. + #### Filter data In this seciton we will show how to filter out some data based on your own logic. You can filter data by any given criteria. This is done by utilizing the `filterData` method from e.g.: diff --git a/packages/dnb-eufemia/src/extensions/forms/DataContext/Context.ts b/packages/dnb-eufemia/src/extensions/forms/DataContext/Context.ts index 421fabdb2dc..f6283182b2b 100644 --- a/packages/dnb-eufemia/src/extensions/forms/DataContext/Context.ts +++ b/packages/dnb-eufemia/src/extensions/forms/DataContext/Context.ts @@ -32,6 +32,14 @@ export type EventListenerCall = { callback: (params?: { value: unknown }) => void | Promise } +export type VisibleDataHandler = ( + data?: Data, + options?: VisibleDataOptions +) => Partial +export type VisibleDataOptions = { + keepPaths?: Array + removePaths?: Array +} export type MutateDataHandler = ( data: Data, mutate: TransformData @@ -96,6 +104,7 @@ export interface ContextState { clearData?: () => void mutateDataHandler?: MutateDataHandler filterDataHandler?: FilterDataHandler + visibleDataHandler?: VisibleDataHandler validateData: () => void handleSubmit: (props?: HandleSubmitProps) => void scrollToTop: () => void diff --git a/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/Provider.tsx b/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/Provider.tsx index 8142dbfa753..6011bb30694 100644 --- a/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/Provider.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/Provider.tsx @@ -42,6 +42,7 @@ import Context, { HandleSubmitCallback, MountState, TransformData, + VisibleDataHandler, } from '../Context' /** @@ -322,7 +323,8 @@ export default function Provider( const hasFieldState = useCallback( (state: SubmitState) => { for (const path in mountedFieldsRef.current) { - if (checkFieldStateFor(path, state)) { + const item = mountedFieldsRef.current[path] + if (item.isMounted && checkFieldStateFor(path, state)) { return true } } @@ -334,7 +336,12 @@ export default function Provider( const hasFieldError = useCallback( (path: Path) => { for (const p in mountedFieldsRef.current) { - if (p === path && checkFieldStateFor(path, 'error')) { + const item = mountedFieldsRef.current[path] + if ( + item.isMounted && + p === path && + checkFieldStateFor(path, 'error') + ) { return true } } @@ -474,6 +481,39 @@ export default function Provider( [] ) + /** + * Ensure only visible data is returned + */ + const visibleDataHandler: VisibleDataHandler = useCallback( + (data = internalDataRef.current, { keepPaths, removePaths } = {}) => { + const visibleData = {} as Partial + + for (const path in mountedFieldsRef.current) { + const item = mountedFieldsRef.current[path] + if ( + item && + item.isVisible !== false && + (item.isPreMounted !== false || item.wasStepChange === true) && + (removePaths ? !removePaths.includes(path) : true) && + pointer.has(data, path) + ) { + pointer.set(visibleData, path, pointer.get(data, path)) + } + } + + if (keepPaths) { + keepPaths.forEach((path) => { + if (pointer.has(data, path)) { + pointer.set(visibleData, path, pointer.get(data, path)) + } + }) + } + + return visibleData + }, + [] + ) + /** * Filter the data set based on the filterData function */ @@ -525,6 +565,7 @@ export default function Provider( // - Shared state const sharedData = useSharedState(id) const sharedAttachments = useSharedState<{ + visibleDataHandler?: VisibleDataHandler filterDataHandler?: FilterDataHandler hasErrors?: ContextState['hasErrors'] hasFieldError?: ContextState['hasFieldError'] @@ -636,6 +677,7 @@ export default function Provider( useLayoutEffect(() => { if (id) { extendAttachment?.({ + visibleDataHandler, filterDataHandler, hasErrors, hasFieldError, @@ -648,6 +690,7 @@ export default function Provider( } }, [ extendAttachment, + visibleDataHandler, filterDataHandler, filterSubmitData, hasErrors, @@ -1020,8 +1063,20 @@ export default function Provider( const filteredData = filterSubmitData ? filterDataHandler(mutatedData, filterSubmitData) : mutatedData // @deprecated – can be removed in v11 + + const reduceToVisibleFields: VisibleDataHandler = ( + data, + options + ) => { + return visibleDataHandler( + transformOut ? mutateDataHandler(data, transformOut) : data, + options + ) + } + const options = { filterData, + reduceToVisibleFields, resetForm: () => { formElement?.reset?.() @@ -1077,6 +1132,7 @@ export default function Provider( scrollTopOnSubmit, sessionStorageId, transformOut, + visibleDataHandler, ] ) @@ -1175,6 +1231,7 @@ export default function Provider( updateDataValue, setData, clearData, + visibleDataHandler, filterDataHandler, addOnChangeHandler, setHandleSubmit, diff --git a/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/ProviderDocs.ts b/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/ProviderDocs.ts index 64749c9f220..500c95dc944 100644 --- a/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/ProviderDocs.ts +++ b/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/ProviderDocs.ts @@ -100,7 +100,7 @@ export const ProviderEvents: PropertiesTableProps = { status: 'optional', }, onSubmit: { - doc: "Will be called (on validation success) when the user submit the form (i.e by clicking a [SubmitButton](/uilib/extensions/forms/Form/SubmitButton) component inside), with the data set as argument. When an async function is provided, it will show an indicator on the submit button during the form submission. All form elements will be disabled during the submit. The indicator will be shown for minimum 1 second. Related props: `minimumAsyncBehaviorTime` and `asyncSubmitTimeout`. You can return an error or an object with these keys `{ status: 'pending', info: 'Info message', warning: 'Warning message', error: Error('My error') } as const` to be shown in a [FormStatus](/uilib/components/form-status). Will only emit when every validation has passed. The second parameter is an object containing the `filterData` function.", + doc: "Will be called (on validation success) when the user submit the form (i.e by clicking a [SubmitButton](/uilib/extensions/forms/Form/SubmitButton) component inside), with the data set as argument. When an async function is provided, it will show an indicator on the submit button during the form submission. All form elements will be disabled during the submit. The indicator will be shown for minimum 1 second. Related props: `minimumAsyncBehaviorTime` and `asyncSubmitTimeout`. You can return an error or an object with these keys `{ status: 'pending', info: 'Info message', warning: 'Warning message', error: Error('My error') } as const` to be shown in a [FormStatus](/uilib/components/form-status). Will only emit when every validation has passed. The second parameter is an object containing the `filterData`, `reduceToVisibleFields`, `resetForm` and `clearData` functions.", type: 'function', status: 'optional', }, 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 a7ebc84d102..6fff81d2df3 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 @@ -4024,6 +4024,7 @@ describe('DataContext.Provider', () => { expect(result.current).toEqual({ data: { myField: 'foo' }, + reduceToVisibleFields: expect.any(Function), filterData: expect.any(Function), getValue: expect.any(Function), update: expect.any(Function), @@ -4064,6 +4065,7 @@ describe('DataContext.Provider', () => { expect(result.current).toEqual({ data: { myField: 'bar' }, + reduceToVisibleFields: expect.any(Function), filterData: expect.any(Function), getValue: expect.any(Function), update: expect.any(Function), @@ -4381,4 +4383,380 @@ describe('DataContext.Provider', () => { expect(third).not.toHaveAttribute('aria-required', 'true') }) }) + + it('should transform a field value with "transformIn"', async () => { + let submitData = null + let changeData = null + + const onSubmit = jest.fn((data) => { + submitData = data + }) + const onChange = jest.fn((data) => { + changeData = data + }) + + const transformIn = jest.fn(({ path, value }) => { + if (path === '/foo' && value === 'foo') { + return 'transformed' + } + return value + }) + + render( + + + + + ) + + const form = document.querySelector('form') + fireEvent.submit(form) + + expect(document.querySelector('input')).toHaveValue('transformed') + expect(submitData).toEqual({ + foo: 'transformed', + myPath: 'My Value', + }) + expect(changeData).toEqual(null) + + fireEvent.change(document.querySelector('input'), { + target: { value: 'baz' }, + }) + expect(document.querySelector('input')).toHaveValue('baz') + expect(submitData).toEqual({ + foo: 'transformed', + myPath: 'My Value', + }) + expect(changeData).toEqual({ + foo: 'baz', + myPath: 'My Value', + }) + + fireEvent.change(document.querySelector('input'), { + target: { value: 'foo' }, + }) + fireEvent.blur(document.querySelector('input')) + expect(document.querySelector('input')).toHaveValue('transformed') + expect(submitData).toEqual({ + foo: 'transformed', + myPath: 'My Value', + }) + expect(changeData).toEqual({ + foo: 'transformed', + myPath: 'My Value', + }) + }) + + it('should transform a field value with "transformOut"', async () => { + let submitData = null + + const onSubmit = jest.fn((data) => { + submitData = data + }) + + const transformOut = jest.fn(({ path, value }) => { + if (path === '/foo') { + return 'bar' + } + return value + }) + + render( + + + + + ) + + const form = document.querySelector('form') + fireEvent.submit(form) + + expect(submitData).toEqual({ + foo: 'bar', + myPath: 'My Value', + }) + expect(document.querySelector('input')).toHaveValue('foo') + }) + + describe('reduceToVisibleFields', () => { + it('should remove data entries of hidden fields using Visibility', async () => { + let submitData = null + + const onSubmit = jest.fn((data, { reduceToVisibleFields }) => { + submitData = reduceToVisibleFields(data) + }) + + render( + + + + + + + + ) + + const form = document.querySelector('form') + const button = document.querySelector('button') + + fireEvent.submit(form) + expect(submitData).toEqual({ + isVisible: false, + }) + + await userEvent.click(button) + fireEvent.submit(form) + expect(submitData).toEqual({ + interactive: 'I am visible', + isVisible: true, + }) + + await userEvent.click(button) + fireEvent.submit(form) + expect(submitData).toEqual({ + isVisible: false, + }) + + await userEvent.click(button) + fireEvent.submit(form) + expect(submitData).toEqual({ + interactive: 'I am visible', + isVisible: true, + }) + }) + + it('should still take in account "transformOut"', async () => { + let submitData = null + + const onSubmit = jest.fn((data, { reduceToVisibleFields }) => { + submitData = reduceToVisibleFields(data) + }) + + const transformOut = jest.fn(({ path, value }) => { + if (path === '/interactive') { + return 'bar' + } + return value + }) + + render( + + + + + + + + ) + + const form = document.querySelector('form') + const button = document.querySelector('button') + + fireEvent.submit(form) + expect(submitData).toEqual({ + isVisible: false, + }) + + await userEvent.click(button) + fireEvent.submit(form) + expect(submitData).toEqual({ + interactive: 'bar', + isVisible: true, + }) + + await userEvent.click(button) + fireEvent.submit(form) + expect(submitData).toEqual({ + isVisible: false, + }) + + await userEvent.click(button) + fireEvent.submit(form) + expect(submitData).toEqual({ + interactive: 'bar', + isVisible: true, + }) + }) + + it('should keep paths with "keepPaths"', async () => { + let submitData = null + + const onSubmit = jest.fn((data, { reduceToVisibleFields }) => { + submitData = reduceToVisibleFields(data, { + keepPaths: ['/otherExistingPath'], + removePaths: ['/isVisible'], + }) + }) + + render( + + + + + + + + ) + + const form = document.querySelector('form') + const button = document.querySelector('button') + + fireEvent.submit(form) + expect(submitData).toEqual({ + otherExistingPath: 'foo', + }) + + await userEvent.click(button) + fireEvent.submit(form) + expect(submitData).toEqual({ + interactive: 'I am visible', + otherExistingPath: 'foo', + }) + + await userEvent.click(button) + fireEvent.submit(form) + expect(submitData).toEqual({ + otherExistingPath: 'foo', + }) + + await userEvent.click(button) + fireEvent.submit(form) + expect(submitData).toEqual({ + interactive: 'I am visible', + otherExistingPath: 'foo', + }) + }) + + it('should exclude paths with "removePaths"', async () => { + let submitData = null + + const onSubmit = jest.fn((data, { reduceToVisibleFields }) => { + submitData = reduceToVisibleFields(data, { + removePaths: ['/isVisible'], + }) + }) + + render( + + + + + + + + ) + + const form = document.querySelector('form') + const button = document.querySelector('button') + + fireEvent.submit(form) + expect(submitData).toEqual({}) + + await userEvent.click(button) + fireEvent.submit(form) + expect(submitData).toEqual({ + interactive: 'I am visible', + }) + + await userEvent.click(button) + fireEvent.submit(form) + expect(submitData).toEqual({}) + + await userEvent.click(button) + fireEvent.submit(form) + expect(submitData).toEqual({ + interactive: 'I am visible', + }) + }) + + it('should return visible data after unmount and mount', async () => { + let submitData = null + + const onSubmit = jest.fn((data, { reduceToVisibleFields }) => { + submitData = reduceToVisibleFields(data) + }) + + const MockComponent = () => { + const [count, increment] = React.useReducer( + (state) => state + 1, + 0 + ) + return ( + + + + {count % 2 ? ( + + ) : null} + + ) + } + + render() + + const form = document.querySelector('form') + const button = document.querySelector('button') + + fireEvent.submit(form) + expect(submitData).toEqual({}) + + await userEvent.click(button) + fireEvent.submit(form) + expect(submitData).toEqual({ + interactive: 'I am visible', + }) + + await userEvent.click(button) + fireEvent.submit(form) + expect(submitData).toEqual({}) + + await userEvent.click(button) + fireEvent.submit(form) + expect(submitData).toEqual({ + interactive: 'I am visible', + }) + }) + }) }) diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Upload/__tests__/Upload.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/Upload/__tests__/Upload.test.tsx index 4259ddfe6a7..c467c781db6 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/Upload/__tests__/Upload.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/Upload/__tests__/Upload.test.tsx @@ -374,6 +374,7 @@ describe('Field.Upload', () => { clearData: expect.any(Function), resetForm: expect.any(Function), filterData: expect.any(Function), + reduceToVisibleFields: expect.any(Function), } ) }) @@ -506,6 +507,7 @@ describe('Field.Upload', () => { clearData: expect.any(Function), resetForm: expect.any(Function), filterData: expect.any(Function), + reduceToVisibleFields: expect.any(Function), } ) }) @@ -627,6 +629,7 @@ describe('Field.Upload', () => { clearData: expect.any(Function), resetForm: expect.any(Function), filterData: expect.any(Function), + reduceToVisibleFields: expect.any(Function), } ) }) @@ -770,6 +773,7 @@ describe('Field.Upload', () => { clearData: expect.any(Function), resetForm: expect.any(Function), filterData: expect.any(Function), + reduceToVisibleFields: expect.any(Function), } ) }) diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Handler/Handler.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/Handler/Handler.tsx index 0b9a71af1d8..40417b81f85 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Form/Handler/Handler.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Form/Handler/Handler.tsx @@ -30,6 +30,7 @@ export default function FormHandler({ globalStatusId, filterSubmitData, transformIn, + transformOut, onChange, onPathChange, onSubmit, @@ -57,6 +58,7 @@ export default function FormHandler({ globalStatusId, filterSubmitData, transformIn, + transformOut, onChange, onPathChange, onSubmit, diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Handler/__tests__/Handler.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/Handler/__tests__/Handler.test.tsx index 9955e8b3161..eac88cfdc11 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Form/Handler/__tests__/Handler.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Form/Handler/__tests__/Handler.test.tsx @@ -653,6 +653,7 @@ describe('Form.Handler', () => { clearData: expect.any(Function), resetForm: expect.any(Function), filterData: expect.any(Function), + reduceToVisibleFields: expect.any(Function), } ) @@ -781,6 +782,7 @@ describe('Form.Handler', () => { clearData: expect.any(Function), resetForm: expect.any(Function), filterData: expect.any(Function), + reduceToVisibleFields: expect.any(Function), } ) @@ -820,6 +822,7 @@ describe('Form.Handler', () => { clearData: expect.any(Function), resetForm: expect.any(Function), filterData: expect.any(Function), + reduceToVisibleFields: expect.any(Function), } ) }) diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/data-context/__tests__/getData.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/data-context/__tests__/getData.test.tsx index 868ba1072c2..93b390a6461 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Form/data-context/__tests__/getData.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Form/data-context/__tests__/getData.test.tsx @@ -158,4 +158,54 @@ describe('getData', () => { bar: 'baz', }) }) + + it('should provide reduceToVisibleFields handler', () => { + type Data = { foo: string } + + const { rerender } = render( + + + + + + + + ) + + const { data, reduceToVisibleFields } = getData(identifier) + + expect(data).toEqual({ + foo: 'foo', + bar: 'baz', + }) + + rerender( + + + + + + + + ) + + expect(reduceToVisibleFields(data)).toEqual({ + bar: 'baz', + }) + + rerender( + + + + + + + + ) + + expect(reduceToVisibleFields(data)).toEqual({ + foo: 'foo', + bar: 'baz', + }) + }) }) diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/data-context/__tests__/useData.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/data-context/__tests__/useData.test.tsx index c023f8d20fb..1c8a6278244 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Form/data-context/__tests__/useData.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Form/data-context/__tests__/useData.test.tsx @@ -1,10 +1,11 @@ import React from 'react' import { renderHook, act, render, fireEvent } from '@testing-library/react' import { makeUniqueId } from '../../../../../shared/component-helper' -import { Field } from '../../..' +import { Field, Form } from '../../..' import Provider from '../../../DataContext/Provider' import useData from '../useData' import { FilterData } from '../../../DataContext/Context' +import userEvent from '@testing-library/user-event' describe('Form.useData', () => { let identifier: string @@ -291,6 +292,7 @@ describe('Form.useData', () => { update: expect.any(Function), set: expect.any(Function), getValue: expect.any(Function), + reduceToVisibleFields: expect.any(Function), filterData: expect.any(Function), }) }) @@ -393,4 +395,328 @@ describe('Form.useData', () => { } as FilterData) ).toEqual({}) }) + + describe('reduceToVisibleFields', () => { + it('should remove data entries of hidden fields using Visibility', async () => { + const data = { field1: 'foo', field2: 'bar', field3: 'baz' } + + const { result } = renderHook(() => useData(identifier), { + wrapper: ({ children }) => ( + + + + + + + + + {children} + + ), + }) + + const [field1, field2] = Array.from( + document.querySelectorAll('input') + ) + + expect( + result.current.reduceToVisibleFields(result.current.data) + ).toEqual({ + field1: 'foo', + field2: 'bar', + field3: 'baz', + }) + + fireEvent.change(field1, { + target: { value: 'hide me' }, + }) + + expect( + result.current.reduceToVisibleFields(result.current.data) + ).toEqual({ + field2: 'bar', + field3: 'baz', + }) + + fireEvent.change(field1, { + target: { value: 'something else' }, + }) + + expect( + result.current.reduceToVisibleFields(result.current.data) + ).toEqual({ + field1: 'something else', + field2: 'bar', + field3: 'baz', + }) + + fireEvent.change(field2, { + target: { value: 'hide me' }, + }) + + expect( + result.current.reduceToVisibleFields(result.current.data) + ).toEqual({ + field1: 'something else', + field3: 'baz', + }) + }) + + it('should return visible data after visibility change', async () => { + let collectedData = null + + const Output = () => { + const { data, reduceToVisibleFields } = Form.useData() + + // Use useEffect to ensure we get the latest data + React.useEffect(() => { + collectedData = reduceToVisibleFields(data) + }, [data, reduceToVisibleFields]) + + return null + } + + render( + + + + + + + + + + ) + + const button = document.querySelector('button') + + expect(collectedData).toEqual({ + isVisible: false, + }) + + await userEvent.click(button) + + expect(collectedData).toEqual({ + interactive: 'I am visible', + isVisible: true, + }) + + await userEvent.click(button) + + expect(collectedData).toEqual({ + isVisible: false, + }) + + await userEvent.click(button) + + expect(collectedData).toEqual({ + interactive: 'I am visible', + isVisible: true, + }) + }) + + it('should keep paths with "keepPaths"', async () => { + let collectedData = null + + const Output = () => { + const { data, reduceToVisibleFields } = Form.useData() + + // Use useEffect to ensure we get the latest data + React.useEffect(() => { + collectedData = reduceToVisibleFields(data, { + keepPaths: ['/otherExistingPath'], + }) + }, [data, reduceToVisibleFields]) + + return null + } + + render( + + + + + + + + + + ) + + const button = document.querySelector('button') + + expect(collectedData).toEqual({ + interactive: 'I am visible', + otherExistingPath: 'foo', + isVisible: true, + }) + + await userEvent.click(button) + expect(collectedData).toEqual({ + otherExistingPath: 'foo', + isVisible: false, + }) + + await userEvent.click(button) + expect(collectedData).toEqual({ + interactive: 'I am visible', + otherExistingPath: 'foo', + isVisible: true, + }) + + await userEvent.click(button) + expect(collectedData).toEqual({ + otherExistingPath: 'foo', + isVisible: false, + }) + + await userEvent.click(button) + expect(collectedData).toEqual({ + interactive: 'I am visible', + otherExistingPath: 'foo', + isVisible: true, + }) + }) + + it('should exclude paths with "removePaths"', async () => { + let collectedData = null + + const Output = () => { + const { data, reduceToVisibleFields } = Form.useData() + + // Use useEffect to ensure we get the latest data + React.useEffect(() => { + collectedData = reduceToVisibleFields(data, { + removePaths: ['/isVisible'], + }) + }, [data, reduceToVisibleFields]) + + return null + } + + render( + + + + + + + + + + ) + + const button = document.querySelector('button') + + expect(collectedData).toEqual({ + interactive: 'I am visible', + }) + + await userEvent.click(button) + expect(collectedData).toEqual({}) + + await userEvent.click(button) + expect(collectedData).toEqual({ + interactive: 'I am visible', + }) + + await userEvent.click(button) + expect(collectedData).toEqual({}) + + await userEvent.click(button) + expect(collectedData).toEqual({ + interactive: 'I am visible', + }) + }) + + it('should return visible data after unmount and mount', async () => { + let collectedData = null + + const Output = () => { + const { data, reduceToVisibleFields } = Form.useData() + + // Use useEffect to ensure we get the latest data + React.useEffect(() => { + collectedData = reduceToVisibleFields(data) + }, [data, reduceToVisibleFields]) + + return null + } + + const MockComponent = () => { + const [count, increment] = React.useReducer( + (state) => state + 1, + 0 + ) + return ( + + + + {count % 2 ? ( + + ) : null} + + + + ) + } + + render() + + const button = document.querySelector('button') + + expect(collectedData).toEqual({}) + + await userEvent.click(button) + + expect(collectedData).toEqual({ + interactive: 'I am visible', + }) + + await userEvent.click(button) + + expect(collectedData).toEqual({}) + + await userEvent.click(button) + + expect(collectedData).toEqual({ + interactive: 'I am visible', + }) + }) + }) }) diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/data-context/getData.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/data-context/getData.tsx index 3c0629b300b..1742333554d 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Form/data-context/getData.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Form/data-context/getData.tsx @@ -3,21 +3,27 @@ import { SharedStateId, createSharedState, } from '../../../../shared/helpers/useSharedState' -import type { FilterDataHandler } from '../../DataContext/Context' +import type { + FilterDataHandler, + VisibleDataHandler, +} from '../../DataContext/Context' import type { Path } from '../../types' import type { UseDataReturnGetValue, UseDataReturnFilterData, + UseDataReturnVisibleData, } from './useData' type SharedAttachment = { filterDataHandler: FilterDataHandler + visibleDataHandler?: VisibleDataHandler } type SetDataReturn = { data: Data getValue: UseDataReturnGetValue filterData: UseDataReturnFilterData + reduceToVisibleFields: UseDataReturnVisibleData } export default function getData( @@ -33,6 +39,10 @@ export default function getData( const filterData: SetDataReturn['filterData'] = (filter) => sharedAttachments.data?.filterDataHandler?.(data, filter) + const reduceToVisibleFields: SetDataReturn['reduceToVisibleFields'] = + (data, options) => + sharedAttachments.data?.visibleDataHandler?.(data, options) + const getValue = (path: Path) => { if (pointer.has(data, path)) { return pointer.get(data, path) @@ -45,5 +55,6 @@ export default function getData( data, getValue, filterData, + reduceToVisibleFields, } } diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/data-context/useData.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/data-context/useData.tsx index fcd7462eb9d..f29c6e6a8ab 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Form/data-context/useData.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Form/data-context/useData.tsx @@ -15,6 +15,7 @@ import type { Path } from '../../types' import DataContext, { FilterData, FilterDataHandler, + VisibleDataHandler, } from '../../DataContext/Context' type PathImpl = P extends `${infer Key}/${infer Rest}` @@ -43,17 +44,21 @@ export type UseDataReturnFilterData = ( data?: Data ) => Partial +export type UseDataReturnVisibleData = VisibleDataHandler + type UseDataReturn = { data: Data set: (newData: Data) => void update: UseDataReturnUpdate getValue: UseDataReturnGetValue filterData: UseDataReturnFilterData + reduceToVisibleFields: UseDataReturnVisibleData } type SharedAttachment = { rerenderUseDataHook: () => void filterDataHandler?: FilterDataHandler + visibleDataHandler?: VisibleDataHandler } /** @@ -141,6 +146,22 @@ export default function useData( [id, updateDataValue] ) + const reduceToVisibleFields = useCallback< + UseDataReturn['reduceToVisibleFields'] + >( + (data, options = {}) => { + if (id) { + return sharedAttachmentsRef.current.data?.visibleDataHandler?.( + data, + options + ) + } + + return context?.visibleDataHandler?.(data, options) + }, + [context, id] + ) + const filterData = useCallback['filterData']>( (filter, data = sharedDataRef.current.data) => { if (id) { @@ -150,7 +171,7 @@ export default function useData( ) } - return context?.filterDataHandler(data, filter) + return context?.filterDataHandler?.(data, filter) }, [context, id] ) @@ -177,8 +198,16 @@ export default function useData( update: updateHandler, set: setHandler, getValue, + reduceToVisibleFields, filterData, }), - [data, getValue, setHandler, updateHandler, filterData] + [ + data, + updateHandler, + setHandler, + getValue, + reduceToVisibleFields, + filterData, + ] ) } diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx index 833db6c504d..05562248acd 100644 --- a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx @@ -3968,6 +3968,7 @@ describe('useFieldProps', () => { }) expect(setMountedFieldState).toHaveBeenNthCalledWith(2, '/foo', { isMounted: true, + isPreMounted: true, }) unmount() @@ -4007,6 +4008,7 @@ describe('useFieldProps', () => { }) expect(setMountedFieldState).toHaveBeenNthCalledWith(3, '/foo', { isMounted: true, + isPreMounted: true, }) unmount() @@ -4048,6 +4050,7 @@ describe('useFieldProps', () => { }) expect(setMountedFieldState).toHaveBeenNthCalledWith(3, '/foo', { isMounted: true, + isPreMounted: true, }) unmount() @@ -4101,6 +4104,7 @@ describe('useFieldProps', () => { }) expect(setMountedFieldState).toHaveBeenNthCalledWith(2, '/foo', { isMounted: true, + isPreMounted: true, }) rerender({ path: '/bar' }) @@ -4115,6 +4119,7 @@ describe('useFieldProps', () => { }) expect(setMountedFieldState).toHaveBeenNthCalledWith(5, '/bar', { isMounted: true, + isPreMounted: true, }) unmount() @@ -4122,6 +4127,7 @@ describe('useFieldProps', () => { expect(setMountedFieldState).toHaveBeenCalledTimes(6) expect(setMountedFieldState).toHaveBeenNthCalledWith(2, '/foo', { isMounted: true, + isPreMounted: true, }) log.mockRestore() diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts index 4950727f7ed..b1f5190d4ea 100644 --- a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts +++ b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts @@ -1457,6 +1457,7 @@ export default function useFieldProps( // Mount procedure. setMountedFieldStateDataContext(identifier, { isMounted: true, + isPreMounted: true, }) // Unmount procedure. diff --git a/packages/dnb-eufemia/src/extensions/forms/stories/FieldAndValueVisibility.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/stories/FieldAndValueVisibility.stories.tsx index 21505a11e60..4597c25dfd1 100644 --- a/packages/dnb-eufemia/src/extensions/forms/stories/FieldAndValueVisibility.stories.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/stories/FieldAndValueVisibility.stories.tsx @@ -6,13 +6,31 @@ export default { title: 'Eufemia/Extensions/Forms/FieldAndValueVisibility', } +const LogVisibleData = () => { + const { data, reduceToVisibleFields } = Form.useData() + + // Use useEffect to ensure we get the latest data + React.useEffect(() => { + console.log( + 'visibleData', + reduceToVisibleFields(data, { + removePaths: ['/isVisible'], + }) + ) + }, [data, reduceToVisibleFields]) + + return null +} + export const ValueVisibility = () => { const [count, increment] = React.useReducer((state) => state + 1, 1) + return ( console.log('onChange', data)} // onSubmit={(data) => console.log('onSubmit', data)} > + diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/PushButton/PushButton.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/PushButton/PushButton.tsx index 6196000cf6c..77c840be1fe 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/PushButton/PushButton.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/PushButton/PushButton.tsx @@ -3,7 +3,7 @@ import classnames from 'classnames' import { Button } from '../../../../components' import { ButtonProps } from '../../../../components/Button' import IterateItemContext from '../IterateItemContext' -import { useSwitchContainerMode } from '../hooks' +import { useArrayLimit, useSwitchContainerMode } from '../hooks' import { omitDataValueReadWriteProps, Path } from '../../types' import { add } from '../../../../icons' import DataContext from '../../DataContext/Context' @@ -29,6 +29,9 @@ function PushButton(props: Props) { props const buttonProps = omitDataValueReadWriteProps(restProps) const arrayValue = useDataValue().getValueByPath(path) + const { hasReachedLimit, setShowStatus } = useArrayLimit({ + path, + }) if (arrayValue !== undefined && !Array.isArray(arrayValue)) { throw new Error('PushButton received a non-array value') @@ -39,6 +42,11 @@ function PushButton(props: Props) { }) const handleClick = useCallback(async () => { + if (hasReachedLimit) { + setShowStatus(true) + return // stop here + } + const newValue = typeof pushValue === 'function' ? pushValue(arrayValue) : pushValue @@ -54,12 +62,14 @@ function PushButton(props: Props) { setLastItemContainerMode('view') }, 100) // UX improvement because of the "openDelay" }, [ + arrayValue, handlePathChange, handlePush, + hasReachedLimit, path, pushValue, setLastItemContainerMode, - arrayValue, + setShowStatus, ]) const content = useMemo(() => { diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/PushButton/__tests__/PushButton.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/PushButton/__tests__/PushButton.test.tsx index ffb5d7a964a..e0856d8ae5c 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/PushButton/__tests__/PushButton.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/PushButton/__tests__/PushButton.test.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { render, fireEvent } from '@testing-library/react' +import { render, fireEvent, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import IterateItemContext from '../../IterateItemContext' import { Field, Form, Iterate } from '../../..' @@ -209,4 +209,49 @@ describe('PushButton', () => { await userEvent.click(removeButton) expect(pushButton).toHaveTextContent('Add no. 1') }) + + it('should inherit "limit" prop from Array and show warning when limit is reached', async () => { + render( + + + + + + + + + ) + + const pushButton = document.querySelector( + '.dnb-forms-iterate-push-button' + ) + + // Add first item + await userEvent.click(pushButton) + + // Add second item + await userEvent.click(pushButton) + + expect(document.querySelector('.dnb-form-status')).toBeNull() + + // Try a third one + await userEvent.click(pushButton) + + await waitFor(() => { + const element = document.querySelector('.dnb-form-status') + expect(element).toBeInTheDocument() + expect(element).toHaveTextContent('Du har nådd grensen på: 2') + expect(element).toHaveClass('dnb-form-status--warn') + expect(document.querySelectorAll('i')).toHaveLength(2) + }) + + const removeButton = document.querySelector( + '.dnb-forms-iterate-remove-element-button' + ) + await userEvent.click(removeButton) + + await waitFor(() => { + expect(document.querySelector('.dnb-form-status')).toBeNull() + }) + }) }) diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/PushContainer/PushContainer.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/PushContainer/PushContainer.tsx index 4807185dd6c..eb8129a4198 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/PushContainer/PushContainer.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/PushContainer/PushContainer.tsx @@ -10,7 +10,7 @@ import OpenButton from './OpenButton' import { Flex, HeightAnimation } from '../../../../components' import { Path } from '../../types' import { SpacingProps } from '../../../../shared/types' -import { useSwitchContainerMode } from '../hooks' +import { useArrayLimit, useSwitchContainerMode } from '../hooks' import Toolbar from '../Toolbar' import { useTranslation } from '../../hooks' import { ArrayItemAreaProps } from '../Array/ArrayItemArea' @@ -73,6 +73,14 @@ function PushContainer(props: AllProps) { >({ path }) const { setNextContainerMode } = useSwitchContainerMode({ path }) + const { hasReachedLimit, setShowStatus } = useArrayLimit({ + path, + }) + const cancelHandler = useCallback(() => { + if (hasReachedLimit) { + setShowStatus(false) + } + }, [hasReachedLimit, setShowStatus]) const showOpenButton = showOpenButtonWhen?.(entries) const newItemContextProps: PushContainerContext = { @@ -94,10 +102,15 @@ function PushContainer(props: AllProps) { transformOnCommit={({ newItems }) => { return moveValueToPath(path, [...entries, ...newItems]) }} - onCommit={(data, { clearData }) => { - setNextContainerMode('view') - switchContainerModeRef.current?.('view') - clearData() + onCommit={(data, { clearData, preventCommit }) => { + if (hasReachedLimit) { + preventCommit() + setShowStatus(true) + } else { + setNextContainerMode('view') + switchContainerModeRef.current?.('view') + clearData() + } }} > @@ -110,6 +123,7 @@ function PushContainer(props: AllProps) { openButton={openButton} switchContainerModeRef={switchContainerModeRef} showOpenButton={showOpenButton} + cancelHandler={cancelHandler} {...rest} > {children} @@ -125,6 +139,7 @@ function NewContainer({ openButton, showOpenButton, switchContainerModeRef, + cancelHandler, children, ...rest }) { @@ -149,7 +164,9 @@ function NewContainer({ - {showOpenButton && } + {showOpenButton && ( + + )} ) diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/PushContainer/__tests__/PushContainer.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/PushContainer/__tests__/PushContainer.test.tsx index 50dcfeaa6f5..efeb0495643 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/PushContainer/__tests__/PushContainer.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/PushContainer/__tests__/PushContainer.test.tsx @@ -82,11 +82,11 @@ describe('PushContainer', () => { }) }) - it('should clear the input when the button is clicked', async () => { + it('should clear the input without an error, when the submit button is clicked', async () => { render( - + ) @@ -94,14 +94,25 @@ describe('PushContainer', () => { const input = document.querySelector('input') const button = document.querySelector('button') - await userEvent.type(input, 'Tony') + await userEvent.type(input, '1') + await userEvent.click(button) + + await waitFor(() => { + expect( + document.querySelector('.dnb-form-status') + ).toBeInTheDocument() + }) + + await userEvent.type(input, '{Backspace}Tony') expect(input).toHaveValue('Tony') await userEvent.click(button) expect(input).toHaveValue('') - expect(document.querySelector('.dnb-form-status')).toBeNull() + await waitFor(() => { + expect(document.querySelector('.dnb-form-status')).toBeNull() + }) }) it('should validate input values', async () => { @@ -559,4 +570,49 @@ describe('PushContainer', () => { await userEvent.click(removeButton) expect(openButton).toHaveTextContent('Add no. 1') }) + + it('should inherit "limit" prop from Array and show warning when limit is reached', async () => { + render( + + + + + + + + content + + + ) + + const doneButton = document.querySelector('button') + + // Add first item + await userEvent.click(doneButton) + + // Add second item + await userEvent.click(doneButton) + + expect(document.querySelector('.dnb-form-status')).toBeNull() + + // Try a third one + await userEvent.click(doneButton) + + await waitFor(() => { + const element = document.querySelector('.dnb-form-status') + expect(element).toBeInTheDocument() + expect(element).toHaveTextContent('Du har nådd grensen på: 2') + expect(element).toHaveClass('dnb-form-status--warn') + expect(document.querySelectorAll('i')).toHaveLength(2) + }) + + const removeButton = document.querySelector( + '.dnb-forms-iterate-remove-element-button' + ) + await userEvent.click(removeButton) + + await waitFor(() => { + expect(document.querySelector('.dnb-form-status')).toBeNull() + }) + }) }) diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/hooks/index.ts b/packages/dnb-eufemia/src/extensions/forms/Iterate/hooks/index.ts index 02f30c1811c..eeae985ca68 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/hooks/index.ts +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/hooks/index.ts @@ -1,2 +1,3 @@ export { default as useItem } from './useItem' export { default as useSwitchContainerMode } from './useSwitchContainerMode' +export { default as useArrayLimit } from './useArrayLimit' diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/hooks/useArrayLimit.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/hooks/useArrayLimit.tsx new file mode 100644 index 00000000000..856f99b3736 --- /dev/null +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/hooks/useArrayLimit.tsx @@ -0,0 +1,42 @@ +import { useCallback } from 'react' +import { useTranslation } from '../../hooks' +import { Path } from '../../types' +import { useSharedState } from '../../../../shared/helpers/useSharedState' + +type SharedState = { + show?: boolean + limit?: number + total?: number +} + +export default function useArrayLimit({ path }: { path: Path }) { + const sharedState = useSharedState(path + '-iterate-limit') + const { set, update, extend, data } = sharedState || {} + const { limit, total, show } = data || {} + + const setLimitProps = useCallback( + (props: Omit) => { + if (props.total !== total) { + update(props) + } else { + set(props) + } + }, + [set, total, update] + ) + + const setShowStatus = useCallback( + (show: boolean) => { + extend({ show }) + }, + [extend] + ) + + const hasReachedLimit = typeof limit === 'number' && total >= limit + const { itemsLimitReached } = useTranslation().IteratePushContainer + const error = show + ? new Error(itemsLimitReached.replace('{limit}', String(limit))) + : undefined + + return { setShowStatus, setLimitProps, error, hasReachedLimit } +} diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/Iterate.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/Iterate.stories.tsx index 8d4fba0a6e7..35e228692b3 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/Iterate.stories.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/Iterate.stories.tsx @@ -121,7 +121,7 @@ export const ViewAndEditContainer = () => { Accounts - + @@ -180,6 +180,7 @@ export const InitialOpen = () => { { const findFirstDuplication = (arr) => arr.findIndex((e, i) => arr.indexOf(e) !== i) + const count = arrayValue.filter(Boolean).length const index = findFirstDuplication(arrayValue) - if (index > -1) { + if (count > 1 && index > -1) { return new Error( 'You can not have duplicate items: ' + getCountryNameByIso(arrayValue.at(index) as string) diff --git a/packages/dnb-eufemia/src/extensions/forms/constants/locales/en-GB.ts b/packages/dnb-eufemia/src/extensions/forms/constants/locales/en-GB.ts index 2ec23ee403e..c455ab0cc64 100644 --- a/packages/dnb-eufemia/src/extensions/forms/constants/locales/en-GB.ts +++ b/packages/dnb-eufemia/src/extensions/forms/constants/locales/en-GB.ts @@ -33,26 +33,27 @@ export default { RemoveButton: { text: 'Remove', }, - IterateViewContainer: { - removeButton: 'Remove', - editButton: 'Edit', - }, SectionViewContainer: { editButton: 'Edit', }, SectionEditContainer: { doneButton: 'Done', cancelButton: 'Cancel', - errorInSection: 'Please correct the errors above', + errorInSection: 'Please correct the errors above.', + }, + IterateViewContainer: { + removeButton: 'Remove', + editButton: 'Edit', }, IterateEditContainer: { removeButton: 'Remove', doneButton: 'Done', cancelButton: 'Cancel', - errorInContainer: 'Please correct the errors above', + errorInContainer: 'Please correct the errors above.', }, IteratePushContainer: { createButton: 'Add', + itemsLimitReached: 'You have reached the limit of: {limit}', }, /** diff --git a/packages/dnb-eufemia/src/extensions/forms/constants/locales/nb-NO.ts b/packages/dnb-eufemia/src/extensions/forms/constants/locales/nb-NO.ts index 8b3ff6c54b3..0b569aa9381 100644 --- a/packages/dnb-eufemia/src/extensions/forms/constants/locales/nb-NO.ts +++ b/packages/dnb-eufemia/src/extensions/forms/constants/locales/nb-NO.ts @@ -33,26 +33,27 @@ export default { RemoveButton: { text: 'Fjern', }, - IterateViewContainer: { - removeButton: 'Fjern', - editButton: 'Endre', - }, SectionViewContainer: { editButton: 'Endre', }, SectionEditContainer: { doneButton: 'Ferdig', cancelButton: 'Avbryt', - errorInSection: 'Feilene ovenfor må rettes', + errorInSection: 'Feilene ovenfor må rettes.', + }, + IterateViewContainer: { + removeButton: 'Fjern', + editButton: 'Endre', }, IterateEditContainer: { removeButton: 'Fjern', doneButton: 'Ferdig', cancelButton: 'Avbryt', - errorInContainer: 'Feilene ovenfor må rettes', + errorInContainer: 'Feilene ovenfor må rettes.', }, IteratePushContainer: { createButton: 'Legg til', + itemsLimitReached: 'Du har nådd grensen på: {limit}', }, /** diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts index 43d21b498a2..90fcbaec974 100644 --- a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts +++ b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts @@ -1507,9 +1507,11 @@ export default function useFieldProps( // Use "useLayoutEffect" to be in sync with the data context "updateDataValueDataContext" routine further down. useLayoutEffect(() => { if (isEmptyData()) { + changedRef.current = false hideError() + clearErrorState() } - }, [externalValue, hideError, isEmptyData]) + }, [externalValue, clearErrorState, hideError, isEmptyData]) // ensure to include "externalValue" in order to properly remove errors // Use "useLayoutEffect" and "externalValueDidChangeRef" // to cooperate with the the data context "updateDataValueDataContext" routine further down, diff --git a/packages/dnb-eufemia/src/extensions/forms/types.ts b/packages/dnb-eufemia/src/extensions/forms/types.ts index dd9c92cd78e..97a19cf85a2 100644 --- a/packages/dnb-eufemia/src/extensions/forms/types.ts +++ b/packages/dnb-eufemia/src/extensions/forms/types.ts @@ -586,7 +586,10 @@ export type OnSubmit = ( export type OnCommit = ( data: Data, - { clearData }: { clearData: () => void } + { + clearData, + preventCommit, + }: { clearData: () => void; preventCommit?: () => void } ) => | EventReturnWithStateObject | void From 069874197569ec8996ed66eaa6ee274e1c892116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Fri, 20 Sep 2024 15:01:23 +0200 Subject: [PATCH 21/37] fix(Forms): ensure `Value.*` component's `transformIn` can changes its value (#3965) --- .../hooks/__tests__/useValueProps.test.tsx | 75 ++++++++++++------- .../extensions/forms/hooks/useValueProps.ts | 4 +- 2 files changed, 47 insertions(+), 32 deletions(-) diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useValueProps.test.tsx b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useValueProps.test.tsx index b6f71a8f03d..b4a69b54cfc 100644 --- a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useValueProps.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useValueProps.test.tsx @@ -10,15 +10,54 @@ import nbNO from '../../constants/locales/nb-NO' const nb = nbNO['nb-NO'] describe('useValueProps', () => { - it('should call use "transformIn" to prepare value', () => { - const value = 1 + describe('transformIn', () => { + it('should prepare value', () => { + const value = 1 - const transformIn = (value) => value + 1 - const { result } = renderHook(() => - useValueProps({ value, transformIn }) - ) + const transformIn = (value) => value + 1 + const { result } = renderHook(() => + useValueProps({ value, transformIn }) + ) - expect(result.current.value).toBe(2) + expect(result.current.value).toBe(2) + }) + + it('should prepare value when function instance changes', () => { + const { result, rerender } = renderHook(useValueProps, { + initialProps: { + value: 1, + transformIn: (value: number) => value + 1, + }, + }) + + expect(result.current.value).toBe(2) + + rerender({ value: 2, transformIn: (value: number) => value + 2 }) + + expect(result.current.value).toBe(4) + }) + + it('should prepare value from context', () => { + const path = '/contextValue' + + const transformIn = (value) => value + 1 + const { result } = renderHook( + () => useValueProps({ path, transformIn }), + { + wrapper: ({ children }) => ( + + {children} + + ), + } + ) + + expect(result.current.value).toBe(2) + }) }) it('should call use "toInput" to prepare value', () => { @@ -135,28 +174,6 @@ describe('useValueProps', () => { expect(result.current.value).toBe(2) }) - it('should use "transformIn" to prepare value from context', () => { - const path = '/contextValue' - - const transformIn = (value) => value + 1 - const { result } = renderHook( - () => useValueProps({ path, transformIn }), - { - wrapper: ({ children }) => ( - - {children} - - ), - } - ) - - expect(result.current.value).toBe(2) - }) - it('should get value from data context', () => { const path = '/contextValue' diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/useValueProps.ts b/packages/dnb-eufemia/src/extensions/forms/hooks/useValueProps.ts index 553ba2c9f9e..6e689fc4263 100644 --- a/packages/dnb-eufemia/src/extensions/forms/hooks/useValueProps.ts +++ b/packages/dnb-eufemia/src/extensions/forms/hooks/useValueProps.ts @@ -80,9 +80,7 @@ export default function useValueProps< ) const value = shouldBeVisible(path) - ? transformers.current.transformIn( - transformers.current.toInput(externalValue) - ) + ? transformIn(toInput(externalValue)) : undefined const label = From a0db2efa8f5def2660812c4e2e40c4d83cde3b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Fri, 20 Sep 2024 15:02:06 +0200 Subject: [PATCH 22/37] chore: make translations type safe (#3968) --- .../ChildrenWithAgeTranslations.ts | 152 +++++++++--------- .../forms/constants/locales/en-GB.ts | 3 +- .../dnb-eufemia/src/shared/locales/en-GB.ts | 3 +- .../dnb-eufemia/src/shared/locales/en-US.ts | 6 +- 4 files changed, 85 insertions(+), 79 deletions(-) diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAgeTranslations.ts b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAgeTranslations.ts index d13aaf29fe9..7555bcc63e5 100644 --- a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAgeTranslations.ts +++ b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAgeTranslations.ts @@ -1,80 +1,80 @@ -export const translations = { - 'nb-NO': { - ChildrenWithAge: { - hasChildren: { - title: 'Antall barn', - fieldLabel: 'Har du/dere barn under 18 år?', - required: 'Du må angi om du har barn under 18 år eller ikke.', - }, - countChildren: { - fieldLabel: 'Antall barn under 18 år', - required: 'Du må skrive inn antall barn.', - valueVale: 'Antall barn under 18 år', - suffix: 'barn', - }, - childrenAge: { - fieldLabel: 'Alder på barn nr. {itemNr}', - required: 'Du må skrive inn alder på barnet.', - suffix: 'år', - }, - hasJointResponsibility: { - fieldLabel: 'Har du delt omsorg for noen av barna?', - }, - confirmJointResponsibility: { - fieldLabel: 'Delt omsorg', - }, - usesDaycare: { - fieldLabel: 'Har du/dere SFO/AKS for noen av barna?', - required: 'Du må angi om du har SFO/AKS for noen av barna.', - }, - hasDaycare: { - fieldLabel: 'Er på SFO/AKS', - }, - jointResponsibilityTrue: 'delt omsorg', - jointResponsibilityFalse: 'ikke delt omsorg', - daycareTrue: 'SFO/AKS', - daycareFalse: 'uten SFO/AKS', - }, +const nbNO = { + hasChildren: { + title: 'Antall barn', + fieldLabel: 'Har du/dere barn under 18 år?', + required: 'Du må angi om du har barn under 18 år eller ikke.', + }, + countChildren: { + fieldLabel: 'Antall barn under 18 år', + required: 'Du må skrive inn antall barn.', + valueVale: 'Antall barn under 18 år', + suffix: 'barn', + }, + childrenAge: { + fieldLabel: 'Alder på barn nr. {itemNr}', + required: 'Du må skrive inn alder på barnet.', + suffix: 'år', + }, + hasJointResponsibility: { + fieldLabel: 'Har du delt omsorg for noen av barna?', }, - 'en-GB': { - ChildrenWithAge: { - hasChildren: { - title: 'Number of children', - fieldLabel: 'Do you have children under the age of 18?', - required: - 'You must state whether you have children under 18 or not.', - }, - countChildren: { - fieldLabel: 'Number of children under the age of 18', - required: 'You must enter the number of children.', - valueVale: 'Number of children under the age of 18', - suffix: 'children', - }, - childrenAge: { - fieldLabel: 'Age of child no. {itemNr}', - required: 'You must enter the age of the child.', - suffix: 'years old', - }, - hasJointResponsibility: { - fieldLabel: - 'Do you have joint parental responsibility for any of the children?', - }, - confirmJointResponsibility: { - fieldLabel: 'Joint parental responsibility', - }, - usesDaycare: { - fieldLabel: 'Do you have SFO/AKS for any of the children?', - required: - 'You must state whether you have SFO/AKS for any of the children.', - }, - hasDaycare: { - fieldLabel: 'Has SFO/AKS', - }, - jointResponsibilityTrue: 'joint parental responsibility', - jointResponsibilityFalse: 'not joint parental responsibility', - daycareTrue: 'SFO/AKS', - daycareFalse: 'without SFO/AKS', - }, + confirmJointResponsibility: { + fieldLabel: 'Delt omsorg', + }, + usesDaycare: { + fieldLabel: 'Har du/dere SFO/AKS for noen av barna?', + required: 'Du må angi om du har SFO/AKS for noen av barna.', + }, + hasDaycare: { + fieldLabel: 'Er på SFO/AKS', + }, + jointResponsibilityTrue: 'delt omsorg', + jointResponsibilityFalse: 'ikke delt omsorg', + daycareTrue: 'SFO/AKS', + daycareFalse: 'uten SFO/AKS', +} + +const enGB = { + hasChildren: { + title: 'Number of children', + fieldLabel: 'Do you have children under the age of 18?', + required: 'You must state whether you have children under 18 or not.', }, + countChildren: { + fieldLabel: 'Number of children under the age of 18', + required: 'You must enter the number of children.', + valueVale: 'Number of children under the age of 18', + suffix: 'children', + }, + childrenAge: { + fieldLabel: 'Age of child no. {itemNr}', + required: 'You must enter the age of the child.', + suffix: 'years old', + }, + hasJointResponsibility: { + fieldLabel: + 'Do you have joint parental responsibility for any of the children?', + }, + confirmJointResponsibility: { + fieldLabel: 'Joint parental responsibility', + }, + usesDaycare: { + fieldLabel: 'Do you have SFO/AKS for any of the children?', + required: + 'You must state whether you have SFO/AKS for any of the children.', + }, + hasDaycare: { + fieldLabel: 'Has SFO/AKS', + }, + jointResponsibilityTrue: 'joint parental responsibility', + jointResponsibilityFalse: 'not joint parental responsibility', + daycareTrue: 'SFO/AKS', + daycareFalse: 'without SFO/AKS', +} satisfies typeof nbNO + +export const translations = { + 'nb-NO': { ChildrenWithAge: nbNO }, + 'en-GB': { ChildrenWithAge: enGB }, } + export type Translation = (typeof translations)[keyof typeof translations] diff --git a/packages/dnb-eufemia/src/extensions/forms/constants/locales/en-GB.ts b/packages/dnb-eufemia/src/extensions/forms/constants/locales/en-GB.ts index c455ab0cc64..39fe56ad24c 100644 --- a/packages/dnb-eufemia/src/extensions/forms/constants/locales/en-GB.ts +++ b/packages/dnb-eufemia/src/extensions/forms/constants/locales/en-GB.ts @@ -1,3 +1,4 @@ +import nb from './nb-NO' export default { 'en-GB': { /** @@ -168,5 +169,5 @@ export default { errorRequired: 'You must upload a file.', errorInvalidFiles: 'Remove all files that have errors.', }, - }, + } satisfies (typeof nb)['nb-NO'], } diff --git a/packages/dnb-eufemia/src/shared/locales/en-GB.ts b/packages/dnb-eufemia/src/shared/locales/en-GB.ts index faee7c7ab10..3eaaa60cc30 100644 --- a/packages/dnb-eufemia/src/shared/locales/en-GB.ts +++ b/packages/dnb-eufemia/src/shared/locales/en-GB.ts @@ -1,3 +1,4 @@ +import nb from './nb-NO' export default { 'en-GB': { TextCounter: { @@ -164,5 +165,5 @@ export default { deleteButton: 'Delete', fileListAriaLabel: 'uploaded files', }, - }, + } satisfies (typeof nb)['nb-NO'], } diff --git a/packages/dnb-eufemia/src/shared/locales/en-US.ts b/packages/dnb-eufemia/src/shared/locales/en-US.ts index 40a25803e15..027f9df5b66 100644 --- a/packages/dnb-eufemia/src/shared/locales/en-US.ts +++ b/packages/dnb-eufemia/src/shared/locales/en-US.ts @@ -7,9 +7,13 @@ export default { ...enGB, DatePicker: { ...enGB.DatePicker, - first_day: 'sunday', mask_order: 'mm/dd/yyyy', mask_placeholder: 'mm/dd/yyyy', // have to be same setup as "mask" - but can be like: mm/dd/åååå + first_day: 'sunday', }, + } satisfies typeof enGB & { + DatePicker: { + first_day: string + } }, } From 87b0c0d21be7941ca533ca9307955cc56a406ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Fri, 20 Sep 2024 15:02:25 +0200 Subject: [PATCH 23/37] chore(Form.Section): remove code duplication (#3963) Switching the mode when there is a submit error happens on two other places already. We don't need this third one. --- .../Section/EditContainer/EditToolbarTools.tsx | 2 -- .../EditContainer/useEditContainerToolbar.ts | 15 --------------- 2 files changed, 17 deletions(-) delete mode 100644 packages/dnb-eufemia/src/extensions/forms/Form/Section/EditContainer/useEditContainerToolbar.ts diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Section/EditContainer/EditToolbarTools.tsx b/packages/dnb-eufemia/src/extensions/forms/Form/Section/EditContainer/EditToolbarTools.tsx index f9bde515659..c5ff6062a36 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Form/Section/EditContainer/EditToolbarTools.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Form/Section/EditContainer/EditToolbarTools.tsx @@ -5,10 +5,8 @@ import SectionContainerContext from '../containers/SectionContainerContext' import FieldBoundaryContext from '../../../DataContext/FieldBoundary/FieldBoundaryContext' import { check, close } from '../../../../../icons' import useContainerDataStore from './useContainerDataStore' -import useEditContainerToolbar from './useEditContainerToolbar' export default function EditToolbarTools() { - useEditContainerToolbar() const { restoreOriginalData } = useContainerDataStore() const { switchContainerMode, initialContainerMode } = diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Section/EditContainer/useEditContainerToolbar.ts b/packages/dnb-eufemia/src/extensions/forms/Form/Section/EditContainer/useEditContainerToolbar.ts deleted file mode 100644 index 190fab063d5..00000000000 --- a/packages/dnb-eufemia/src/extensions/forms/Form/Section/EditContainer/useEditContainerToolbar.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { useContext, useEffect } from 'react' -import SectionContainerContext from '../containers/SectionContainerContext' -import FieldBoundaryContext from '../../../DataContext/FieldBoundary/FieldBoundaryContext' - -export default function useEditContainerToolbar() { - const { switchContainerMode } = useContext(SectionContainerContext) || {} - const { hasSubmitError } = useContext(FieldBoundaryContext) || {} - - useEffect(() => { - // Ensure the edit container is always in edit mode when the form has a submit error - if (hasSubmitError) { - switchContainerMode?.('edit') - } - }, [hasSubmitError, switchContainerMode]) -} From f489182f57df018942130074dce567915fc1ed37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Fri, 20 Sep 2024 15:05:29 +0200 Subject: [PATCH 24/37] chore(BreadcrumbItem): rename the internal itemNr prop to itemNo (#3970) --- .../src/components/breadcrumb/BreadcrumbItem.tsx | 6 +++--- .../src/components/breadcrumb/BreadcrumbMultiple.tsx | 4 ++-- .../components/breadcrumb/__tests__/BreadcrumbItem.test.tsx | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/dnb-eufemia/src/components/breadcrumb/BreadcrumbItem.tsx b/packages/dnb-eufemia/src/components/breadcrumb/BreadcrumbItem.tsx index bd7eafd2136..a8024783adf 100644 --- a/packages/dnb-eufemia/src/components/breadcrumb/BreadcrumbItem.tsx +++ b/packages/dnb-eufemia/src/components/breadcrumb/BreadcrumbItem.tsx @@ -64,7 +64,7 @@ export type BreadcrumbItemProps = { skeleton?: SkeletonShow /** Internal */ - itemNr?: number + itemNo?: number } & (AnchorAllProps & Omit) & DataAttributeTypes @@ -107,7 +107,7 @@ const BreadcrumbItem = (localProps: BreadcrumbItemProps) => { onClick, variant, skeleton, - itemNr, + itemNo, ...props } = extendPropsWithContext( localProps, @@ -136,7 +136,7 @@ const BreadcrumbItem = (localProps: BreadcrumbItemProps) => { const currentText = text || (variant === 'home' && homeText) || '' const isInteractive = (href || onClick || props.to) && variant !== 'current' - const style = { '--delay': String(itemNr) } as React.CSSProperties + const style = { '--delay': String(itemNo) } as React.CSSProperties const iconToUse = variant === 'home' || currentIcon === 'home-icon' diff --git a/packages/dnb-eufemia/src/components/breadcrumb/BreadcrumbMultiple.tsx b/packages/dnb-eufemia/src/components/breadcrumb/BreadcrumbMultiple.tsx index 13992ee6d61..7c44e9de0d1 100644 --- a/packages/dnb-eufemia/src/components/breadcrumb/BreadcrumbMultiple.tsx +++ b/packages/dnb-eufemia/src/components/breadcrumb/BreadcrumbMultiple.tsx @@ -38,7 +38,7 @@ export const BreadcrumbMultiple = ({ (i == data.length - 1 && 'current') || null } - itemNr={i} + itemNo={i} {...breadcrumbItem} /> ) @@ -47,7 +47,7 @@ export const BreadcrumbMultiple = ({ {React.Children.toArray(items) .filter((item) => React.isValidElement(item)) .map((item: React.ReactElement, i) => - React.cloneElement(item, { key: i, itemNr: i }) + React.cloneElement(item, { key: i, itemNo: i }) )} diff --git a/packages/dnb-eufemia/src/components/breadcrumb/__tests__/BreadcrumbItem.test.tsx b/packages/dnb-eufemia/src/components/breadcrumb/__tests__/BreadcrumbItem.test.tsx index df5239232c9..da59cf0ae30 100644 --- a/packages/dnb-eufemia/src/components/breadcrumb/__tests__/BreadcrumbItem.test.tsx +++ b/packages/dnb-eufemia/src/components/breadcrumb/__tests__/BreadcrumbItem.test.tsx @@ -54,9 +54,9 @@ describe('BreadcrumbItem', () => { ) }) - it('should have delay style based on itemNr', () => { + it('should have delay style based on itemNo', () => { const { rerender } = render( - + ) expect(document.querySelector('li')).toHaveAttribute( @@ -64,7 +64,7 @@ describe('BreadcrumbItem', () => { '--delay: 1;' ) - rerender() + rerender() expect(document.querySelector('li')).toHaveAttribute( 'style', From 55ba97dc9956adaed291d9bafdaadbed5519157a Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 20 Sep 2024 15:20:34 +0200 Subject: [PATCH 25/37] docs: adds import statement to info doc for elements (#3972) --- .../src/docs/uilib/elements/blockquote/info.mdx | 6 ++++++ .../src/docs/uilib/elements/code.mdx | 6 ++++++ .../src/docs/uilib/elements/heading/info.mdx | 7 +++++++ .../docs/uilib/elements/horizontal-rule/info.mdx | 6 ++++++ .../src/docs/uilib/elements/image/info.mdx | 12 ++++++------ .../src/docs/uilib/elements/ingress/info.mdx | 14 ++++++-------- .../src/docs/uilib/elements/lead/info.mdx | 14 ++++++-------- .../src/docs/uilib/elements/lists/info.mdx | 6 ++++++ .../src/docs/uilib/elements/paragraph/demos.mdx | 5 +++++ .../src/docs/uilib/elements/paragraph/info.mdx | 16 +++++----------- 10 files changed, 59 insertions(+), 33 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/elements/blockquote/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/elements/blockquote/info.mdx index cd98b5aae4e..eee1e6381c6 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/elements/blockquote/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/elements/blockquote/info.mdx @@ -2,6 +2,12 @@ showTabs: true --- +## Import + +```tsx +import { Blockquote } from '@dnb/eufemia/elements' +``` + ## Description The blockquote element is used to indicate the quotation of a large section of text from another source. diff --git a/packages/dnb-design-system-portal/src/docs/uilib/elements/code.mdx b/packages/dnb-design-system-portal/src/docs/uilib/elements/code.mdx index 20e2b3263ff..94289578fe3 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/elements/code.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/elements/code.mdx @@ -4,6 +4,12 @@ title: 'Code' # Code +## Import + +```tsx +import { Code } from '@dnb/eufemia/elements' +``` + ## Code and Syntax highlighting [Prism](https://prismjs.com) is a popular Syntax Highlighting tool. DNB has its own **theme** you can use: diff --git a/packages/dnb-design-system-portal/src/docs/uilib/elements/heading/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/elements/heading/info.mdx index a2c23001c40..d352aa7acef 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/elements/heading/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/elements/heading/info.mdx @@ -2,6 +2,13 @@ showTabs: true --- +## Import + +```tsx +import { Heading } from '@dnb/eufemia/components' +import { H1, H2, H3, H4, H5, H6 } from '@dnb/eufemia/elements' +``` + ## Description Eufemia comes with three levels of heading styles: diff --git a/packages/dnb-design-system-portal/src/docs/uilib/elements/horizontal-rule/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/elements/horizontal-rule/info.mdx index 3d470eb6c5f..0a043de3a4d 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/elements/horizontal-rule/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/elements/horizontal-rule/info.mdx @@ -2,6 +2,12 @@ showTabs: true --- +## Import + +```tsx +import { Hr } from '@dnb/eufemia/elements' +``` + ## Description The `
    ` tag in HTML stands for horizontal rule and is used to insert a horizontal rule or a thematic break in an HTML page to divide or separate document sections. The `
    ` tag is an empty tag and it does not require an end tag. diff --git a/packages/dnb-design-system-portal/src/docs/uilib/elements/image/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/elements/image/info.mdx index ae6ca9d2468..929c859d373 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/elements/image/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/elements/image/info.mdx @@ -2,14 +2,14 @@ showTabs: true --- +## Import + +```tsx +import { Img } from '@dnb/eufemia/elements' +``` + ## Description The image element associated with the class `dnb-img` does not have much opinionated styling. It exists more to have a future possibility to optimize and add features. As of now, the React image "element" (Img) does provide a `figure` element with the `role="img"` and an `img` tag inside. This is mainly to support the [Skeleton](/uilib/components/skeleton) provider. - -```jsx -import { Img } from '@dnb/eufemia' - -render() -``` diff --git a/packages/dnb-design-system-portal/src/docs/uilib/elements/ingress/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/elements/ingress/info.mdx index ec7390b18c7..fd937d1814a 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/elements/ingress/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/elements/ingress/info.mdx @@ -2,14 +2,12 @@ showTabs: true --- -## Description - -Ingress is a brief, introductory paragraph that follows immediately after the title of an article. +## Import -## Ingress in React +```tsx +import { Ingress } from '@dnb/eufemia/elements' +``` -Ingress using React JSX. +## Description -```jsx -import { Ingress } from '@dnb/eufemia' -``` +Ingress is a brief, introductory paragraph that follows immediately after the title of an article. diff --git a/packages/dnb-design-system-portal/src/docs/uilib/elements/lead/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/elements/lead/info.mdx index 34dea38ddca..a2a4f2c24d9 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/elements/lead/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/elements/lead/info.mdx @@ -2,6 +2,12 @@ showTabs: true --- +## Import + +```tsx +import { Lead } from '@dnb/eufemia/elements' +``` + ## Description A lead paragraph is the opening paragraph of an article, etc. @@ -9,11 +15,3 @@ A lead paragraph is the opening paragraph of an article, etc. ## Paragraph class modifiers Eufemia comes with [several styles](/uilib/elements/paragraph/#paragraph-class-modifiers) you can use on paragraphs and other HTML text elements, where `.dnb-p--lead` is one of them. - -### Lead in React - -Lead using React JSX. - -```jsx -import { Lead } from '@dnb/eufemia' -``` diff --git a/packages/dnb-design-system-portal/src/docs/uilib/elements/lists/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/elements/lists/info.mdx index a63b67abcbd..67364e39f8b 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/elements/lists/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/elements/lists/info.mdx @@ -2,6 +2,12 @@ showTabs: true --- +## Import + +```tsx +import { Dd, Dl, Dt, Ol, Ul } from '@dnb/eufemia/elements' +``` + ## Description Lists are used to specify lists of information. diff --git a/packages/dnb-design-system-portal/src/docs/uilib/elements/paragraph/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/elements/paragraph/demos.mdx index 4bf48894885..1da55828471 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/elements/paragraph/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/elements/paragraph/demos.mdx @@ -7,10 +7,15 @@ import { ParagraphSmall, ParagraphAdditional, ParagraphRegressionTests, + ParagraphModifiers, } from 'Docs/uilib/elements/paragraph/Examples' ## Demos +### Paragraphs modifiers + + + ### Paragraphs `basis` sized diff --git a/packages/dnb-design-system-portal/src/docs/uilib/elements/paragraph/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/elements/paragraph/info.mdx index cea6f1570bd..bb55617fbed 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/elements/paragraph/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/elements/paragraph/info.mdx @@ -2,7 +2,11 @@ showTabs: true --- -import { ParagraphModifiers } from 'Docs/uilib/elements/paragraph/Examples' +## Import + +```tsx +import { P } from '@dnb/eufemia/elements' +``` ## Description @@ -26,14 +30,4 @@ Eufemia comes with several styles you can use on paragraphs and other HTML text - `.dnb-p--lead` -### Paragraphs in React - -Paragraphs using React JSX. - -```jsx -import { P } from '@dnb/eufemia' -``` - - - Read more [about Fonts in the Designer Guides](/quickguide-designer/fonts/). From 7be44770c9c779c1aedd4c386694cce772f4d3fe Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 20 Sep 2024 15:21:39 +0200 Subject: [PATCH 26/37] chore: change type of innerRef to React.Ref for Checkbox (#3971) --- packages/dnb-eufemia/src/components/checkbox/CheckboxDocs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dnb-eufemia/src/components/checkbox/CheckboxDocs.ts b/packages/dnb-eufemia/src/components/checkbox/CheckboxDocs.ts index 6d85627884b..9db67ef4f89 100644 --- a/packages/dnb-eufemia/src/components/checkbox/CheckboxDocs.ts +++ b/packages/dnb-eufemia/src/components/checkbox/CheckboxDocs.ts @@ -68,7 +68,7 @@ export const CheckboxProperties: PropertiesTableProps = { }, innerRef: { doc: 'By providing a React.ref we can get the internally used input element (DOM). E.g. `innerRef={myRef}` by using `React.createRef()` or `React.useRef()`.', - type: 'string', + type: 'React.Ref', status: 'optional', }, '[Space](/uilib/layout/space/properties)': { From 594f9675c0cf2888ebc6a222ebd20d9d36003b03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Fri, 20 Sep 2024 15:56:58 +0200 Subject: [PATCH 27/37] fix(Forms): deprecate and rename `{itemNr}` in favor of `{itemNo}` (#3969) --- .../releases/eufemia/v11-info.mdx | 4 +++ .../docs/uilib/extensions/forms/Examples.tsx | 6 ++-- .../Iterate/AnimatedContainer/Examples.tsx | 2 +- .../forms/Iterate/AnimatedContainer/info.mdx | 4 +-- .../forms/Iterate/Array/Examples.tsx | 10 +++--- .../extensions/forms/Iterate/Array/info.mdx | 12 +++---- .../forms/Iterate/EditContainer/info.mdx | 6 ++-- .../forms/Iterate/PushContainer/Examples.tsx | 6 ++-- .../forms/Iterate/ViewContainer/info.mdx | 4 +-- .../forms/FieldBlock/FieldBlock.tsx | 2 +- .../__tests__/AnimatedContainer.test.tsx | 4 +-- .../Iterate/Array/__tests__/Array.test.tsx | 12 +++---- .../Iterate/EditContainer/EditContainer.tsx | 11 ++++-- .../__tests__/EditContainer.test.tsx | 8 ++--- .../Iterate/ViewContainer/ViewContainer.tsx | 4 +-- .../__tests__/ViewContainer.test.tsx | 4 +-- .../forms/Iterate/stories/Iterate.stories.tsx | 8 ++--- .../forms/ValueBlock/ValueBlock.tsx | 2 +- .../ChildrenWithAgeTranslations.ts | 4 +-- .../__tests__/ChildrenWithAge.test.tsx | 4 +-- .../ChildrenWithAge.test.tsx.snap | 36 +++++++++---------- 21 files changed, 82 insertions(+), 71 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/about-the-lib/releases/eufemia/v11-info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/about-the-lib/releases/eufemia/v11-info.mdx index 6e6fc8b1b52..8231a2740e9 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/about-the-lib/releases/eufemia/v11-info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/about-the-lib/releases/eufemia/v11-info.mdx @@ -129,4 +129,8 @@ The `InputPassword` component has been moved to `Field.Password`, and is now a p - replace `withValue` with `hasValue`. +## Form.Iterate + +- Rename label variable `{itemNr}` to `{itemNo}`. + _February, 6. 2024_ diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Examples.tsx index 64876092319..5b2f771449f 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Examples.tsx @@ -578,8 +578,8 @@ export const UsingIterate = () => { const MyEditItem = () => { return ( @@ -591,7 +591,7 @@ export const UsingIterate = () => { console.log('index:', item.index) return ( - + diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/AnimatedContainer/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/AnimatedContainer/Examples.tsx index 697deb4f353..59a45d025ca 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/AnimatedContainer/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/AnimatedContainer/Examples.tsx @@ -20,7 +20,7 @@ export const Default = () => { path="/myList" placeholder={<>Empty list} > - + diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/AnimatedContainer/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/AnimatedContainer/info.mdx index 17c807bf6f3..62b4ec6a3c3 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/AnimatedContainer/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/AnimatedContainer/info.mdx @@ -19,14 +19,14 @@ render( ## The item number in the title -You can use the `{itemNr}` variable in the `title` or the `titleWhenNew` property to display the current item number. +You can use the `{itemNo}` variable in the `title` or the `titleWhenNew` property to display the current item number. ```tsx import { Iterate, Field, Value } from '@dnb/eufemia/extensions/forms' render( - + , diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Array/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Array/Examples.tsx index fc8d1405889..f8376cda4a9 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Array/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/Array/Examples.tsx @@ -239,8 +239,8 @@ export const ViewAndEditContainer = () => { const MyEditItem = () => { return ( @@ -252,7 +252,7 @@ export const ViewAndEditContainer = () => { console.log('index:', item.index) return ( - + @@ -331,7 +331,7 @@ export const DynamicPathValue = () => { : { myObject: index } } > - +
    @@ -486,7 +486,7 @@ export const WithArrayValidator = () => { > - + , ) ``` -The [ViewContainer](/uilib/extensions/forms/Iterate/ViewContainer) and the [EditContainer](/uilib/extensions/forms/Iterate/EditContainer) also supports `{itemNr}` in the `title` property to display the current item number. +The [ViewContainer](/uilib/extensions/forms/Iterate/ViewContainer) and the [EditContainer](/uilib/extensions/forms/Iterate/EditContainer) also supports `{itemNo}` in the `title` property to display the current item number. ```tsx import { Iterate, Field } from '@dnb/eufemia/extensions/forms' render( - + ... , @@ -127,8 +127,8 @@ render( }} > - - + + , ) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/EditContainer/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/EditContainer/info.mdx index bc187efbd48..4dcd97f104b 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/EditContainer/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/EditContainer/info.mdx @@ -30,7 +30,7 @@ render( ## The item number in the title -You can use the `{itemNr}` variable in the `title` or the `titleWhenNew` property to display the current item number. +You can use the `{itemNo}` variable in the `title` or the `titleWhenNew` property to display the current item number. ```tsx import { Iterate, Field, Value } from '@dnb/eufemia/extensions/forms' @@ -38,8 +38,8 @@ import { Iterate, Field, Value } from '@dnb/eufemia/extensions/forms' render( diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/PushContainer/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/PushContainer/Examples.tsx index 6d9992ff1b4..b61a1910194 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/PushContainer/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/PushContainer/Examples.tsx @@ -29,8 +29,8 @@ export const InitiallyOpen = () => { const MyEditItem = () => { return ( @@ -42,7 +42,7 @@ export const InitiallyOpen = () => { console.log('index:', item.index) return ( - + diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/ViewContainer/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/ViewContainer/info.mdx index 5205c5e49d6..033c2697c3b 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/ViewContainer/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Iterate/ViewContainer/info.mdx @@ -30,14 +30,14 @@ render( ## The item number in the title -You can use the `{itemNr}` variable in the `title` property to display the current item number. +You can use the `{itemNo}` variable in the `title` property to display the current item number. ```tsx import { Iterate, Value } from '@dnb/eufemia/extensions/forms' render( - + , diff --git a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/FieldBlock.tsx b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/FieldBlock.tsx index 60457f7b5b4..3a2f5e2c0c8 100644 --- a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/FieldBlock.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/FieldBlock.tsx @@ -140,7 +140,7 @@ function FieldBlock(props: Props) { if (iterateIndex !== undefined) { content = convertJsxToString(labelProp).replace( - '{itemNr}', + '{itemNo}', String(iterateIndex + 1) ) } diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/AnimatedContainer/__tests__/AnimatedContainer.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/AnimatedContainer/__tests__/AnimatedContainer.test.tsx index 30dca4a885b..f401ddae8b1 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/AnimatedContainer/__tests__/AnimatedContainer.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/AnimatedContainer/__tests__/AnimatedContainer.test.tsx @@ -33,10 +33,10 @@ describe('AnimatedContainer', () => { ) }) - it('should render title with "itemNr"', () => { + it('should render title with "itemNo"', () => { render( - + content diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/Array/__tests__/Array.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/Array/__tests__/Array.test.tsx index 1774ca88415..2f4a0ac609b 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/Array/__tests__/Array.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/Array/__tests__/Array.test.tsx @@ -125,11 +125,11 @@ describe('Iterate.Array', () => { }) describe('label', () => { - it('should replace {itemNr} in labels for fields and values', () => { + it('should replace {itemNo} in labels for fields and values', () => { render( - - + + ) @@ -142,13 +142,13 @@ describe('Iterate.Array', () => { expect(valueLabel2).toHaveTextContent('Value label 2') }) - it('should replace {itemNr} in labels for FieldBlock and ValueBlock', () => { + it('should replace {itemNo} in labels for FieldBlock and ValueBlock', () => { render( - + content - + content diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/EditContainer.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/EditContainer.tsx index c9bff7b0cca..7da3dbf8647 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/EditContainer.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/EditContainer.tsx @@ -90,8 +90,15 @@ export function EditContainerWithoutToolbar( const wasNew = useWasNew({ isNew, containerMode }) let itemTitle = wasNew && titleWhenNew ? titleWhenNew : title let ariaLabel = useMemo(() => convertJsxToString(itemTitle), [itemTitle]) - if (ariaLabel.includes('{itemNr}')) { - itemTitle = ariaLabel = ariaLabel.replace('{itemNr}', index + 1) + if (ariaLabel.includes('{itemN')) { + /** + * {itemNr} is deprecated, and can be removed in v11 in favor of {itemNo} + * So in v11 we can use '{itemNo}' instead of a regex + */ + itemTitle = ariaLabel = ariaLabel.replace( + /\{itemN(r|o)\}/g, + String(index + 1) + ) } useSwitchContainerMode({ path }) diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/__tests__/EditContainer.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/__tests__/EditContainer.test.tsx index 920ff488216..2c8674a450a 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/__tests__/EditContainer.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/EditContainer/__tests__/EditContainer.test.tsx @@ -134,10 +134,10 @@ describe('EditContainer', () => { ) }) - it('should render title with "itemNr"', () => { + it('should render title with "itemNo"', () => { render( - content + content ) @@ -147,12 +147,12 @@ describe('EditContainer', () => { expect(leads[1]).toHaveTextContent('Item title 2') }) - it('should render titleWhenNew with "itemNr"', () => { + it('should render titleWhenNew with "itemNo"', () => { render( - + content diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/ViewContainer/ViewContainer.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/ViewContainer/ViewContainer.tsx index 0ab74348dd9..37defa7f598 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/ViewContainer/ViewContainer.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/ViewContainer/ViewContainer.tsx @@ -40,8 +40,8 @@ function ViewContainer(props: AllProps) { let itemTitle = title let ariaLabel = useMemo(() => convertJsxToString(itemTitle), [itemTitle]) - if (ariaLabel.includes('{itemNr}')) { - itemTitle = ariaLabel = ariaLabel.replace('{itemNr}', index + 1) + if (ariaLabel.includes('{itemNo}')) { + itemTitle = ariaLabel = ariaLabel.replace('{itemNo}', index + 1) } let toolbarElement = toolbar diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/ViewContainer/__tests__/ViewContainer.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/ViewContainer/__tests__/ViewContainer.test.tsx index dba3f137ee5..ca9e6d18bd0 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/ViewContainer/__tests__/ViewContainer.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/ViewContainer/__tests__/ViewContainer.test.tsx @@ -64,10 +64,10 @@ describe('ViewContainer', () => { ) }) - it('should render title with "itemNr"', () => { + it('should render title with "itemNo"', () => { render( - content + content ) diff --git a/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/Iterate.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/Iterate.stories.tsx index 35e228692b3..141db4d1067 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/Iterate.stories.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Iterate/stories/Iterate.stories.tsx @@ -19,7 +19,7 @@ export const AnimatedContainer = () => { Empty list}> - + @@ -65,8 +65,8 @@ const MyEditItemForm = () => { const MyEditItem = (props) => { return ( @@ -91,7 +91,7 @@ const CreateNewEntry = () => { const MyViewItem = () => { return ( - + diff --git a/packages/dnb-eufemia/src/extensions/forms/ValueBlock/ValueBlock.tsx b/packages/dnb-eufemia/src/extensions/forms/ValueBlock/ValueBlock.tsx index c2b56c6a2b2..9a16ca6bb74 100644 --- a/packages/dnb-eufemia/src/extensions/forms/ValueBlock/ValueBlock.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/ValueBlock/ValueBlock.tsx @@ -58,7 +58,7 @@ function ValueBlock(props: Props) { } if (iterateIndex !== undefined) { return convertJsxToString(labelProp).replace( - '{itemNr}', + '{itemNo}', String(iterateIndex + 1) ) } diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAgeTranslations.ts b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAgeTranslations.ts index 7555bcc63e5..ba4188a2a1f 100644 --- a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAgeTranslations.ts +++ b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAgeTranslations.ts @@ -11,7 +11,7 @@ const nbNO = { suffix: 'barn', }, childrenAge: { - fieldLabel: 'Alder på barn nr. {itemNr}', + fieldLabel: 'Alder på barn nr. {itemNo}', required: 'Du må skrive inn alder på barnet.', suffix: 'år', }, @@ -47,7 +47,7 @@ const enGB = { suffix: 'children', }, childrenAge: { - fieldLabel: 'Age of child no. {itemNr}', + fieldLabel: 'Age of child no. {itemNo}', required: 'You must enter the age of the child.', suffix: 'years old', }, 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 1540c3b897f..fefe9347e2c 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 @@ -70,7 +70,7 @@ describe('ChildrenWithAge', () => { screen.queryByText( translations[ 'nb-NO' - ].ChildrenWithAge.childrenAge.fieldLabel.replace('{itemNr}', '1') + ].ChildrenWithAge.childrenAge.fieldLabel.replace('{itemNo}', '1') ) ).toBeInTheDocument() }) @@ -102,7 +102,7 @@ describe('ChildrenWithAge', () => { const childrenAgeFieldBlock = screen.queryByText( translationsNO.ChildrenWithAge.childrenAge.fieldLabel.replace( - '{itemNr}', + '{itemNo}', '1' ) ).parentElement.parentElement.parentElement 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 2ed01d2370c..7a0537f0a34 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 @@ -31,7 +31,7 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` "en-GB": { "ChildrenWithAge": { "childrenAge": { - "fieldLabel": "Age of child no. {itemNr}", + "fieldLabel": "Age of child no. {itemNo}", "required": "You must enter the age of the child.", "suffix": "years old", }, @@ -68,7 +68,7 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` "nb-NO": { "ChildrenWithAge": { "childrenAge": { - "fieldLabel": "Alder på barn nr. {itemNr}", + "fieldLabel": "Alder på barn nr. {itemNo}", "required": "Du må skrive inn alder på barnet.", "suffix": "år", }, @@ -116,7 +116,7 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` "en-GB": { "ChildrenWithAge": { "childrenAge": { - "fieldLabel": "Age of child no. {itemNr}", + "fieldLabel": "Age of child no. {itemNo}", "required": "You must enter the age of the child.", "suffix": "years old", }, @@ -153,7 +153,7 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` "nb-NO": { "ChildrenWithAge": { "childrenAge": { - "fieldLabel": "Alder på barn nr. {itemNr}", + "fieldLabel": "Alder på barn nr. {itemNo}", "required": "Du må skrive inn alder på barnet.", "suffix": "år", }, @@ -202,7 +202,7 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` "en-GB": { "ChildrenWithAge": { "childrenAge": { - "fieldLabel": "Age of child no. {itemNr}", + "fieldLabel": "Age of child no. {itemNo}", "required": "You must enter the age of the child.", "suffix": "years old", }, @@ -239,7 +239,7 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` "nb-NO": { "ChildrenWithAge": { "childrenAge": { - "fieldLabel": "Alder på barn nr. {itemNr}", + "fieldLabel": "Alder på barn nr. {itemNo}", "required": "Du må skrive inn alder på barnet.", "suffix": "år", }, @@ -307,7 +307,7 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` "en-GB": { "ChildrenWithAge": { "childrenAge": { - "fieldLabel": "Age of child no. {itemNr}", + "fieldLabel": "Age of child no. {itemNo}", "required": "You must enter the age of the child.", "suffix": "years old", }, @@ -344,7 +344,7 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` "nb-NO": { "ChildrenWithAge": { "childrenAge": { - "fieldLabel": "Alder på barn nr. {itemNr}", + "fieldLabel": "Alder på barn nr. {itemNo}", "required": "Du må skrive inn alder på barnet.", "suffix": "år", }, @@ -392,7 +392,7 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` "en-GB": { "ChildrenWithAge": { "childrenAge": { - "fieldLabel": "Age of child no. {itemNr}", + "fieldLabel": "Age of child no. {itemNo}", "required": "You must enter the age of the child.", "suffix": "years old", }, @@ -429,7 +429,7 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` "nb-NO": { "ChildrenWithAge": { "childrenAge": { - "fieldLabel": "Alder på barn nr. {itemNr}", + "fieldLabel": "Alder på barn nr. {itemNo}", "required": "Du må skrive inn alder på barnet.", "suffix": "år", }, @@ -478,7 +478,7 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` "en-GB": { "ChildrenWithAge": { "childrenAge": { - "fieldLabel": "Age of child no. {itemNr}", + "fieldLabel": "Age of child no. {itemNo}", "required": "You must enter the age of the child.", "suffix": "years old", }, @@ -515,7 +515,7 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` "nb-NO": { "ChildrenWithAge": { "childrenAge": { - "fieldLabel": "Alder på barn nr. {itemNr}", + "fieldLabel": "Alder på barn nr. {itemNo}", "required": "Du må skrive inn alder på barnet.", "suffix": "år", }, @@ -567,7 +567,7 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` "en-GB": { "ChildrenWithAge": { "childrenAge": { - "fieldLabel": "Age of child no. {itemNr}", + "fieldLabel": "Age of child no. {itemNo}", "required": "You must enter the age of the child.", "suffix": "years old", }, @@ -604,7 +604,7 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` "nb-NO": { "ChildrenWithAge": { "childrenAge": { - "fieldLabel": "Alder på barn nr. {itemNr}", + "fieldLabel": "Alder på barn nr. {itemNo}", "required": "Du må skrive inn alder på barnet.", "suffix": "år", }, @@ -673,7 +673,7 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` "en-GB": { "ChildrenWithAge": { "childrenAge": { - "fieldLabel": "Age of child no. {itemNr}", + "fieldLabel": "Age of child no. {itemNo}", "required": "You must enter the age of the child.", "suffix": "years old", }, @@ -710,7 +710,7 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` "nb-NO": { "ChildrenWithAge": { "childrenAge": { - "fieldLabel": "Alder på barn nr. {itemNr}", + "fieldLabel": "Alder på barn nr. {itemNo}", "required": "Du må skrive inn alder på barnet.", "suffix": "år", }, @@ -766,7 +766,7 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` "en-GB": { "ChildrenWithAge": { "childrenAge": { - "fieldLabel": "Age of child no. {itemNr}", + "fieldLabel": "Age of child no. {itemNo}", "required": "You must enter the age of the child.", "suffix": "years old", }, @@ -803,7 +803,7 @@ exports[`ChildrenWithAge should match snapshot 1`] = ` "nb-NO": { "ChildrenWithAge": { "childrenAge": { - "fieldLabel": "Alder på barn nr. {itemNr}", + "fieldLabel": "Alder på barn nr. {itemNo}", "required": "Du må skrive inn alder på barnet.", "suffix": "år", }, From 77eab0c2a9a71222565722835c3a02508c89eb7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Fri, 20 Sep 2024 15:57:44 +0200 Subject: [PATCH 28/37] fix(Forms): execute `transformOut` when value or defaultValue is given (#3974) --- .../create-component/useFieldProps/info.mdx | 2 + .../Field/SelectCountry/SelectCountry.tsx | 8 +- .../Field/String/__tests__/String.test.tsx | 4 +- .../extensions/forms/hooks/DataValueDocs.ts | 2 +- .../hooks/__tests__/useFieldProps.test.tsx | 38 ++++++- .../extensions/forms/hooks/useFieldProps.ts | 102 ++++++++++-------- .../dnb-eufemia/src/extensions/forms/types.ts | 13 ++- 7 files changed, 108 insertions(+), 61 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/create-component/useFieldProps/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/create-component/useFieldProps/info.mdx index 91fd4fd8ccc..bb696d78e5c 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/create-component/useFieldProps/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/create-component/useFieldProps/info.mdx @@ -253,6 +253,8 @@ They should return a transformed value: `(value) => value` - `transformValue` transforms the value given by `handleChange` after `fromInput` and before `updateValue` and `toEvent`. The second parameter returns the current value. +- `provideAdditionalArgs` provide a function that can be called by the field to provide additional parameters, so events (`onFocus`, `onBlur` and `onChange`) and transformers (`transformOut`) get an additional parameter when transforming the value. + In addition there are **field value transformers** which should be used outside of the field component (by the field consumer): - `transformIn` transforms the `value` before its displayed in the field (e.g. input). diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/SelectCountry.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/SelectCountry.tsx index 62d5504e8b6..fcb19f78cae 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/SelectCountry.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/SelectCountry.tsx @@ -50,14 +50,12 @@ function SelectCountry(props: Props) { const translations = useTranslation().SelectCountry const lang = sharedContext.locale?.split('-')[0] as CountryLang - const transformAdditionalArgs = (additionalArgs: CountryType, value) => { + const provideAdditionalArgs = useCallback((value) => { const country = countries.find(({ iso }) => value === iso) if (country?.iso) { return country - } else { - return additionalArgs } - } + }, []) const errorMessages = useErrorMessage(props.path, props.errorMessages, { required: translations.errorRequired, @@ -69,7 +67,7 @@ function SelectCountry(props: Props) { const preparedProps: Props = { ...defaultProps, ...props, - transformAdditionalArgs, + provideAdditionalArgs, } const { diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/String/__tests__/String.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/String/__tests__/String.test.tsx index 0a443c6d8b6..31b6c96ece1 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/String/__tests__/String.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/String/__tests__/String.test.tsx @@ -277,7 +277,7 @@ describe('Field.String', () => { expect(transformIn).toHaveBeenCalledTimes(7) expect(transformIn).toHaveBeenLastCalledWith('abc') expect(transformOut).toHaveBeenCalledTimes(6) - expect(transformOut).toHaveBeenLastCalledWith('ABc') + expect(transformOut).toHaveBeenLastCalledWith('ABc', undefined) expect(onChangeProvider).toHaveBeenCalledTimes(6) expect(onChangeProvider).toHaveBeenLastCalledWith( { myField: 'abc' }, @@ -292,7 +292,7 @@ describe('Field.String', () => { expect(transformIn).toHaveBeenCalledTimes(13) expect(transformIn).toHaveBeenLastCalledWith('efg') expect(transformOut).toHaveBeenCalledTimes(12) - expect(transformOut).toHaveBeenLastCalledWith('EFG') + expect(transformOut).toHaveBeenLastCalledWith('EFG', undefined) expect(onChangeProvider).toHaveBeenCalledTimes(12) expect(onChangeProvider).toHaveBeenLastCalledWith( { myField: 'efg' }, diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/DataValueDocs.ts b/packages/dnb-eufemia/src/extensions/forms/hooks/DataValueDocs.ts index 3a8e70a2065..53b70ff900b 100644 --- a/packages/dnb-eufemia/src/extensions/forms/hooks/DataValueDocs.ts +++ b/packages/dnb-eufemia/src/extensions/forms/hooks/DataValueDocs.ts @@ -92,7 +92,7 @@ export const dataValueProperties: PropertiesTableProps = { status: 'optional', }, transformOut: { - doc: 'Transforms the value before it gets forwarded to the form data object or returned as the `onChange` value parameter.', + doc: 'Transforms the value before it gets forwarded to the form data object (context) or returned as the `onChange` value parameter. The first parameter is the internal value. Some fields to support a second parameter, like the SelectCountry, where the country object is given.', type: 'function', status: 'optional', }, diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx index 61be05796da..dc5a7433ec8 100644 --- a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx @@ -2229,7 +2229,7 @@ describe('useFieldProps', () => { expect(transformIn).toHaveBeenCalledTimes(2) expect(transformIn).toHaveBeenLastCalledWith(3) expect(transformOut).toHaveBeenCalledTimes(1) - expect(transformOut).toHaveBeenLastCalledWith(2) + expect(transformOut).toHaveBeenLastCalledWith(2, undefined) act(() => { handleChange(4) @@ -2238,7 +2238,37 @@ describe('useFieldProps', () => { expect(transformIn).toHaveBeenCalledTimes(3) expect(transformIn).toHaveBeenLastCalledWith(5) expect(transformOut).toHaveBeenCalledTimes(2) - expect(transformOut).toHaveBeenLastCalledWith(4) + expect(transformOut).toHaveBeenLastCalledWith(4, undefined) + }) + + it('should call "transformOut" initially when value is given', () => { + const transformOut = jest.fn((v) => v + 1) + const defaultValue = 1 + + const { result } = renderHook( + () => useFieldProps({ path: '/foo', transformOut, defaultValue }), + { wrapper: Provider } + ) + + expect(result.current.dataContext.data).toEqual({ + foo: 2, + }) + expect(transformOut).toHaveBeenCalledTimes(1) + }) + + it('should call "transformOut" initially when defaultValue is given', () => { + const transformOut = jest.fn((v) => v + 1) + const defaultValue = 1 + + const { result } = renderHook( + () => useFieldProps({ path: '/foo', transformOut, defaultValue }), + { wrapper: Provider } + ) + + expect(result.current.dataContext.data).toEqual({ + foo: 2, + }) + expect(transformOut).toHaveBeenCalledTimes(1) }) it('should call "transformIn" and "transformOut" after "fromInput" and "toInput"', () => { @@ -2272,7 +2302,7 @@ describe('useFieldProps', () => { expect(transformIn).toHaveBeenCalledTimes(2) expect(transformIn).toHaveBeenLastCalledWith(3) expect(transformOut).toHaveBeenCalledTimes(1) - expect(transformOut).toHaveBeenLastCalledWith(3) + expect(transformOut).toHaveBeenLastCalledWith(3, undefined) act(() => { handleChange(4) @@ -2281,7 +2311,7 @@ describe('useFieldProps', () => { expect(transformIn).toHaveBeenCalledTimes(3) expect(transformIn).toHaveBeenLastCalledWith(5) expect(transformOut).toHaveBeenCalledTimes(2) - expect(transformOut).toHaveBeenLastCalledWith(5) + expect(transformOut).toHaveBeenLastCalledWith(5, undefined) }) it('should call "fromInput" and "toInput"', () => { diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts index 90fcbaec974..6f17dac5537 100644 --- a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts +++ b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts @@ -114,13 +114,15 @@ export default function useFieldProps( fromInput = (value: Value) => value, toEvent = (value: Value) => value, transformValue = (value: Value) => value, - transformAdditionalArgs = (additionalArgs: AdditionalEventArgs) => - additionalArgs, + provideAdditionalArgs = ( + value: Value, + additionalArgs: AdditionalEventArgs + ) => additionalArgs, fromExternal = (value: Value) => value, validateRequired = (value, { emptyValue, required, error }) => { const res = required && - ((value as any) === emptyValue || + ((value as unknown) === emptyValue || (typeof emptyValue === 'undefined' && value === '')) ? error : undefined @@ -154,7 +156,7 @@ export default function useFieldProps( const transformers = useRef({ transformIn, transformOut, - transformAdditionalArgs, + provideAdditionalArgs, toInput, fromInput, toEvent, @@ -1023,39 +1025,49 @@ export default function useFieldProps( } }, [continuousValidation, hideError, revealError]) + const getEventArgs = useCallback( + ({ + eventName, + additionalArgs, + overrideValue = undefined, + }): [Value] | [Value, AdditionalEventArgs] => { + const value = transformers.current.toEvent( + overrideValue ?? valueRef.current, + eventName + ) + const args = transformers.current.provideAdditionalArgs( + value, + additionalArgs + ) + + if (typeof args !== 'undefined') { + return [value, args] + } + + return [value] + }, + [] + ) + const setHasFocus = useCallback( async ( hasFocus: boolean, overrideValue?: Value, additionalArgs?: AdditionalEventArgs ) => { - const getArgs = ( - type: Parameters[1] - ) => { - const value = transformers.current.toEvent( - overrideValue ?? valueRef.current, - type - ) - const transformedAdditionalArgs = - transformers.current.transformAdditionalArgs( - additionalArgs, - value - ) - - return typeof transformedAdditionalArgs !== 'undefined' - ? [value, transformedAdditionalArgs] - : [value] - } + const args = getEventArgs({ + eventName: hasFocus ? 'onFocus' : 'onBlur', + overrideValue, + additionalArgs, + }) if (hasFocus) { // Field was put in focus (like when clicking in a text field or opening a dropdown menu) hasFocusRef.current = true - const args = getArgs('onFocus') onFocus?.apply(this, args) } else { // Field was removed from focus (like when tabbing out of a text field or closing a dropdown menu) hasFocusRef.current = false - const args = getArgs('onBlur') onBlur?.apply(this, args) if (!changedRef.current && !validateUnchanged) { @@ -1078,6 +1090,7 @@ export default function useFieldProps( } }, [ + getEventArgs, onFocus, onBlur, validateUnchanged, @@ -1311,7 +1324,11 @@ export default function useFieldProps( } const transformedValue = transformers.current.transformOut( - transformers.current.transformValue(fromInput, currentValue) + transformers.current.transformValue(fromInput, currentValue), + transformers.current.provideAdditionalArgs( + fromInput, + additionalArgs + ) ) // Must be set before validation @@ -1332,28 +1349,14 @@ export default function useFieldProps( updateValue(transformedValue) } - const getArgs = (): [Value] | [Value, AdditionalEventArgs] => { - const value = transformers.current.toEvent( - valueRef.current, - 'onChange' - ) - - const transformedAdditionalArgs = - transformers.current.transformAdditionalArgs( - additionalArgs, - value - ) - - return typeof transformedAdditionalArgs !== 'undefined' - ? [value, transformedAdditionalArgs] - : [value] - } - if (isAsync(onChange)) { addToPool( 'onChangeLocal', async () => { - const args = getArgs() + const args = getEventArgs({ + eventName: 'onChange', + additionalArgs, + }) await yieldAsyncProcess({ name: 'onChangeLocal', @@ -1389,7 +1392,11 @@ export default function useFieldProps( true ) } else { - setEventResult(onChange?.apply(this, getArgs())) + const args = getEventArgs({ + eventName: 'onChange', + additionalArgs, + }) + setEventResult(onChange?.apply(this, args)) } await runPool() @@ -1404,6 +1411,7 @@ export default function useFieldProps( hideError, updateValue, addToPool, + getEventArgs, yieldAsyncProcess, defineAsyncProcess, hasError, @@ -1620,7 +1628,13 @@ export default function useFieldProps( ) { // Update the data context when a pointer not exists, // but was given initially. - updateDataValueDataContext?.(identifier, value) + updateDataValueDataContext?.( + identifier, + transformers.current.transformOut( + value, + transformers.current.provideAdditionalArgs(value) + ) + ) validateDataDataContext?.() } } diff --git a/packages/dnb-eufemia/src/extensions/forms/types.ts b/packages/dnb-eufemia/src/extensions/forms/types.ts index 97a19cf85a2..9e058eea074 100644 --- a/packages/dnb-eufemia/src/extensions/forms/types.ts +++ b/packages/dnb-eufemia/src/extensions/forms/types.ts @@ -356,7 +356,10 @@ export interface UseFieldProps< * Transforms the value before it gets forwarded to the form data object or returned as the onChange value parameter. * Public API. Should not be used internally. */ - transformOut?: (internal: Value | unknown) => Value + transformOut?: ( + internal: Value | unknown, + additionalArgs?: unknown + ) => Value /** * Transforms the value given by `handleChange` after `fromInput` and before `updateValue` and `toEvent`. The second parameter returns the current value. @@ -364,11 +367,11 @@ export interface UseFieldProps< transformValue?: (value: Value, currentValue?: Value) => Value /** - * Transform additionalArgs or generate it based on value after `toEvent` and before callbacks such as `onChange`, `onFocus` and `onBlur`. + * Transform additionalArgs or generate it based on `value`. */ - transformAdditionalArgs?: ( - additionalArgs: AdditionalEventArgs, - internal: Value + provideAdditionalArgs?: ( + value: Value, + additionalArgs?: AdditionalEventArgs ) => AdditionalEventArgs /** From 2ae6a5f373b4e07f0b1b75855c6fb4b701837e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Fri, 20 Sep 2024 22:15:21 +0200 Subject: [PATCH 29/37] fix(Forms): enhance reset routine of ChildrenWithAge block (#3961) Instead of resetting the amount of children, this PR removes it, so the default value applies when switching back again. --- .../extensions/forms/Form/Handler/demos.mdx | 2 +- .../forms/blocks/ChildrenWithAge/Examples.tsx | 16 ++- .../extensions/forms/Value/Number/Number.tsx | 1 + .../Value/Number/__tests__/Number.test.tsx | 14 +++ .../ChildrenWithAge/ChildrenWithAge.tsx | 29 ++--- .../ChildrenWithAge/ChildrenWithAgeDocs.ts | 10 +- .../__tests__/ChildrenWithAge.test.tsx | 111 ++++++++++++++++-- .../stories/ChildrenWithAge.stories.tsx | 9 ++ 8 files changed, 161 insertions(+), 31 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/demos.mdx index 28a15bf58b4..dbd760ad65c 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/demos.mdx @@ -44,7 +44,7 @@ This example is only for demo purpose and will NOT redirect to a new location. I -### Visible data +### Reduce your data to visible fields You can use the `reduceToVisibleFields` function to get only the data of visible (mounted) fields. diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/Examples.tsx index a644a9d5144..c6224e02bf9 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/blocks/ChildrenWithAge/Examples.tsx @@ -14,7 +14,11 @@ import { useData } from '@dnb/eufemia/src/extensions/forms/Form' export const ChildrenWithAge = (props) => { return ( - + { + console.log(reduceToVisibleFields(data)) + }} + > { const MyForm = () => { const { summaryTitle } = Form.useLocale().Step return ( - + { + console.log(reduceToVisibleFields(data)) + }} + > @@ -50,7 +58,6 @@ export const ChildrenWithAgeWizard = (props) => { @@ -178,6 +185,9 @@ export const ChildrenWithAgePrefilledYes = () => { countChildren: 2, children: [{}, {}], }} + onSubmit={(data, { reduceToVisibleFields }) => { + console.log(reduceToVisibleFields(data)) + }} > { ).toBe('-12 345.68 Swedish kronor') }) + it('should forward HTML attributes', () => { + render( + + ) + + const element = document.querySelector('.dnb-number-format') + const attributes = Array.from(element.attributes).map( + (attr) => attr.name + ) + + expect(attributes).toEqual(['lang', 'class', 'data-testid', 'role']) + expect(element).toHaveAttribute('data-testid', 'testid') + }) + describe('inheritLabel', () => { it('renders label from field with same path', () => { render( diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAge.tsx b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAge.tsx index 87fe6168216..bfdce37bc0e 100644 --- a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAge.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/ChildrenWithAge.tsx @@ -22,9 +22,9 @@ export type Props = SectionProps & { export default function ChildrenWithAge({ mode, - showEmpty, enableAdditionalQuestions, toWizardStep, + showEmpty, ...props }: Props) { const spacingProps = pickSpacingProps(props) @@ -32,13 +32,13 @@ export default function ChildrenWithAge({ return ( {mode === 'summary' ? ( - ) : ( - @@ -47,14 +47,13 @@ export default function ChildrenWithAge({ ) } -function EditContent({ +function EditContainer({ spacingProps, enableAdditionalQuestions, }: Props & { spacingProps?: SpacingProps }) { const tr = Form.useTranslation() - const { update } = Form.useData() return ( @@ -68,11 +67,6 @@ function EditContent({ errorMessages={{ required: tr.ChildrenWithAge.hasChildren.required, }} - onChange={(value) => { - if (value === false) { - update('/countChildren', 0) - } - }} /> @@ -115,10 +109,10 @@ function EditContent({ /> () + const { getValue } = Form.useData() + const hasNoChildren = getValue('/hasChildren') === false + return ( @@ -179,9 +176,13 @@ function Summary({ defaultValue={0} suffix={tr.ChildrenWithAge.countChildren.suffix} maximum={20} + transformIn={(value) => (hasNoChildren ? 0 : value)} /> - + { expect(screen.queryByRole('alert')).not.toBeInTheDocument() }) - it('should reset age when hasChildren changes to false', async () => { - render() + it('should show summary with 0 children when hasChildren changes to false', async () => { + render( + + + + + ) const [yesButton, noButton] = Array.from( document.querySelectorAll('button') ) - expect( - document.querySelector('.dnb-input__input') - ).not.toBeInTheDocument() + expect(document.querySelectorAll('input')).toHaveLength(0) + const dlDDs = Array.from(document.querySelectorAll('dl dd')) + expect(dlDDs).toHaveLength(1) + expect(dlDDs.at(0)).toHaveTextContent('0 barn') await userEvent.click(yesButton) expect(document.querySelector('.dnb-input__input')).toHaveValue('1') + await waitFor(() => { + const dlDDs = Array.from(document.querySelectorAll('dl dd')) + expect(dlDDs).toHaveLength(2) + expect(dlDDs.at(0)).toHaveTextContent('1 barn') + }) + await userEvent.click(noButton) - expect(document.querySelector('.dnb-input__input')).toHaveValue('0') + await waitFor(() => { + expect(document.querySelectorAll('input')).toHaveLength(0) + }) + + // Here we check that the summary still shows 0 children + { + const dlDDs = Array.from(document.querySelectorAll('dl dd')) + expect(dlDDs).toHaveLength(1) + expect(dlDDs.at(0)).toHaveTextContent('0 barn') + } await userEvent.click(yesButton) - expect(document.querySelector('.dnb-input__input')).toHaveValue('0') + expect(document.querySelector('.dnb-input__input')).toHaveValue('1') + + { + const dlDDs = Array.from(document.querySelectorAll('dl dd')) + expect(dlDDs).toHaveLength(2) + expect(dlDDs.at(0)).toHaveTextContent('1 barn') + } }) it('should replace translations', async () => { @@ -253,4 +286,66 @@ describe('ChildrenWithAge', () => { const { propsOfValues } = generateRef.current() expect(propsOfValues).toMatchSnapshot() }) + + it('should run "reduceToVisibleFields" on submit in React.StrictMode', async () => { + let submitData = null + + const defaultData = { + countChildren: 1, + children: [ + { + age: 17, + }, + ], + } + + render( + + { + submitData = reduceToVisibleFields(data) + }} + > + + + + ) + + const [yesButton, noButton] = Array.from( + document.querySelectorAll('button') + ) + const form = document.querySelector('form') + + fireEvent.submit(form) + expect(submitData).toEqual({ + hasChildren: false, + }) + + await userEvent.click(yesButton) + await waitFor(() => { + expect(screen.getByText('Alder på barn nr. 1')).toBeInTheDocument() + }) + + fireEvent.submit(form) + expect(submitData).toEqual({ + hasChildren: true, + countChildren: 1, + children: [ + { + age: 17, + }, + ], + }) + + await userEvent.click(noButton) + await waitFor(() => { + expect(document.querySelectorAll('input')).toHaveLength(0) + }) + + fireEvent.submit(form) + expect(submitData).toEqual({ + hasChildren: false, + }) + }) }) diff --git a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/stories/ChildrenWithAge.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/stories/ChildrenWithAge.stories.tsx index acca560c668..0591a3347f9 100644 --- a/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/stories/ChildrenWithAge.stories.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/blocks/ChildrenWithAge/stories/ChildrenWithAge.stories.tsx @@ -30,6 +30,10 @@ export function Basic() { // onChange={(data) => { // console.log('onChange', data.children.length) // }} + onSubmit={(data, { reduceToVisibleFields }) => { + console.log('Raw', data) + console.log('Reduced', reduceToVisibleFields(data)) + }} translations={myTranslations} > @@ -40,6 +44,7 @@ export function Basic() { @@ -65,6 +70,10 @@ export function InsideWizard() { }, ], }} + onSubmit={(data, { reduceToVisibleFields }) => { + console.log('Raw', data) + console.log('Reduced', reduceToVisibleFields(data)) + }} > From d1c44c88a3401f4cd6d7884416287ca827137b9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Fri, 20 Sep 2024 22:15:43 +0200 Subject: [PATCH 30/37] fix(Forms): add support for `transformIn` and `transformOut` to Field.SelectCountry (#3975) Based on PR #3974 Here is a [demo](https://eufemia-git-fix-select-country-transform-eufemia.vercel.app/uilib/extensions/forms/feature-fields/SelectCountry/#transformin-and-transformout). --- .../forms/Value/SelectCountry/info.mdx | 36 +++++ .../feature-fields/SelectCountry/Examples.tsx | 51 ++++++- .../feature-fields/SelectCountry/demos.mdx | 4 + .../Field/SelectCountry/SelectCountry.tsx | 39 ++++-- .../__tests__/SelectCountry.test.tsx | 131 +++++++++++++++++- .../stories/SelectCountry.stories.tsx | 38 ++++- .../src/extensions/forms/Tools/Log.tsx | 2 +- .../extensions/forms/constants/countries.ts | 1 + 8 files changed, 282 insertions(+), 20 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/SelectCountry/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/SelectCountry/info.mdx index a164eab1497..20a975b4b56 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/SelectCountry/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Value/SelectCountry/info.mdx @@ -24,3 +24,39 @@ const MyComponent = () => { const { getCountryNameByIso } = Value.SelectCountry.useCountry('NO') } ``` + +### TransformIn and TransformOut + +You can use the `transformIn` and `transformOut` to transform the value before it gets displayed in the field and before it gets sent to the form. The second parameter is the country object. You may have a look at the demo below to see how it works. + +```tsx +const transformOut = (value, country) => { + if (value) { + return `${country.name} (${value})` + } +} +const transformIn = (value) => { + return String(value).match(/\((.*)\)/)?.[1] +} +``` + +### onFocus, onBlur, onChange + +These events have an additional parameter with the country object. + +```tsx +const onFocus = (value, country) => {} +``` + +## The country object + +```ts +{ + cdc: '47', + iso: 'NO', + name: 'Norge', + i18n: { en: 'Norway', nb: 'Norge' }, + regions: ['Scandinavia', 'Nordic'], + continent: 'Europe', +} +``` diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/Examples.tsx index 7081d83d5ae..3446b28748c 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/Examples.tsx @@ -1,5 +1,11 @@ +import { Card } from '@dnb/eufemia/src' import ComponentBox from '../../../../../../shared/tags/ComponentBox' -import { Field } from '@dnb/eufemia/src/extensions/forms' +import { + Field, + Form, + Tools, + Value, +} from '@dnb/eufemia/src/extensions/forms' export const Empty = () => { return ( @@ -114,3 +120,46 @@ export const ValidationRequired = () => { ) } + +export function TransformInAndOut() { + return ( + + {() => { + const transformOut = (value, country) => { + if (value) { + return country + } + } + const transformIn = (country) => { + return country?.iso + } + + const MyForm = () => { + return ( + + + + + + + + + + + ) + } + return + }} + + ) +} diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/demos.mdx index db36463058f..a0c546c3d6e 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/demos.mdx @@ -41,3 +41,7 @@ import * as Examples from './Examples' ### Validation - Required + +### TransformIn and TransformOut + + diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/SelectCountry.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/SelectCountry.tsx index fcb19f78cae..3dee0e582e5 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/SelectCountry.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/SelectCountry.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useContext, useMemo } from 'react' +import React, { useCallback, useContext, useMemo, useRef } from 'react' import classnames from 'classnames' import SharedContext from '../../../../shared/Context' import { Autocomplete, HelpButton } from '../../../../components' @@ -50,12 +50,27 @@ function SelectCountry(props: Props) { const translations = useTranslation().SelectCountry const lang = sharedContext.locale?.split('-')[0] as CountryLang - const provideAdditionalArgs = useCallback((value) => { - const country = countries.find(({ iso }) => value === iso) - if (country?.iso) { + const getCountryObjectByIso = useCallback( + (value: CountryType['iso']) => { + const country = countries.find(({ iso }) => value === iso) + if (country?.i18n) { + country['name'] = country.i18n[lang] + } return country - } - }, []) + }, + [lang] + ) + + const provideAdditionalArgs = useCallback( + (value: CountryType['iso']) => { + const country = getCountryObjectByIso(value) + + if (country?.iso) { + return country + } + }, + [getCountryObjectByIso] + ) const errorMessages = useErrorMessage(props.path, props.errorMessages, { required: translations.errorRequired, @@ -94,9 +109,9 @@ function SelectCountry(props: Props) { : undefined, } = useFieldProps(preparedProps) - const dataRef = React.useRef(null) - const langRef = React.useRef(lang) - const wasFilled = React.useRef(false) + const dataRef = useRef(null) + const langRef = useRef(lang) + const wasFilled = useRef(false) /** * We do not process the whole country list at the first render. @@ -132,12 +147,12 @@ function SelectCountry(props: Props) { const handleCountryChange = useCallback( ({ data }: { data: { selectedKey: string } }) => { const newValue = data?.selectedKey - const country = countries.find(({ iso }) => newValue === iso) + const country = getCountryObjectByIso(newValue) if (country?.iso) { - handleChange(country.iso) + handleChange(country.iso, country) } }, - [handleChange] + [getCountryObjectByIso, handleChange] ) const fillData = useCallback(() => { diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/__tests__/SelectCountry.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/__tests__/SelectCountry.test.tsx index 2f08dfba1fa..7caf8178058 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/__tests__/SelectCountry.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/__tests__/SelectCountry.test.tsx @@ -3,7 +3,8 @@ import { axeComponent } from '../../../../../core/jest/jestSetup' import { fireEvent, render, waitFor } from '@testing-library/react' import { Props } from '..' import { Provider } from '../../../../../shared' -import { Field, Form, FieldBlock } from '../../..' +import { Field, Form, FieldBlock, Value } from '../../..' +import userEvent from '@testing-library/user-event' describe('Field.SelectCountry', () => { it('should render with props', () => { @@ -30,7 +31,7 @@ describe('Field.SelectCountry', () => { ) const firstItemElement = () => document.querySelectorAll('li.dnb-drawer-list__option')[0] - expect(inputElement.value).toEqual('') + expect(inputElement).toHaveValue('') fireEvent.focus(inputElement) fireEvent.change(inputElement, { target: { value: 'Norge' } }) @@ -47,10 +48,11 @@ describe('Field.SelectCountry', () => { }, iso: 'NO', regions: ['Scandinavia', 'Nordic'], + name: 'Norge', }, ] expect(onChange).toHaveBeenLastCalledWith(...firstEventValues) - expect(inputElement.value).toEqual('Norge') + expect(inputElement).toHaveValue('Norge') fireEvent.blur(inputElement) fireEvent.focus(inputElement) @@ -70,8 +72,9 @@ describe('Field.SelectCountry', () => { }, iso: 'DK', regions: ['Scandinavia', 'Nordic'], + name: 'Danmark', }) - expect(inputElement.value).toEqual('Danmark') + expect(inputElement).toHaveValue('Danmark') }) it('should select matching country on type change to support autofill', async () => { @@ -95,7 +98,7 @@ describe('Field.SelectCountry', () => { nativeEvent: undefined, }) - expect(inputElement.value).toEqual('Sverige') + expect(inputElement).toHaveValue('Sverige') expect(liElements()).toHaveLength(2) expect(selectedItemElement().textContent).toBe('Sverige') expect(onChange).toHaveBeenCalledTimes(1) @@ -105,6 +108,7 @@ describe('Field.SelectCountry', () => { i18n: { en: 'Sweden', nb: 'Sverige' }, iso: 'SE', regions: ['Scandinavia', 'Nordic'], + name: 'Sverige', }) }) @@ -322,6 +326,123 @@ describe('Field.SelectCountry', () => { expect(input).toHaveClass('dnb-input__status--error') }) + it('should support "transformIn" and "transformOut"', async () => { + const transformOut = jest.fn((value, country) => { + if (value) { + return `${country.name} (${value})` + } + }) + const transformIn = jest.fn((value) => { + return String(value).match(/\((.*)\)/)?.[1] + }) + const valueTransformIn = jest.fn((value) => { + return String(value).match(/\((.*)\)/)?.[1] + }) + + const onSubmit = jest.fn() + + render( + + + + + + + + ) + + const NO = { + cdc: '47', + continent: 'Europe', + i18n: { en: 'Norway', nb: 'Norge' }, + iso: 'NO', + name: 'Norge', + regions: ['Scandinavia', 'Nordic'], + } + + const CH = { + cdc: '41', + continent: 'Europe', + i18n: { en: 'Switzerland', nb: 'Sveits' }, + iso: 'CH', + name: 'Sveits', + } + + expect(transformOut).toHaveBeenCalledTimes(1) + expect(transformIn).toHaveBeenCalledTimes(3) + expect(valueTransformIn).toHaveBeenCalledTimes(2) + + const firstItemElement = () => + document.querySelectorAll('li.dnb-drawer-list__option')[0] + + const form = document.querySelector('form') + const input = document.querySelector('input') + const value = document.querySelector('.dnb-forms-value-block__content') + + fireEvent.submit(form) + expect(onSubmit).toHaveBeenCalledTimes(1) + expect(onSubmit).toHaveBeenLastCalledWith( + { country: 'Norge (NO)' }, + expect.anything() + ) + + expect(transformOut).toHaveBeenCalledTimes(1) + expect(transformIn).toHaveBeenCalledTimes(4) + expect(valueTransformIn).toHaveBeenCalledTimes(3) + + expect(input).toHaveValue('Norge') + expect(value).toHaveTextContent('Norge') + + await userEvent.type(input, '{Backspace>10}Sveits') + await waitFor(() => { + expect(firstItemElement()).toBeInTheDocument() + }) + await userEvent.click(firstItemElement()) + + expect(input).toHaveValue('Sveits') + expect(value).toHaveTextContent('Sveits') + + expect(transformOut).toHaveBeenCalledTimes(2) + expect(transformIn).toHaveBeenCalledTimes(6) + expect(valueTransformIn).toHaveBeenCalledTimes(4) + + fireEvent.submit(form) + expect(onSubmit).toHaveBeenCalledTimes(2) + expect(onSubmit).toHaveBeenLastCalledWith( + { country: 'Sveits (CH)' }, + expect.anything() + ) + + expect(transformOut).toHaveBeenCalledTimes(2) + expect(transformIn).toHaveBeenCalledTimes(7) + expect(valueTransformIn).toHaveBeenCalledTimes(5) + + expect(transformOut).toHaveBeenNthCalledWith(1, 'NO', NO) + expect(transformOut).toHaveBeenNthCalledWith(2, 'CH', CH) + + expect(transformIn).toHaveBeenNthCalledWith(1, 'NO') + expect(transformIn).toHaveBeenNthCalledWith(2, 'NO') + expect(transformIn).toHaveBeenNthCalledWith(3, 'Norge (NO)') + expect(transformIn).toHaveBeenNthCalledWith(4, 'Norge (NO)') + expect(transformIn).toHaveBeenNthCalledWith(5, 'Norge (NO)') + expect(transformIn).toHaveBeenNthCalledWith(6, 'Sveits (CH)') + expect(transformIn).toHaveBeenNthCalledWith(7, 'Sveits (CH)') + + expect(valueTransformIn).toHaveBeenNthCalledWith(1, undefined) + expect(valueTransformIn).toHaveBeenNthCalledWith(2, 'Norge (NO)') + expect(valueTransformIn).toHaveBeenNthCalledWith(3, 'Norge (NO)') + expect(valueTransformIn).toHaveBeenNthCalledWith(4, 'Sveits (CH)') + expect(valueTransformIn).toHaveBeenNthCalledWith(5, 'Sveits (CH)') + }) + describe('ARIA', () => { it('should validate with ARIA rules', async () => { const result = render( diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/stories/SelectCountry.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/stories/SelectCountry.stories.tsx index ebc8e000ae5..b32f0ff6bf2 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/stories/SelectCountry.stories.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/stories/SelectCountry.stories.tsx @@ -1,5 +1,7 @@ import React from 'react' -import { Field } from '../../..' +import { Field, Form, Tools, Value } from '../../..' +import { Flex } from '../../../../../components' +import { CountryType } from '../../../constants/countries' export default { title: 'Eufemia/Extensions/Forms/SelectCountry', @@ -21,3 +23,37 @@ export function SelectCountry() { /> ) } + +const transformOut = (value, country: CountryType) => { + if (value) { + return `${country.name} (${value})` + } +} +const transformIn = (value) => { + return String(value).match(/\((.*)\)/)?.[1] +} + +export function Transform() { + return ( + + + + + + + + + + + + ) +} diff --git a/packages/dnb-eufemia/src/extensions/forms/Tools/Log.tsx b/packages/dnb-eufemia/src/extensions/forms/Tools/Log.tsx index 28861e3e34c..dfe5302a714 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Tools/Log.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Tools/Log.tsx @@ -14,7 +14,7 @@ function Log(props: SectionProps) { {...props} >
    -        {JSON.stringify(data)}
    +        {JSON.stringify(data, null, 2)}
             {' ' /* Ensure one line of spacing */}
           
    diff --git a/packages/dnb-eufemia/src/extensions/forms/constants/countries.ts b/packages/dnb-eufemia/src/extensions/forms/constants/countries.ts index cce1b804a8b..a68a2d042ec 100644 --- a/packages/dnb-eufemia/src/extensions/forms/constants/countries.ts +++ b/packages/dnb-eufemia/src/extensions/forms/constants/countries.ts @@ -24,6 +24,7 @@ export type CountryType = { | 'South America' | 'None' regions?: Array<'Scandinavia' | 'Nordic'> + name?: string } export type CountryLang = keyof CountryType['i18n'] From 52a5e69ca6e7f09e01dda0bf975897ab00471a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Sat, 21 Sep 2024 06:03:52 +0200 Subject: [PATCH 31/37] fix(Forms): make `exportValidators` work with the same validator as given in `validator` or `onBlurValidator` (#3977) --- .../hooks/__tests__/useFieldProps.test.tsx | 41 ++++++++++++++-- .../extensions/forms/hooks/useFieldProps.ts | 47 +++++++++++-------- 2 files changed, 65 insertions(+), 23 deletions(-) diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx index dc5a7433ec8..ba5c800dd9e 100644 --- a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx @@ -3228,7 +3228,7 @@ describe('useFieldProps', () => { expect(screen.queryByRole('alert')).toHaveTextContent('bar') }) expect(myValidator).toHaveBeenCalledTimes(5) - expect(fooValidator).toHaveBeenCalledTimes(5) + expect(fooValidator).toHaveBeenCalledTimes(4) expect(barValidator).toHaveBeenCalledTimes(4) }) @@ -3278,7 +3278,7 @@ describe('useFieldProps', () => { expect(screen.queryByRole('alert')).toHaveTextContent('bar') }) expect(myValidator).toHaveBeenCalledTimes(5) - expect(fooValidator).toHaveBeenCalledTimes(5) + expect(fooValidator).toHaveBeenCalledTimes(4) expect(barValidator).toHaveBeenCalledTimes(4) }) @@ -3376,11 +3376,44 @@ describe('useFieldProps', () => { }) expect(publicValidator).toHaveBeenCalledTimes(9) expect(fooValidator).toHaveBeenCalledTimes(1) - expect(barValidator).toHaveBeenCalledTimes(8) + expect(barValidator).toHaveBeenCalledTimes(7) expect(bazValidator).toHaveBeenCalledTimes(7) expect(internalValidators).toHaveBeenCalledTimes(0) }) + it('should export and call same validator without "Maximum call stack size exceeded"', async () => { + const onBlurValidator = jest.fn((value) => { + if (value === '1234') { + return Error('Error message') + } + }) + + render( + + ) + + const input = document.querySelector('input') + + await userEvent.type(input, '123') + fireEvent.blur(input) + + expect(onBlurValidator).toHaveBeenCalledTimes(2) + expect(document.querySelector('.dnb-form-status')).toBeNull() + + await userEvent.type(input, '4') + fireEvent.blur(input) + + expect(onBlurValidator).toHaveBeenCalledTimes(3) + await waitFor(() => { + expect( + document.querySelector('.dnb-form-status') + ).toHaveTextContent('Error message') + }) + }) + it('should only call returned validators (barValidator should not be called)', async () => { let internalValidators, fooValidator, barValidator, bazValidator @@ -3566,7 +3599,7 @@ describe('useFieldProps', () => { expect(screen.queryByRole('alert')).toHaveTextContent('baz') }) expect(publicValidator).toHaveBeenCalledTimes(9) - expect(barValidator).toHaveBeenCalledTimes(8) + expect(barValidator).toHaveBeenCalledTimes(7) expect(bazValidator).toHaveBeenCalledTimes(7) expect(internalValidators).toHaveBeenCalledTimes(0) }) diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts index 6f17dac5537..3ac99dce336 100644 --- a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts +++ b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts @@ -531,8 +531,7 @@ export default function useFieldProps( exportValidatorsRef.current && !result && (validator === onChangeValidatorRef.current || - validator === onBlurValidatorRef.current) && - !Array.isArray(result) + validator === onBlurValidatorRef.current) ) { return Object.values(exportValidatorsRef.current) } @@ -542,13 +541,20 @@ export default function useFieldProps( [] ) + const callStackRef = useRef>>([]) + const hasBeenCalledRef = useCallback((validator: Validator) => { + const result = callStackRef.current.includes(validator) + callStackRef.current.push(validator) + return result + }, []) + const callValidatorFnSync = useCallback( ( validator: Validator, value: Value = valueRef.current ): ReturnType> => { if (typeof validator !== 'function') { - return undefined + return // stop here } const result = extendWithExportedValidators( @@ -558,18 +564,20 @@ export default function useFieldProps( if (Array.isArray(result)) { for (const validator of result) { - const result = callValidatorFnSync(validator, value) - if (result instanceof Error) { - return result + if (!hasBeenCalledRef(validator)) { + const result = callValidatorFnSync(validator, value) + if (result instanceof Error) { + return result + } } } - return // stop here + callStackRef.current = [] + } else { + return result } - - return result }, - [additionalArgs, extendWithExportedValidators] + [additionalArgs, extendWithExportedValidators, hasBeenCalledRef] ) const callValidatorFnAsync = useCallback( @@ -578,7 +586,7 @@ export default function useFieldProps( value: Value = valueRef.current ): Promise>> => { if (typeof validator !== 'function') { - return undefined + return } const result = extendWithExportedValidators( @@ -588,19 +596,20 @@ export default function useFieldProps( if (Array.isArray(result)) { for (const validator of result) { - const result = await callValidatorFnAsync(validator, value) - - if (result instanceof Error) { - return result + if (!hasBeenCalledRef(validator)) { + const result = await callValidatorFnAsync(validator, value) + if (result instanceof Error) { + return result + } } } - return // stop here + callStackRef.current = [] + } else { + return result } - - return result }, - [additionalArgs, extendWithExportedValidators] + [additionalArgs, extendWithExportedValidators, hasBeenCalledRef] ) /** From 2276ba40749c6894582a16c46d243f1ac95dd5c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Sat, 21 Sep 2024 07:27:08 +0200 Subject: [PATCH 32/37] chore: revert unwanted example changes (#3978) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts this [WIP commit](https://github.com/dnbexperience/eufemia/pull/3962/commits/1562f98eef92e7a6a4b89ef9406b8fb91cf22eab), which I not wanted to me merged 😄 --- .../extensions/forms/Form/Handler/Examples.tsx | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/Examples.tsx index 5fa79c5cd5d..9395fb53f47 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/Examples.tsx @@ -301,9 +301,9 @@ export const VisibleData = () => { return ( { const myData = reduceToVisibleFields(data, { removePaths: ['/isVisible'], @@ -316,7 +316,6 @@ export const VisibleData = () => { label="Show radio buttons" variant="button" path="/isVisible" - defaultValue={true} /> { - - From 4ced6265a8d78874358eac8163ab4af69ce2c7fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Sun, 22 Sep 2024 13:58:54 +0200 Subject: [PATCH 33/37] chore(Forms): fix initial `transformOut` call (#3980) In PR #3975 (not released at this point of time), we added a new `transformOut` function to the `useFieldProps` hook. This function is with that also called when the field is initialized (and there is a path and Form.Handler). It can be used to transform the value before it is stored in the data context. However, this function, when changing the value in the context, it will re-render and with that, try to do the same again, but we had no check for this case, so it can/will result in an infinite loop. By double checking if the data context value did change since last time, we can avoid this infinite loop. The provided tests would fail with an infinite loop without the fix. --- .../extensions/forms/getting-started.mdx | 44 +++++- .../hooks/__tests__/useFieldProps.test.tsx | 141 +++++++++++++++++- .../extensions/forms/hooks/useFieldProps.ts | 26 +++- 3 files changed, 196 insertions(+), 15 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx index 8ec6b7ee927..5f5bcafd916 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx @@ -226,19 +226,59 @@ You may check out an [interactive example](/uilib/extensions/forms/Form/Handler/ #### Transforming data -Each [field](/uilib/extensions/forms/all-fields/) supports transformer functions. So you can transform a value before it is processed to the form data object and vis-a-versa: +Each [field](/uilib/extensions/forms/all-fields/) and [value](/uilib/extensions/forms/Value/) component supports transformer functions. These functions allow you to transform a value before it is processed into the form data object and vice versa: ```tsx ``` +This allows you to show a value in a different format than it is stored in the form data object. + +- `transformIn` (in to the field or value) transforms the internal value before it is displayed. +- `transformOut` (out of the field) transforms the internal value before it gets forwarded to the data context or returned as e.g. the `onChange` value parameter. + +##### Complex objects in the data context + +If you need to store complex objects in the data context instead of simple values like strings, numbers, or booleans, you can use transformer functions. + +Suppose you want to store a country object instead of just a country code like `NO` or `SV` when using [Field.SelectCountry](/uilib/extensions/forms/feature-fields/SelectCountry/). + +You can achieve this by using the `transformIn` and `transformOut` functions: + +```tsx +import { Field } from '@dnb/eufemia/extensions/forms' + +const transformOut = (value, country) => { + if (value) { + return country + } +} +const transformIn = (country) => { + return country?.iso +} + +const MyForm = () => { + return ( + + + + + + ) +} +``` + ### Async form handling It depends on your use case if this feature is needed. But when it is, its ofter a time consuming task to implement. Eufemia Forms has therefor a built-in feature that enables async form behavior. diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx index ba5c800dd9e..f7480a46ad2 100644 --- a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx @@ -2201,7 +2201,7 @@ describe('useFieldProps', () => { }) }) - describe('value manipulation', () => { + describe('value manipulation with transformers', () => { it('should call "transformIn" and "transformOut"', () => { const transformIn = jest.fn((v) => v - 1) const transformOut = jest.fn((v) => v + 1) @@ -2241,33 +2241,62 @@ describe('useFieldProps', () => { expect(transformOut).toHaveBeenLastCalledWith(4, undefined) }) - it('should call "transformOut" initially when value is given', () => { + it('should call "transformOut" initially when path is given', () => { const transformOut = jest.fn((v) => v + 1) - const defaultValue = 1 + + const { result } = renderHook(useFieldProps, { + initialProps: { + path: '/myPath', + value: 1, + transformOut, + }, + wrapper: Provider, + }) + + expect(transformOut).toHaveBeenCalledTimes(1) + expect(transformOut).toHaveBeenLastCalledWith(1, undefined) + expect(result.current.dataContext.data).toEqual({ + myPath: 2, + }) + expect(result.current.value).toEqual(1) + }) + + it('should call "transformOut" initially when "value" is given', () => { + const transformOut = jest.fn((v) => v + 1) + const value = 1 const { result } = renderHook( - () => useFieldProps({ path: '/foo', transformOut, defaultValue }), + () => useFieldProps({ path: '/foo', transformOut, value }), { wrapper: Provider } ) expect(result.current.dataContext.data).toEqual({ foo: 2, }) + expect(result.current.value).toEqual(1) expect(transformOut).toHaveBeenCalledTimes(1) }) - it('should call "transformOut" initially when defaultValue is given', () => { + it('should call "transformOut" initially when "defaultValue" is given', () => { const transformOut = jest.fn((v) => v + 1) + const transformIn = jest.fn((v) => v - 1) const defaultValue = 1 const { result } = renderHook( - () => useFieldProps({ path: '/foo', transformOut, defaultValue }), + () => + useFieldProps({ + path: '/foo', + transformOut, + transformIn, + defaultValue, + }), { wrapper: Provider } ) expect(result.current.dataContext.data).toEqual({ foo: 2, }) + expect(result.current.value).toEqual(1) expect(transformOut).toHaveBeenCalledTimes(1) }) @@ -2493,6 +2522,106 @@ describe('useFieldProps', () => { expect(transformValue).toHaveBeenCalledTimes(2) expect(transformValue).toHaveBeenLastCalledWith(4, 3) }) + + it('"provideAdditionalArgs" should provide additional arguments to "onFocus", "onBlur" and "onChange" and "transformOut"', () => { + const onFocus = jest.fn() + const onBlur = jest.fn() + const onChange = jest.fn() + const transformOut = jest.fn((value) => value + 1) + const provideAdditionalArgs = jest.fn((value) => { + return { + value, + foo: 'bar', + } + }) + + const { result } = renderHook(useFieldProps, { + initialProps: { + path: '/myPath', + value: 1, + onFocus, + onBlur, + onChange, + transformOut, + provideAdditionalArgs, + }, + wrapper: Provider, + }) + + const { handleFocus, handleBlur, handleChange } = result.current + + expect(transformOut).toHaveBeenCalledTimes(1) + expect(transformOut).toHaveBeenLastCalledWith(1, { + foo: 'bar', + value: 1, + }) + expect(result.current.value).toBe(1) + expect(result.current.dataContext.data).toEqual({ + myPath: 2, + }) + + act(() => { + handleFocus() + handleChange(2) + handleBlur() + }) + + expect(result.current.value).toBe(3) + expect(result.current.dataContext.data).toEqual({ + myPath: 3, + }) + expect(transformOut).toHaveBeenCalledTimes(2) + expect(transformOut).toHaveBeenLastCalledWith(2, { + value: 2, + foo: 'bar', + }) + expect(onFocus).toHaveBeenCalledTimes(1) + expect(onFocus).toHaveBeenLastCalledWith(1, { + value: 1, + foo: 'bar', + }) + expect(onBlur).toHaveBeenCalledTimes(1) + expect(onBlur).toHaveBeenLastCalledWith(3, { + value: 3, + foo: 'bar', + }) + expect(onChange).toHaveBeenCalledTimes(1) + expect(onChange).toHaveBeenLastCalledWith(3, { + value: 3, + foo: 'bar', + }) + + act(() => { + handleFocus() + handleChange(4) + handleBlur() + }) + + expect(result.current.value).toBe(5) + expect(result.current.dataContext.data).toEqual({ + myPath: 5, + }) + expect(transformOut).toHaveBeenCalledTimes(3) + expect(transformOut).toHaveBeenLastCalledWith(4, { + value: 4, + foo: 'bar', + }) + expect(onFocus).toHaveBeenCalledTimes(2) + expect(onFocus).toHaveBeenLastCalledWith(3, { + value: 3, + foo: 'bar', + }) + expect(onBlur).toHaveBeenCalledTimes(2) + expect(onBlur).toHaveBeenLastCalledWith(5, { + value: 5, + foo: 'bar', + }) + expect(onChange).toHaveBeenCalledTimes(2) + expect(onChange).toHaveBeenLastCalledWith(5, { + value: 5, + foo: 'bar', + }) + }) }) describe('updating internal value', () => { diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts index 3ac99dce336..0bf0642c8d1 100644 --- a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts +++ b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts @@ -20,6 +20,7 @@ import { EventStateObjectWithSuccess, ValidatorAdditionalArgs, Validator, + Identifier, } from '../types' import { Context as DataContext, ContextState } from '../DataContext' import { clearedData } from '../DataContext/Provider/Provider' @@ -1590,6 +1591,7 @@ export default function useFieldProps( // Use "useLayoutEffect" to avoid flickering when value/defaultValue gets set, and other fields dependent on it. // Form.Visibility is an example of a logic, where a field value/defaultValue can be used to set the set state of a path, // where again other fields depend on it. + const tmpValueRef = useRef>({}) useLayoutEffect(() => { if (hasPath) { let value = valueProp @@ -1635,15 +1637,25 @@ export default function useFieldProps( // Prevents an infinite loop by skipping the update if the value hasn't changed valueRef.current !== existingValue) ) { + if ( + identifier in tmpValueRef.current && + tmpValueRef.current[identifier] === value + ) { + return // stop here, avoid infinite loop + } + + const transformedValue = transformers.current.transformOut( + value, + transformers.current.provideAdditionalArgs(value) + ) + if (transformedValue !== value) { + tmpValueRef.current[identifier] = value + value = transformedValue + } + // Update the data context when a pointer not exists, // but was given initially. - updateDataValueDataContext?.( - identifier, - transformers.current.transformOut( - value, - transformers.current.provideAdditionalArgs(value) - ) - ) + updateDataValueDataContext?.(identifier, value) validateDataDataContext?.() } } From 56dfe4fbbd03c5a3baee15f104d6d43a95b6128e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Sun, 22 Sep 2024 14:17:42 +0200 Subject: [PATCH 34/37] feat(Forms): add `filterCountries` prop to Field.SelectCountry to filter out certain countries (#3979) --- .../feature-fields/SelectCountry/Examples.tsx | 12 ++++ .../feature-fields/SelectCountry/demos.mdx | 6 ++ .../Field/SelectCountry/SelectCountry.tsx | 64 +++++++++++++++---- .../Field/SelectCountry/SelectCountryDocs.ts | 5 ++ .../__tests__/SelectCountry.test.tsx | 25 ++++++++ 5 files changed, 98 insertions(+), 14 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/Examples.tsx index 3446b28748c..f80b8d8ac48 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/Examples.tsx @@ -121,6 +121,17 @@ export const ValidationRequired = () => { ) } +export function FilterCountries() { + return ( + + iso !== 'DK'} + /> + + ) +} + export function TransformInAndOut() { return ( @@ -152,6 +163,7 @@ export function TransformInAndOut() { showEmpty /> + Data Context
    diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/demos.mdx index a0c546c3d6e..4825d0646fc 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/SelectCountry/demos.mdx @@ -45,3 +45,9 @@ import * as Examples from './Examples' ### TransformIn and TransformOut + +### Filter countries + +This example demonstrates how to filter specific countries. Use the `countries` prop to define a set of countries and/or the `filterCountries` prop to apply custom filtering logic. + + diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/SelectCountry.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/SelectCountry.tsx index 3dee0e582e5..6d2988c7291 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/SelectCountry.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/SelectCountry.tsx @@ -26,16 +26,14 @@ export type CountryFilterSet = export type Props = FieldHelpProps & FieldPropsWithExtraValue & { + /** + * Lists only the countries you want to show. Can be `Scandinavia`, `Nordic`, `Europe` or `Prioritized`. + * Defaults to `Prioritized`. + */ countries?: CountryFilterSet - // Styling - width?: FieldBlockWidth - /** - * For internal use only. - * - * @param country - * @returns boolean + * Use this prop to filter out certain countries. The function receives the country object and should return a boolean. Returning `false` will omit the country. */ filterCountries?: (country: CountryType) => boolean @@ -43,6 +41,11 @@ export type Props = FieldHelpProps & * For internal testing purposes */ noAnimation?: boolean + + /** + * The width of the component. + */ + width?: FieldBlockWidth } function SelectCountry(props: Props) { @@ -104,15 +107,20 @@ function SelectCountry(props: Props) { handleChange, updateValue, forceUpdate, - filterCountries = ccFilter !== 'Prioritized' - ? makeCountryFilterSet(ccFilter) - : undefined, + filterCountries, } = useFieldProps(preparedProps) const dataRef = useRef(null) const langRef = useRef(lang) const wasFilled = useRef(false) + const filter = useCallback( + (country: CountryType) => { + return countryFilter(country, filterCountries, ccFilter) + }, + [ccFilter, filterCountries] + ) + /** * We do not process the whole country list at the first render. * Only when the Autocomplete opens (focus). @@ -130,7 +138,7 @@ function SelectCountry(props: Props) { lang, filter: !wasFilled.current ? (country) => country.iso === value - : filterCountries, + : filter, sort: ccFilter as Extract, }) @@ -142,7 +150,7 @@ function SelectCountry(props: Props) { }) } } - }, [lang, filterCountries, ccFilter, updateValue, value]) + }, [lang, filter, ccFilter, updateValue, value]) const handleCountryChange = useCallback( ({ data }: { data: { selectedKey: string } }) => { @@ -160,12 +168,12 @@ function SelectCountry(props: Props) { wasFilled.current = true dataRef.current = getCountryData({ lang: langRef.current, - filter: filterCountries, + filter, sort: ccFilter as Extract, }) forceUpdate() } - }, [ccFilter, filterCountries, forceUpdate]) + }, [ccFilter, filter, forceUpdate]) const onFocusHandler = useCallback( ({ updateData }) => { @@ -288,6 +296,34 @@ export function getCountryData({ .map((country) => makeObject(country, lang)) } +export function countryFilter( + country: CountryType, + filterCountries: (country: CountryType) => boolean, + ccFilter: CountryFilterSet +) { + let result = true + + if (ccFilter !== 'Prioritized') { + switch (ccFilter) { + case 'Scandinavia': + case 'Nordic': { + result = country.regions?.includes(ccFilter) + break + } + case 'Europe': { + result = country.continent.includes(ccFilter) + break + } + } + } + + if (result && filterCountries) { + result = filterCountries(country) + } + + return result +} + export function makeCountryFilterSet(ccFilter: CountryFilterSet) { return (country: CountryType) => { switch (ccFilter) { diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/SelectCountryDocs.ts b/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/SelectCountryDocs.ts index 8a15497bef6..b397257097e 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/SelectCountryDocs.ts +++ b/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/SelectCountryDocs.ts @@ -7,6 +7,11 @@ export const SelectCountryProperties: PropertiesTableProps = { type: 'string', status: 'optional', }, + filterCountries: { + doc: 'Use this prop to filter out certain countries. The function receives the country object and should return a boolean. Returning `false` will omit the country.', + type: 'function', + status: 'optional', + }, } export const SelectCountryGeneralEvents = getFieldEventsWithTypes( diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/__tests__/SelectCountry.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/__tests__/SelectCountry.test.tsx index 7caf8178058..7b1ea083a94 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/__tests__/SelectCountry.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/SelectCountry/__tests__/SelectCountry.test.tsx @@ -185,6 +185,31 @@ describe('Field.SelectCountry', () => { expect(liElements[2].textContent).toBe('Sverige') }) + it('should show only Scandinavian countries and filterCountries at the same time', () => { + render( + iso !== 'DK'} + /> + ) + + const inputElement: HTMLInputElement = document.querySelector( + '.dnb-forms-field-select-country input' + ) + + // open + fireEvent.focus(inputElement) + fireEvent.keyDown(inputElement, { + key: 'Enter', + keyCode: 13, + }) + + const liElements = document.querySelectorAll('li:not([aria-hidden])') + expect(liElements).toHaveLength(2) + expect(liElements[0].textContent).toBe('Norge') + expect(liElements[1].textContent).toBe('Sverige') + }) + it('should sort prioritized countries on top', () => { render() From 80415413cd4b2cdcd0aca86961f7eab6987741d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Sun, 22 Sep 2024 20:13:46 +0200 Subject: [PATCH 35/37] feat(Forms): add `filterCountries` to Field.PhoneNumber (#3981) --- .../feature-fields/PhoneNumber/Examples.tsx | 11 ++++ .../feature-fields/PhoneNumber/demos.mdx | 6 ++ .../feature-fields/PhoneNumber/properties.mdx | 15 +---- .../forms/Field/PhoneNumber/PhoneNumber.tsx | 27 +++++--- .../Field/PhoneNumber/PhoneNumberDocs.ts | 65 ++++++++++++++++++- .../__tests__/PhoneNumber.test.tsx | 30 +++++++++ 6 files changed, 130 insertions(+), 24 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/Examples.tsx index 3c8591bfbc9..d73ce8dfb45 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/Examples.tsx @@ -174,3 +174,14 @@ export const InCard = () => { ) } + +export function FilterCountries() { + return ( + + iso !== 'DK'} + /> + + ) +} diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/demos.mdx index 8a39aa90158..469b3cde966 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/demos.mdx @@ -50,6 +50,12 @@ import * as Examples from './Examples' +### Filter countries + +This example demonstrates how to filter specific countries. Use the `countries` prop to define a set of countries and/or the `filterCountries` prop to apply custom filtering logic. + + + diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/properties.mdx index 8136e438e40..a145b6bad4d 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/properties.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/properties.mdx @@ -5,24 +5,13 @@ showTabs: true import TranslationsTable from 'dnb-design-system-portal/src/shared/parts/TranslationsTable' import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable' import { fieldProperties } from '@dnb/eufemia/src/extensions/forms/Field/FieldDocs' +import { phoneNumberSpecificProperties } from '@dnb/eufemia/src/extensions/forms/Field/PhoneNumber/PhoneNumberDocs' ## Properties ### Field-specific props -| Property | Type | Description | -| --------------------------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `pattern` | `string` | _(optional)_ Validation based on regex pattern for the number field e.g. `pattern="^[49]+"`. | -| `help` | `object` | _(optional)_ Provide a help button. Object consisting of `title` and `content`. | -| `countries` | `string` | _(optional)_ List only a certain set of countries: `Scandinavia`, `Nordic`, `Europe` or `Prioritized`(all countries [sorted by priority](/uilib/extensions/forms/feature-fields/PhoneNumber/#filter-or-prioritize-country-listing)). Defaults to `Prioritized`. | -| `omitCountryCodeField` | `boolean` | _(optional)_ If `true` is given, then everything related to country code is removed. | -| `countryCodeFieldClassName` | `string` | _(optional)_ Class name for the country code component. | -| `numberFieldClassName` | `string` | _(optional)_ Class name for the number component. | -| `countryCodePlaceholder` | `string` | _(optional)_ Placeholder for the country code field. | -| `countryCodeLabel` | `string` | _(optional)_ Label to show above / before the country code field. | -| `numberMask` | Various | _(optional)_ See property `mask` of the [InputMasked](/uilib/components/input-masked/properties) component. | -| `width` | `string` or `false` | _(optional)_ `large` for predefined standard width, `stretch` for fill available width. | -| [Space](/uilib/layout/space/properties) | Various | _(optional)_ Spacing properties like `top` or `bottom` are supported. | + ### General props diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/PhoneNumber.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/PhoneNumber.tsx index 4482535a753..3451cfe2da0 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/PhoneNumber.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/PhoneNumber.tsx @@ -17,9 +17,9 @@ import { import { pickSpacingProps } from '../../../../components/flex/utils' import SharedContext from '../../../../shared/Context' import { + countryFilter, CountryFilterSet, getCountryData, - makeCountryFilterSet, } from '../SelectCountry' import useErrorMessage from '../../hooks/useErrorMessage' import useTranslation from '../../hooks/useTranslation' @@ -41,13 +41,15 @@ export type Props = FieldHelpProps & omitCountryCodeField?: boolean onCountryCodeChange?: (value: string | undefined) => void onNumberChange?: (value: string | undefined) => void + + /** + * Defines the countries to filter. Can be `Scandinavia`, `Nordic`, `Europe` or `Prioritized`. + * Defaults to `Prioritized`. + */ countries?: CountryFilterSet /** - * For internal use only. - * - * @param country - * @returns boolean + * Use this prop to filter out certain countries. The function receives the country object and should return a boolean. Returning `false` will omit the country. */ filterCountries?: (country: CountryType) => boolean @@ -161,9 +163,7 @@ function PhoneNumber(props: Props) { handleChange, onCountryCodeChange, onNumberChange, - filterCountries = ccFilter !== 'Prioritized' - ? makeCountryFilterSet(ccFilter) - : undefined, + filterCountries, } = useFieldProps(preparedProps) function fromExternal(external: string) { @@ -182,6 +182,13 @@ function PhoneNumber(props: Props) { return value } + const filter = useCallback( + (country: CountryType) => { + return countryFilter(country, filterCountries, ccFilter) + }, + [ccFilter, filterCountries] + ) + const updateCurrentDataSet = useCallback(() => { dataRef.current = getCountryData({ lang, @@ -191,11 +198,11 @@ function PhoneNumber(props: Props) { ? (country) => `${formatCountryCode(country.cdc)}` === countryCodeRef.current - : filterCountries, + : filter, sort: ccFilter as Extract, makeObject, }) - }, [lang, filterCountries, ccFilter]) + }, [lang, filter, ccFilter]) const getEventValues = useCallback( ({ diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/PhoneNumberDocs.ts b/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/PhoneNumberDocs.ts index 50f32925d07..2b2923ddd11 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/PhoneNumberDocs.ts +++ b/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/PhoneNumberDocs.ts @@ -1,9 +1,72 @@ import { PropertiesTableProps } from '../../../../shared/types' import { getFieldEventsWithTypes } from '../FieldDocs' +export const phoneNumberSpecificProperties: PropertiesTableProps = { + countries: { + doc: 'List only a certain set of countries: `Scandinavia`, `Nordic`, `Europe` or `Prioritized`(all countries [sorted by priority](/uilib/extensions/forms/feature-fields/SelectCountry/#filter-or-prioritize-country-listing)). Defaults to `Prioritized`.', + type: 'string', + status: 'optional', + }, + filterCountries: { + doc: 'Use this prop to filter out certain countries. The function receives the country object and should return a boolean. Returning `false` will omit the country.', + type: 'function', + status: 'optional', + }, + pattern: { + doc: 'Validation based on regex pattern for the number field e.g. `pattern="^[49]+"`.', + type: 'string', + status: 'optional', + }, + help: { + doc: 'Provide a help button. Object consisting of `title` and `content`.', + type: 'object', + status: 'optional', + }, + omitCountryCodeField: { + doc: 'If `true` is given, then everything related to country code is removed.', + type: 'boolean', + status: 'optional', + }, + countryCodeFieldClassName: { + doc: 'Class name for the country code component.', + type: 'string', + status: 'optional', + }, + numberFieldClassName: { + doc: 'Class name for the number component.', + type: 'string', + status: 'optional', + }, + countryCodePlaceholder: { + doc: 'Placeholder for the country code field.', + type: 'string', + status: 'optional', + }, + countryCodeLabel: { + doc: 'Label to show above / before the country code field.', + type: 'string', + status: 'optional', + }, + numberMask: { + doc: 'See property `mask` of the [InputMasked](/uilib/components/input-masked/properties) component.', + type: 'Various', + status: 'optional', + }, + width: { + doc: '`large` for predefined standard width, `stretch` for fill available width.', + type: 'string | false', + status: 'optional', + }, + '[Space](/uilib/layout/space/properties)': { + doc: 'Spacing properties like `top` or `bottom` are supported.', + type: ['string', 'object'], + status: 'optional', + }, +} + export const phoneNumberSpecificEvents: PropertiesTableProps = { onCountryCodeChange: { - doc: ' Callback on country code change.', + doc: 'Callback on country code change.', type: '(value?: string) => void', status: 'optional', }, diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/__tests__/PhoneNumber.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/__tests__/PhoneNumber.test.tsx index 7155eb4f0a1..6f0e32184b3 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/__tests__/PhoneNumber.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/__tests__/PhoneNumber.test.tsx @@ -775,6 +775,36 @@ describe('Field.PhoneNumber', () => { ).toBe('+47 Norge') }) + it('should show only Scandinavian countries and filterCountries at the same time', () => { + render( + iso !== 'DK'} + /> + ) + + const codeElement: HTMLInputElement = document.querySelector( + '.dnb-forms-field-phone-number__country-code input' + ) + + // open + fireEvent.focus(codeElement) + fireEvent.keyDown(codeElement, { + key: 'Enter', + keyCode: 13, + }) + + const liElements = document.querySelectorAll('li:not([aria-hidden])') + expect(liElements).toHaveLength(2) + expect(liElements[0].textContent).toBe('+47 Norge') + expect(liElements[1].textContent).toBe('+46 Sverige') + + expect( + document.querySelector('li.dnb-drawer-list__option--selected') + .textContent + ).toBe('+47 Norge') + }) + it('should sort prioritized countries on top', () => { render() From 6f344c2a54e784fe08a5557698e449accdbb246f Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 23 Sep 2024 10:10:33 +0200 Subject: [PATCH 36/37] feat(OrganizationNumber): adds organization number validation (#3976) --- .../OrganizationNumber/Examples.tsx | 32 +++ .../OrganizationNumber/demos.mdx | 6 + .../OrganizationNumber/info.mdx | 1 + .../OrganizationNumber/OrganizationNumber.tsx | 53 +++- .../__tests__/OrganizationNumber.test.tsx | 244 +++++++++++++++++- 5 files changed, 329 insertions(+), 7 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/OrganizationNumber/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/OrganizationNumber/Examples.tsx index 0135be6ff1d..e67d1220664 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/OrganizationNumber/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/OrganizationNumber/Examples.tsx @@ -111,3 +111,35 @@ export const ValidationRequired = () => { ) } + +export const ValidationExtendValidator = () => { + return ( + + {() => { + const firstNumIs1 = (value: string) => + value.substring(0, 1) === '1' + ? { status: 'valid' } + : { status: 'invalid' } + + const myValidator = (value, { validators }) => { + const { organizationNumberValidator } = validators + const result = firstNumIs1(value) + if (result.status === 'invalid') { + return new Error('My error') + } + + return [organizationNumberValidator] + } + + return ( + + ) + }} + + ) +} diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/OrganizationNumber/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/OrganizationNumber/demos.mdx index 70eeebf4e1a..cab97347b07 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/OrganizationNumber/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/OrganizationNumber/demos.mdx @@ -41,3 +41,9 @@ import * as Examples from './Examples' ### Validation - Required + +### Extend validation with custom validation function + +You can [extend the existing validation](/uilib/extensions/forms/create-component/useFieldProps/info/#validators)(`organizationNumberValidator`) with your own validation function. + + diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/OrganizationNumber/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/OrganizationNumber/info.mdx index 56f95227c37..e6c31c1561f 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/OrganizationNumber/info.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/OrganizationNumber/info.mdx @@ -7,6 +7,7 @@ showTabs: true `Field.OrganizationNumber` is a wrapper component for the [input of strings](/uilib/extensions/forms/base-fields/String), with user experience tailored for organization number values. This input expects a 9-digit number as its value. This is because Norwegian organization numbers are 9-digits long, based on info from [Brønnøysundregisteret](https://www.brreg.no/en/about-us-2/our-registers/about-the-central-coordinating-register-for-legal-entities-ccr/about-the-organisation-number/?nocache=1701776533136) +It validates input for norwegian organization numbers, as described by [Brønnøysundregistrene](https://www.brreg.no/om-oss/registrene-vare/om-enhetsregisteret/organisasjonsnummeret/). ```jsx import { Field } from '@dnb/eufemia/extensions/forms' diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/OrganizationNumber/OrganizationNumber.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/OrganizationNumber/OrganizationNumber.tsx index 2cb2d4334bd..45cc060a2c6 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/OrganizationNumber/OrganizationNumber.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/OrganizationNumber/OrganizationNumber.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react' +import React, { useCallback, useMemo } from 'react' import StringField, { Props as StringFieldProps } from '../String' import useErrorMessage from '../../hooks/useErrorMessage' import useTranslation from '../../hooks/useTranslation' @@ -10,12 +10,15 @@ export type Props = StringFieldProps & { function OrganizationNumber(props: Props) { const translations = useTranslation().OrganizationNumber + const { errorPattern, errorRequired, label } = translations const { validate = true, omitMask } = props + const validationPattern = '^[0-9]{9}$' + const errorMessages = useErrorMessage(props.path, props.errorMessages, { - required: translations.errorRequired, - pattern: translations.errorPattern, + required: errorRequired, + pattern: errorPattern, }) const mask = useMemo( @@ -26,19 +29,59 @@ function OrganizationNumber(props: Props) { [omitMask] ) + const organizationNumberValidator = useCallback( + (value: string) => { + if ( + new RegExp(validationPattern).test(value) && + !isValidOrgNumber(value) + ) { + return Error(errorPattern) + } + }, + [errorPattern] + ) + const StringFieldProps: Props = { ...props, className: 'dnb-forms-field-organization-number', - pattern: props.pattern ?? (validate ? '^[0-9]{9}$' : undefined), - label: props.label ?? translations.label, + pattern: props.pattern ?? (validate ? validationPattern : undefined), + label: props.label ?? label, errorMessages, mask, width: props.width ?? 'medium', inputMode: 'numeric', + onBlurValidator: validate + ? props.onBlurValidator || organizationNumberValidator + : undefined, + exportValidators: { organizationNumberValidator }, } return } +/** + * Source: + * www.brreg.no/om-oss/registrene-vare/om-enhetsregisteret/organisasjonsnummeret/ + */ +function isValidOrgNumber(digits: string) { + let checkDigit = 2 + let sum = 0 + + for (let i = digits.length - 2; i >= 0; --i) { + sum += parseInt(digits.charAt(i)) * checkDigit + + checkDigit += 1 + + if (checkDigit > 7) { + checkDigit = 2 + } + } + + const result = 11 - (sum % 11) + const finalCheckDigit = result === 11 ? 0 : result + + return parseInt(digits.charAt(digits.length - 1), 10) === finalCheckDigit +} + OrganizationNumber._supportsSpacingProps = true export default OrganizationNumber diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/OrganizationNumber/__tests__/OrganizationNumber.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/OrganizationNumber/__tests__/OrganizationNumber.test.tsx index 261f89dde55..575c34cb0ce 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/OrganizationNumber/__tests__/OrganizationNumber.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/OrganizationNumber/__tests__/OrganizationNumber.test.tsx @@ -1,8 +1,11 @@ import React from 'react' import { axeComponent } from '../../../../../core/jest/jestSetup' -import { render } from '@testing-library/react' +import { render, waitFor, screen, fireEvent } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import { Field } from '../../..' +import { Field, Form, Validator } from '../../..' +import nbNO from '../../../constants/locales/nb-NO' + +const nb = nbNO['nb-NO'] describe('Field.OrganizationNumber', () => { it('should have Norwegian mask', async () => { @@ -57,6 +60,243 @@ describe('Field.OrganizationNumber', () => { expect(input).toHaveAttribute('inputmode', 'numeric') }) + it('should not validate organization number when validate false', async () => { + const invalidOrgNo = '987654321' + + render( + + + + ) + + fireEvent.blur(document.querySelector('input')) + + expect(screen.queryByRole('alert')).toBeNull() + }) + + it('should not validate custom validator when validate false', async () => { + const invalidOrgNo = '987654321' + + const firstNumIs1 = (value: string) => + value.substring(0, 1) === '1' + ? { status: 'valid' } + : { status: 'invalid' } + + const customValidator: Validator = (value) => { + const result = firstNumIs1(value) + if (result.status === 'invalid') { + return new Error('My error') + } + } + + render( + + + + ) + + fireEvent.blur(document.querySelector('input')) + + expect(screen.queryByRole('alert')).toBeNull() + }) + + it('should not validate extended validator when validate false', async () => { + const invalidOrgNo = '987654321' + + const firstNumIs1 = (value: string) => + value.substring(0, 1) === '1' + ? { status: 'valid' } + : { status: 'invalid' } + + const customValidator: Validator = (value, { validators }) => { + const { organizationNumberValidator } = validators + const result = firstNumIs1(value) + if (result.status === 'invalid') { + return new Error('My error') + } + + return [organizationNumberValidator] + } + + render( + + + + ) + + fireEvent.blur(document.querySelector('input')) + + expect(screen.queryByRole('alert')).toBeNull() + }) + + describe('should validate Norwegian organization number', () => { + const validOrgNum = [ + '724841198', + '602105938', + '656231440', + '967746096', + '721357694', + '282334933', + '519909235', + '530028801', + '991541209', + '756299263', + '100214458', + '208141554', + '507364276', + '148623902', + ] + + const invalidOrgNum = ['123', '123456789', '148623907', '987654321'] + + it.each(validOrgNum)('Valid organization number: %s', (orgNo) => { + render( + + + + ) + + fireEvent.blur(document.querySelector('input')) + + expect(screen.queryByRole('alert')).toBeNull() + }) + + it.each(invalidOrgNum)( + 'Invalid organization number: %s', + async (orgNo) => { + render( + + ) + + fireEvent.blur(document.querySelector('input')) + + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent( + nb.OrganizationNumber.errorPattern + ) + }) + } + ) + }) + + describe('should extend validation using custom validator', () => { + const validOrgNumStartingWith1 = ['100214458', '148623902'] + + const validOrgNumNotStartingWith1 = [ + '208141554', + '507364276', + '724841198', + '602105938', + '656231440', + '967746096', + '721357694', + '282334933', + '519909235', + '530028801', + '991541209', + '756299263', + ] + + const invalidOrgNum = ['123', '123456789', '148623907', '987654321'] + + const firstNumIs1 = (value: string) => + value.substring(0, 1) === '1' + ? { status: 'valid' } + : { status: 'invalid' } + + const customValidator: Validator = (value, { validators }) => { + const { organizationNumberValidator } = validators + const result = firstNumIs1(value) + if (result.status === 'invalid') { + return new Error('My error') + } + + return [organizationNumberValidator] + } + + it.each(validOrgNumStartingWith1)( + 'Valid organization number: %s', + (orgNo) => { + render( + + + + ) + + fireEvent.blur(document.querySelector('input')) + + expect(screen.queryByRole('alert')).toBeNull() + } + ) + + it.each(validOrgNumNotStartingWith1)( + 'Invalid organization number: %s', + async (orgNo) => { + render( + + ) + + fireEvent.blur(document.querySelector('input')) + + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent('My error') + }) + } + ) + + it.each(invalidOrgNum)( + 'Invalid organization number: %s', + async (orgNo) => { + render( + + ) + + fireEvent.blur(document.querySelector('input')) + + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent( + nb.OrganizationNumber.errorPattern + ) + }) + } + ) + }) + describe('ARIA', () => { it('should validate with ARIA rules', async () => { const result = render( From 517f11afac0953919779b43632b16cc01235d7c0 Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 23 Sep 2024 11:17:08 +0200 Subject: [PATCH 37/37] chore: changelog for release v10.50.0 (#3973) --- .../src/docs/uilib/extensions/forms/changelog.mdx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/changelog.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/changelog.mdx index 9c8078c14dd..fb11285ba31 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/changelog.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/changelog.mdx @@ -13,10 +13,22 @@ breadcrumb: Change log for the Eufemia Forms extension. +## v10.50 + +- Added `reduceToVisibleFields` to the [Form.useData](/uilib/extensions/forms/Form/useData/) hook and [Form.Handler](/uilib/extensions/forms/Form/Handler/) `onSubmit`. +- Added `inheritVisibility` to each `Value.*` component. +- Added `variant` to [Value.ArraySelection](/uilib/extensions/forms/base-fields/ArraySelection/), to allow for list layout. +- Added validation of norwegian organization number to [Field.OrganizationNumber](/uilib/extensions/forms/feature-fields/OrganizationNumber/). +- Added `filterCountries` to [Field.PhoneNumber](/uilib/extensions/forms/feature-fields/PhoneNumber/), to be able to filter out countries. +- Added `filterCountries` to [Field.SelectCountry](/uilib/extensions/forms/feature-fields/SelectCountry/), to be able to filter out countries. +- Added `limit` in [Iterate](/uilib/extensions/forms/Iterate/Array/). +- Added `remove` method to the [Form.useData](/uilib/extensions/forms/Form/useData/) hook. +- Deprecated `itemNr` in [Iterate](/uilib/extensions/forms/Iterate/) and replaced with `itemNo`. + ## v10.48 - Make [Iterate.Toolbar](/uilib/extensions/forms/Iterate/Toolbar/) customizable. -- Add new property `toolbarVariant` to [Iterate.ViewContainer](/uilib/extensions/forms/Iterate/ViewContainer/) and [Iterate.EditContainer](/uilib/extensions/forms/Iterate/EditContainer/) for hiding toolbar buttons when there is only one item in the array. +- Added new property `toolbarVariant` to [Iterate.ViewContainer](/uilib/extensions/forms/Iterate/ViewContainer/) and [Iterate.EditContainer](/uilib/extensions/forms/Iterate/EditContainer/) for hiding toolbar buttons when there is only one item in the array. ## v10.46