From 53c7769c8af6489e0353ad35a50bcbf84c60e54b Mon Sep 17 00:00:00 2001 From: -l Date: Thu, 10 Oct 2024 14:48:27 +0200 Subject: [PATCH 01/12] feat(Forms): add adultValidator to Field.NationalIdentityNumber --- .../NationalIdentityNumber/Examples.tsx | 23 + .../NationalIdentityNumber/demos.mdx | 12 +- .../NationalIdentityNumber/properties.mdx | 5 +- .../NationalIdentityNumber.tsx | 52 +- ...ionalIdentityNumberAdultValidator.test.tsx | 408 ++++++++++ .../NationalIdentityNumber.stories.tsx | 755 +++++++++++++++++- .../forms/constants/locales/en-GB.ts | 1 + .../forms/constants/locales/nb-NO.ts | 1 + 8 files changed, 1244 insertions(+), 13 deletions(-) create mode 100644 packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/Examples.tsx index bcc3301f1e8..090a11d59ee 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/Examples.tsx @@ -188,3 +188,26 @@ export const ValidationExtendValidator = () => { ) } + +export const ValidationExtendValidatorAdult = () => { + return ( + + {() => { + const myAdultValidator = (value, { validators }) => { + const { adultValidator, dnrAndFnrValidator } = validators + + return [dnrAndFnrValidator, adultValidator] + } + + return ( + + ) + }} + + ) +} diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/demos.mdx index 2d20256438d..34fdcd66139 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/demos.mdx @@ -54,18 +54,24 @@ Below is an example of the error message displayed when there's an invalid Norwe It validates [D numbers](https://www.skatteetaten.no/en/person/national-registry/identitetsnummer/d-nummer/) using the [fnrvalidator](https://github.com/navikt/fnrvalidator). -Below is an example of the error message displayed when there's an invalid D number: +Below is an example of the error message displayed when there's an invalid D number(a D number has its first number in the identification number increased by 4): ### Validation function -You can provide your own validation function. +You can provide your own validation function, either to `validator` or `onBlurValidator`. ### Extend validation with custom validation function -You can [extend the existing validations](/uilib/extensions/forms/create-component/useFieldProps/info/#validators)(`dnrValidator`, `fnrValidator`, and `dnrAndFnrValidator`) with your own validation function. +You can [extend the existing validations](/uilib/extensions/forms/create-component/useFieldProps/info/#validators)(`dnrValidator`, `fnrValidator`, `dnrAndFnrValidator`, and `adultValidator`) with your own validation function. + +### Extend validation with adult validator + +You can [extend the existing validations](/uilib/extensions/forms/create-component/useFieldProps/info/#validators)(`dnrValidator`, `fnrValidator`, and `dnrAndFnrValidator`) with the `adultValidator`. + + diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx index 694c8830f82..7b8902a1fee 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx @@ -21,11 +21,12 @@ import { NationalIdentityNumberProperties } from '@dnb/eufemia/src/extensions/fo `Field.NationalIdentityNumber` expose the following validators through its `validator` and `onBlurValidator` property: -- `dnrValidator`: validates a d number. +- `dnrValidator`: validates a D number. - `fnrValidator`: validates a national identity number (fødselsnummer). - `dnrAndFnrValidator`: - - validates the identification number as a d number when first digit is 4 or greater (because a d number has its first number increased by 4). + - validates the identification number as a D number when first digit is 4 or greater (because a D number has its first number increased by 4). - validates the identification number as a national identity number (fødselsnummer) when first digit is 3 or less. +- `adultValidator`: validates if the identification number has a date of birth that is 18 years or older. See the following [example](/uilib/extensions/forms/feature-fields/NationalIdentityNumber/#extend-validation-with-custom-validation-function) on how to extend validation using the exposed validators. diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx index 5e3420b525f..b19d97de5bf 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx @@ -14,17 +14,19 @@ export type Props = Omit & { function NationalIdentityNumber(props: Props) { const translations = useTranslation().NationalIdentityNumber - const { label, errorRequired, errorFnr, errorDnr } = translations + const { label, errorRequired, errorFnr, errorDnr, errorAdult } = + translations const errorMessages = useErrorMessage(props.path, props.errorMessages, { required: errorRequired, pattern: errorFnr, errorFnr, errorDnr, + errorAdult, }) const fnrValidator = useCallback( (value: string) => { - // have to check for undefined as @navikt/fnrvalidator does not support undefined + // We don't want to validate if the value is undefined if (value !== undefined && fnr(value).status === 'invalid') { return Error(errorFnr) } @@ -34,7 +36,7 @@ function NationalIdentityNumber(props: Props) { const dnrValidator = useCallback( (value: string) => { - // have to check for undefined as @navikt/fnrvalidator does not support undefined + // We don't want to validate if the value is undefined if (value !== undefined && dnr(value).status === 'invalid') { return Error(errorDnr) } @@ -54,6 +56,16 @@ function NationalIdentityNumber(props: Props) { [dnrValidator, fnrValidator] ) + const adultValidator = useCallback( + (value: string) => { + // We don't want to validate if the value is undefined + if (value !== undefined && !is18YearsOrOlder(value)) { + return Error(errorAdult) + } + }, + [errorAdult] + ) + const { validate = true, omitMask, @@ -100,11 +112,45 @@ function NationalIdentityNumber(props: Props) { dnrValidator, fnrValidator, dnrAndFnrValidator, + adultValidator, }, } return } +function is18YearsOrOlder(value: string) { + if (!new RegExp('^[0-9]{11}$').test(value)) return false + + function getAge(birthDate: Date): number { + const today = new Date() + const age = today.getFullYear() - birthDate.getFullYear() + const month = today.getMonth() - birthDate.getMonth() + const day = today.getDate() - birthDate.getDate() + + if (month < 0 || (month === 0 && day < 0)) { + return age - 1 + } + + return age + } + + const yearPart = value.substring(4, 6) + const individNumber = Number.parseInt(value.substring(6, 9)) + + const isBornIn20XX = individNumber >= 500 && individNumber <= 999 + const year = isBornIn20XX ? `20${yearPart}` : `19${yearPart}` + const month = Number.parseInt(value.substring(2, 4)) + + const isDnr = dnr(value).status === 'valid' + + const day = isDnr + ? Number.parseInt(value.substring(0, 2)) - 40 + : Number.parseInt(value.substring(0, 2)) + const date = new Date(Number.parseInt(year), month - 1, day) + + return getAge(date) >= 18 +} + NationalIdentityNumber._supportsSpacingProps = true export default NationalIdentityNumber diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx new file mode 100644 index 00000000000..7025d81ce52 --- /dev/null +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx @@ -0,0 +1,408 @@ +import React from 'react' +import { render, waitFor, screen } from '@testing-library/react' +import { Field, Validator } from '../../..' + +import nbNO from '../../../constants/locales/nb-NO' + +const nb = nbNO['nb-NO'] + +describe('Field.NationalIdentityNumber with adultValidator', () => { + const extendingDnrAndFnrValidatorWithAdultValidator: Validator< + string + > = (value, { validators }) => { + const { adultValidator, dnrAndFnrValidator } = validators + + return [dnrAndFnrValidator, adultValidator] + } + + const myAdultValidator: Validator = (value, { validators }) => { + const { adultValidator } = validators + + return [adultValidator] + } + + it('should display error if required and validateInitially', async () => { + render( + + ) + + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent( + nb.NationalIdentityNumber.errorRequired + ) + }) + }) + + it('should display error when validateInitially and value', async () => { + render( + + ) + + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent( + nb.NationalIdentityNumber.errorAdult + ) + }) + }) + + it('should not display error when validateInitially and no value', async () => { + render( + + ) + + await expect(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + }).neverToResolve() + }) + + describe('should validate if identity numbers is adult(18 years and older)', () => { + jest.useFakeTimers().setSystemTime(new Date('2024-10-09').getTime()) + const fnr0YearsOld = [ + '10072476609', + '29082499936', + '03022450718', + '11032455001', + '30082489912', + ] + + const fnr17YearsOld = [ + '31050752669', + '10040752779', + '28050772596', + '25060798446', + '07100782566', + '08100787300', + ] + + const fnrUnder18YearsOld = [...fnr0YearsOld, ...fnr17YearsOld] + + const fnr18Years = [ + '09100654021', + '09100696336', + '24040664900', + '26020682328', + '07070663990', + '11030699302', + '31010699021', + '49100651997', + ] + const fnr99Years = [ + '14102535759', + '20042528022', + '14082523414', + '01022537632', + '01022504416', + ] + const fnr18YearsOldTo99 = [ + '25047441741', + '06118836551', + '19042648291', + '18053526132', + '29075642618', + ] + const fnr99To120YearsOld = [ + '22041330302', + '02061234694', + '23020704845', + '28021741177', + '10121933999', + ] + const fnr18YearsOldAndOlder = [ + ...fnr18Years, + ...fnr99Years, + ...fnr18YearsOldTo99, + ...fnr99To120YearsOld, + ] + + const dnrUnder18YearsOld = [ + '42011660597', + '44011957371', + '45010886213', + '60050972871', + '65052062378', + '70121275293', + '71072354979', + '43072496079', + '44052351836', + '56052459244', + '59082354829', + '63032486179', + '48100754692', + ] + const dnr18YearsOldAndOlder = [ + '49100651997', + '49100697466', + '41070663889', + '42020653633', + '41012413597', + '41062421922', + '41080422588', + '44081020024', + '71081924796', + '60067139081', + '60075812380', + ] + + const validIds = [...fnr18YearsOldAndOlder, ...dnr18YearsOldAndOlder] + + const invalidIds = [...fnrUnder18YearsOld, ...dnrUnder18YearsOld] + + const invalidDnums = ['69020112345', '690'] + const invalidFnrs = ['29020112345', '290'] + + describe('when provided as the only validator validation function', () => { + it.each(validIds)( + 'Identity number is 18 years or older : %s', + async (validId) => { + render( + + ) + + await expect(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + }).neverToResolve() + } + ) + + it.each(invalidIds)( + 'Invalid identity number is not 18 years or older: %s', + async (invalidId) => { + render( + + ) + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent( + nb.NationalIdentityNumber.errorAdult + ) + }) + } + ) + }) + + describe('when provided as the only onBlurValidation validation function', () => { + it.each(validIds)( + 'Identity number is 18 years or older : %s', + async (validId) => { + render( + + ) + + await expect(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + }).neverToResolve() + } + ) + + it.each(invalidIds)( + 'Invalid identity number is not 18 years or older: %s', + async (invalidId) => { + render( + + ) + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent( + nb.NationalIdentityNumber.errorAdult + ) + }) + } + ) + }) + + describe('when extending the dnrAndFnrValidator as validator', () => { + it.each(validIds)( + 'Identity number is 18 years or older : %s', + async (validId) => { + render( + + ) + + await expect(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + }).neverToResolve() + } + ) + + it.each(invalidIds)( + 'Invalid identity number is not 18 years or older: %s', + async (invalidId) => { + render( + + ) + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent( + nb.NationalIdentityNumber.errorAdult + ) + }) + } + ) + + it.each(invalidDnums)( + 'Invalid identity number is not 18 years or older: %s', + async (invalidDnum) => { + render( + + ) + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent( + nb.NationalIdentityNumber.errorDnr + ) + }) + } + ) + + it.each(invalidFnrs)( + 'Invalid identity number is not 18 years or older: %s', + async (invalidFnr) => { + render( + + ) + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent( + nb.NationalIdentityNumber.errorFnr + ) + }) + } + ) + }) + + describe('when extending the dnrAndFnrValidator as onBlurValidator', () => { + it.each(validIds)( + 'Identity number is 18 years or older : %s', + async (validId) => { + render( + + ) + + await expect(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + }).neverToResolve() + } + ) + + it.each(invalidIds)( + 'Invalid identity number is not 18 years or older: %s', + async (invalidId) => { + render( + + ) + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent( + nb.NationalIdentityNumber.errorAdult + ) + }) + } + ) + + it.each(invalidDnums)( + 'Invalid identity number is not 18 years or older: %s', + async (invalidDnum) => { + render( + + ) + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent( + nb.NationalIdentityNumber.errorDnr + ) + }) + } + ) + + it.each(invalidFnrs)( + 'Invalid identity number is not 18 years or older: %s', + async (invalidFnr) => { + render( + + ) + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent( + nb.NationalIdentityNumber.errorFnr + ) + }) + } + ) + }) + }) +}) diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/stories/NationalIdentityNumber.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/stories/NationalIdentityNumber.stories.tsx index a6870b12bf2..27c2c212d11 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/stories/NationalIdentityNumber.stories.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/stories/NationalIdentityNumber.stories.tsx @@ -1,16 +1,761 @@ import React from 'react' -import { Field } from '../../..' +import { Field, Validator } from '../../..' +import { Wrapper } from 'storybook-utils/helpers' export default { title: 'Eufemia/Extensions/Forms/NationalIdentityNumber', } -export function NationalIdentityNumber() { +const simpleValidator = (value) => { + return value?.length < 4 ? Error('At least 4 characters') : undefined +} + +// const alwaysErrorValidator = (value) => { +// return Error('Always Error Validator') +// } + +const myAdultValidator: Validator = (value, { validators }) => { + const { adultValidator } = validators + + return [adultValidator] +} + +const myAdultFnrDnrValidator: Validator = ( + value, + { validators } +) => { + const { adultValidator, dnrAndFnrValidator } = validators + + return [dnrAndFnrValidator, adultValidator] +} + +const myFnrValidator: Validator = (value, { validators }) => { + const { fnrValidator } = validators + + return [fnrValidator] +} + +const myDnrValidator: Validator = (value, { validators }) => { + const { dnrValidator } = validators + + return [dnrValidator] +} + +const myFnrAndDnrValidator: Validator = ( + value, + { validators } +) => { + const { dnrAndFnrValidator } = validators + + return [dnrAndFnrValidator] +} + +export function ValidatorsUndefinedFalse() { return ( - <> + + + + +

Validate Initially:

+ + + +
+ ) +} + +export function NationalIdentityNumberDefault() { + return ( + - - + +

Validate Initially:

+ + + +
+ ) +} + +export function NationalIdentityNumberAndDNumberOnBlurValidator() { + return ( + + + + +

Validate Initially:

+ + + +
+ ) +} + +export function NationalIdentityNumberOnBlurValidator() { + return ( + + + + +

Validate Initially:

+ + + +
+ ) +} + +export function NationalIdentityNumberValidator() { + return ( + + + + +

Validate Initially:

+ + + +
+ ) +} + +export function DNumberOnBlurValidator() { + return ( + + + + +

Validate Initially:

+ + + +
+ ) +} + +export function DNumberValidator() { + return ( + + + + +

Validate Initially:

+ + + +
+ ) +} + +export function AdultOnBlurValidator() { + return ( + + + + + + + +

Validate Initially:

+ + + + + + +
+ ) +} + +export function AdultValidator() { + return ( + + + + + + + +

Validate Initially:

+ + + + + + +
+ ) +} + +export function AdultOnBlurValidatorAndDefaultValidator() { + return ( + + + + + + +

Validate Initially:

+ + + + + +
+ ) +} + +export function AdultValidatorAndDefaultValidator() { + return ( + + + + + + +

Validate Initially:

+ + + + + +
+ ) +} + +export function CustomValidatorFunction() { + return ( + + + + + + +

Validate Initially:

+ + + + + +
+ ) +} + +export function CustomOnBlurValidatorFunction() { + return ( + + + + + + +

Validate Initially:

+ + + + + +
+ ) +} + +export function CustomValidatorFunctionReturnArray() { + const validatorX = (value) => { + return value?.length < 4 ? Error('At least 4 characters') : undefined + } + + const simpleValidator = () => { + return [validatorX] + } + + return ( + + + + + + +

Validate Initially:

+ + + + + +
+ ) +} + +export function CustomOnBlurValidatorFunctionReturnArray() { + const validatorX = (value) => { + return value?.length < 4 ? Error('At least 4 characters') : undefined + } + + const simpleValidator = () => { + return [validatorX] + } + + return ( + + + + + + +

Validate Initially:

+ + + + + +
) } 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 cb609a0fbfa..2e69eb0b986 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 @@ -120,6 +120,7 @@ export default { errorRequired: 'You must enter a national identity number.', errorFnr: 'Invalid national identity number.', errorDnr: 'Invalid D number.', + errorAdult: 'Must be atleast 18 years of age.', }, OrganizationNumber: { label: 'Organisation number', 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 309b6f1266b..fb93107b368 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 @@ -118,6 +118,7 @@ export default { errorRequired: 'Du må fylle inn et fødselsnummer.', errorFnr: 'Ugyldig fødselsnummer.', errorDnr: 'Ugyldig d-nummer.', + errorAdult: 'Må være minst 18 år.', }, OrganizationNumber: { label: 'Organisasjonsnummer', From 8641d33ad3b11149202741ca44316594c41d643e Mon Sep 17 00:00:00 2001 From: -l Date: Thu, 10 Oct 2024 21:45:56 +0200 Subject: [PATCH 02/12] chore: adds example, docs, tests --- .../NationalIdentityNumber/Examples.tsx | 23 ++ .../NationalIdentityNumber/demos.mdx | 4 + .../NationalIdentityNumber/properties.mdx | 2 +- .../NationalIdentityNumber.tsx | 22 +- ...ionalIdentityNumberAdultValidator.test.tsx | 257 +++++++++++++++++- 5 files changed, 300 insertions(+), 8 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/Examples.tsx index 090a11d59ee..b5bfbab93f5 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/Examples.tsx @@ -211,3 +211,26 @@ export const ValidationExtendValidatorAdult = () => { ) } + +export const ValidationFnrAdult = () => { + return ( + + {() => { + const myFnrAdultValidator = (value, { validators }) => { + const { adultValidator, fnrValidator } = validators + + return [fnrValidator, adultValidator] + } + + return ( + + ) + }} + + ) +} diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/demos.mdx index 34fdcd66139..fdd5f59f534 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/demos.mdx @@ -75,3 +75,7 @@ You can [extend the existing validations](/uilib/extensions/forms/create-compone You can [extend the existing validations](/uilib/extensions/forms/create-component/useFieldProps/info/#validators)(`dnrValidator`, `fnrValidator`, and `dnrAndFnrValidator`) with the `adultValidator`. + +### Validate only national identity numbers(fnr) above 18 years old + + diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx index 7b8902a1fee..79e74aaedbb 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx @@ -26,7 +26,7 @@ import { NationalIdentityNumberProperties } from '@dnb/eufemia/src/extensions/fo - `dnrAndFnrValidator`: - validates the identification number as a D number when first digit is 4 or greater (because a D number has its first number increased by 4). - validates the identification number as a national identity number (fødselsnummer) when first digit is 3 or less. -- `adultValidator`: validates if the identification number has a date of birth that is 18 years or older. +- `adultValidator`: validates if the identification number has a date of birth that is 18 years or older. It uses only the 9 first digits of an identification number to validate, first 6 digits representing the birth of date, and the next three digits are individual numbers. It does not validate the identification number, therefore it's quite common to use this validator together with one of the validators above (`dnrValidator`, `fnrValidator` or `dnrAndFnrValidator`) to validate the identification number as well. See the following [example](/uilib/extensions/forms/feature-fields/NationalIdentityNumber/#extend-validation-with-custom-validation-function) on how to extend validation using the exposed validators. diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx index b19d97de5bf..c3d0515d0f6 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx @@ -26,8 +26,11 @@ function NationalIdentityNumber(props: Props) { const fnrValidator = useCallback( (value: string) => { - // We don't want to validate if the value is undefined - if (value !== undefined && fnr(value).status === 'invalid') { + if ( + value !== undefined && + (Number.parseInt(value.substring(0, 1)) > 3 || + fnr(value).status === 'invalid') + ) { return Error(errorFnr) } }, @@ -36,8 +39,11 @@ function NationalIdentityNumber(props: Props) { const dnrValidator = useCallback( (value: string) => { - // We don't want to validate if the value is undefined - if (value !== undefined && dnr(value).status === 'invalid') { + if ( + value !== undefined && + (Number.parseInt(value.substring(0, 1)) < 4 || + dnr(value).status === 'invalid') + ) { return Error(errorDnr) } }, @@ -58,7 +64,9 @@ function NationalIdentityNumber(props: Props) { const adultValidator = useCallback( (value: string) => { - // We don't want to validate if the value is undefined + // if (value !== undefined && value.length < 9) { + // return Error(errorAdultPattern) + // } if (value !== undefined && !is18YearsOrOlder(value)) { return Error(errorAdult) } @@ -142,7 +150,9 @@ function is18YearsOrOlder(value: string) { const year = isBornIn20XX ? `20${yearPart}` : `19${yearPart}` const month = Number.parseInt(value.substring(2, 4)) - const isDnr = dnr(value).status === 'valid' + const differentiatorValue = + value.length > 0 ? Number.parseInt(value.substring(0, 1)) : undefined + const isDnr = differentiatorValue && differentiatorValue > 3 const day = isDnr ? Number.parseInt(value.substring(0, 2)) - 40 diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx index 7025d81ce52..8474a8e829b 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx @@ -15,6 +15,24 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { return [dnrAndFnrValidator, adultValidator] } + const extendingDnrValidatorWithAdultValidator: Validator = ( + value, + { validators } + ) => { + const { adultValidator, dnrValidator } = validators + + return [dnrValidator, adultValidator] + } + + const extendingFnrValidatorWithAdultValidator: Validator = ( + value, + { validators } + ) => { + const { adultValidator, fnrValidator } = validators + + return [fnrValidator, adultValidator] + } + const myAdultValidator: Validator = (value, { validators }) => { const { adultValidator } = validators @@ -97,7 +115,6 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { '07070663990', '11030699302', '31010699021', - '49100651997', ] const fnr99Years = [ '14102535759', @@ -404,5 +421,243 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { } ) }) + + describe('when extending the dnrValidator as validator', () => { + it.each(dnr18YearsOldAndOlder)( + 'D number is 18 years or older : %s', + async (validDnum) => { + render( + + ) + + await expect(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + }).neverToResolve() + } + ) + + it.each(dnrUnder18YearsOld)( + 'D number is not 18 years or older: %s', + async (invalidDnum) => { + render( + + ) + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent( + nb.NationalIdentityNumber.errorAdult + ) + }) + } + ) + + it.each([...invalidDnums, ...invalidFnrs, ...fnr18YearsOldAndOlder])( + 'Invalid d number: %s', + async (invalidDnum) => { + render( + + ) + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent( + nb.NationalIdentityNumber.errorDnr + ) + }) + } + ) + }) + + describe('when extending the dnrValidator as onBlurValidator', () => { + it.each(dnr18YearsOldAndOlder)( + 'D number is 18 years or older : %s', + async (validDnum) => { + render( + + ) + + await expect(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + }).neverToResolve() + } + ) + + it.each(dnrUnder18YearsOld)( + 'D number is not 18 years or older: %s', + async (invalidDnum) => { + render( + + ) + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent( + nb.NationalIdentityNumber.errorAdult + ) + }) + } + ) + + it.each([ + ...invalidDnums, + ...invalidFnrs, + ...fnr18YearsOldAndOlder, + ...fnrUnder18YearsOld, + ])('Invalid d number: %s', async (invalidDnum) => { + render( + + ) + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent( + nb.NationalIdentityNumber.errorDnr + ) + }) + }) + }) + + describe('when extending the fnrValidator as validator', () => { + it.each(fnr18YearsOldAndOlder)( + 'Identity number(fnr) is 18 years or older : %s', + async (validFnr) => { + render( + + ) + + await expect(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + }).neverToResolve() + } + ) + + it.each(fnrUnder18YearsOld)( + 'Identity number(fnr) is not 18 years or older: %s', + async (invalidFnr) => { + render( + + ) + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent( + nb.NationalIdentityNumber.errorAdult + ) + }) + } + ) + + it.each([...invalidFnrs, ...invalidDnums, ...dnr18YearsOldAndOlder])( + 'Invalid identity number(fnr): %s', + async (invalidFnr) => { + render( + + ) + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent( + nb.NationalIdentityNumber.errorFnr + ) + }) + } + ) + }) + + describe('when extending the fnrValidator as onBlurValidator', () => { + it.each(fnr18YearsOldAndOlder)( + 'Identity number(fnr) is 18 years or older : %s', + async (validFnr) => { + render( + + ) + + await expect(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + }).neverToResolve() + } + ) + + it.each(fnrUnder18YearsOld)( + 'Identity number(fnr) is not 18 years or older: %s', + async (invalidFnr) => { + render( + + ) + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent( + nb.NationalIdentityNumber.errorAdult + ) + }) + } + ) + + it.each([ + ...invalidFnrs, + ...invalidDnums, + ...dnr18YearsOldAndOlder, + ...dnrUnder18YearsOld, + ])('Invalid identity number(fnr): %s', async (invalidFnr) => { + render( + + ) + await waitFor(() => { + expect(screen.queryByRole('alert')).toBeInTheDocument() + expect(screen.queryByRole('alert')).toHaveTextContent( + nb.NationalIdentityNumber.errorFnr + ) + }) + }) + }) }) }) From a7f05f85865cac264c2993ed8266f6dda80e1f49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Thu, 10 Oct 2024 22:34:46 +0200 Subject: [PATCH 03/12] Add `createAboveAgeValidator` --- .../NationalIdentityNumber/Examples.tsx | 11 ++-- .../NationalIdentityNumber/properties.mdx | 11 ++++ .../NationalIdentityNumber.tsx | 66 ++++++++++--------- ...ionalIdentityNumberAdultValidator.test.tsx | 40 +++++------ .../NationalIdentityNumber.stories.tsx | 11 ++-- .../forms/constants/locales/en-GB.ts | 2 +- .../forms/constants/locales/nb-NO.ts | 2 +- 7 files changed, 80 insertions(+), 63 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/Examples.tsx index b5bfbab93f5..e860513b988 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/Examples.tsx @@ -1,4 +1,5 @@ import ComponentBox from '../../../../../../shared/tags/ComponentBox' +import { createAboveAgeValidator } from '@dnb/eufemia/src/extensions/forms/Field/NationalIdentityNumber' import { Field } from '@dnb/eufemia/src/extensions/forms' export const Empty = () => { @@ -191,10 +192,11 @@ export const ValidationExtendValidator = () => { export const ValidationExtendValidatorAdult = () => { return ( - + {() => { + const adultValidator = createAboveAgeValidator(18) const myAdultValidator = (value, { validators }) => { - const { adultValidator, dnrAndFnrValidator } = validators + const { dnrAndFnrValidator } = validators return [dnrAndFnrValidator, adultValidator] } @@ -214,10 +216,11 @@ export const ValidationExtendValidatorAdult = () => { export const ValidationFnrAdult = () => { return ( - + {() => { + const adultValidator = createAboveAgeValidator(18) const myFnrAdultValidator = (value, { validators }) => { - const { adultValidator, fnrValidator } = validators + const { fnrValidator } = validators return [fnrValidator, adultValidator] } diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx index 79e74aaedbb..bb27162d59b 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx @@ -28,6 +28,17 @@ import { NationalIdentityNumberProperties } from '@dnb/eufemia/src/extensions/fo - validates the identification number as a national identity number (fødselsnummer) when first digit is 3 or less. - `adultValidator`: validates if the identification number has a date of birth that is 18 years or older. It uses only the 9 first digits of an identification number to validate, first 6 digits representing the birth of date, and the next three digits are individual numbers. It does not validate the identification number, therefore it's quite common to use this validator together with one of the validators above (`dnrValidator`, `fnrValidator` or `dnrAndFnrValidator`) to validate the identification number as well. +You can create your own `adultValidator` by using the `createAboveAgeValidator` function. It takes an age as a parameter and returns a validator function. The validator function takes a value and returns an error message if the value is not above the given age. + +You need to import the `createAboveAgeValidator` function from the `Field.NationalIdentityNumber` component: + +```tsx +import { createAboveAgeValidator } from '@dnb/eufemia/extensions/forms/Field/NationalIdentityNumber' + +// Create a validator that validates if the value is above 18 years old +const above18Validator = createAboveAgeValidator(18) +``` + See the following [example](/uilib/extensions/forms/feature-fields/NationalIdentityNumber/#extend-validation-with-custom-validation-function) on how to extend validation using the exposed validators. ## Translations diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx index c3d0515d0f6..b983325debc 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useMemo } from 'react' import StringField, { Props as StringFieldProps } from '../String' import { dnr, fnr } from '@navikt/fnrvalidator' -import { Validator } from '../../types' +import { FormError, Validator } from '../../types' import useErrorMessage from '../../hooks/useErrorMessage' import useTranslation from '../../hooks/useTranslation' @@ -14,14 +14,14 @@ export type Props = Omit & { function NationalIdentityNumber(props: Props) { const translations = useTranslation().NationalIdentityNumber - const { label, errorRequired, errorFnr, errorDnr, errorAdult } = + const { label, errorRequired, errorFnr, errorDnr, errorBelowAge } = translations const errorMessages = useErrorMessage(props.path, props.errorMessages, { required: errorRequired, pattern: errorFnr, errorFnr, errorDnr, - errorAdult, + errorBelowAge, }) const fnrValidator = useCallback( @@ -62,18 +62,6 @@ function NationalIdentityNumber(props: Props) { [dnrValidator, fnrValidator] ) - const adultValidator = useCallback( - (value: string) => { - // if (value !== undefined && value.length < 9) { - // return Error(errorAdultPattern) - // } - if (value !== undefined && !is18YearsOrOlder(value)) { - return Error(errorAdult) - } - }, - [errorAdult] - ) - const { validate = true, omitMask, @@ -120,33 +108,34 @@ function NationalIdentityNumber(props: Props) { dnrValidator, fnrValidator, dnrAndFnrValidator, - adultValidator, }, } return } -function is18YearsOrOlder(value: string) { - if (!new RegExp('^[0-9]{11}$').test(value)) return false +export function getAgeByBirthDate(birthDate: Date): number { + const today = new Date() + const age = today.getFullYear() - birthDate.getFullYear() + const month = today.getMonth() - birthDate.getMonth() + const day = today.getDate() - birthDate.getDate() - function getAge(birthDate: Date): number { - const today = new Date() - const age = today.getFullYear() - birthDate.getFullYear() - const month = today.getMonth() - birthDate.getMonth() - const day = today.getDate() - birthDate.getDate() + if (month < 0 || (month === 0 && day < 0)) { + return age - 1 + } - if (month < 0 || (month === 0 && day < 0)) { - return age - 1 - } + return age +} - return age +export function getBirthDateByFnrOrDnr(value: string) { + if (value === undefined) { + return // stop here } const yearPart = value.substring(4, 6) - const individNumber = Number.parseInt(value.substring(6, 9)) + const individualNumber = Number.parseInt(value.substring(6, 9)) - const isBornIn20XX = individNumber >= 500 && individNumber <= 999 + const isBornIn20XX = individualNumber >= 500 && individualNumber <= 999 const year = isBornIn20XX ? `20${yearPart}` : `19${yearPart}` const month = Number.parseInt(value.substring(2, 4)) @@ -157,9 +146,24 @@ function is18YearsOrOlder(value: string) { const day = isDnr ? Number.parseInt(value.substring(0, 2)) - 40 : Number.parseInt(value.substring(0, 2)) - const date = new Date(Number.parseInt(year), month - 1, day) - return getAge(date) >= 18 + return new Date(Number.parseInt(year), month - 1, day) +} + +export function createAboveAgeValidator(age: number) { + return (value: string) => { + if (typeof value !== 'string' || value.length < 9) { + return + } + + const date = getBirthDateByFnrOrDnr(value) + if (getAgeByBirthDate(date) < age) { + return new FormError('NationalIdentityNumber.errorBelowAge', { + validationRule: 'errorBelowAge', // "validationRule" Will be removed in future PR + messageValues: { age: String(age) }, + }) + } + } } NationalIdentityNumber._supportsSpacingProps = true diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx index 8474a8e829b..86e76284fc8 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx @@ -1,16 +1,22 @@ import React from 'react' import { render, waitFor, screen } from '@testing-library/react' import { Field, Validator } from '../../..' +import { createAboveAgeValidator } from '../NationalIdentityNumber' import nbNO from '../../../constants/locales/nb-NO' const nb = nbNO['nb-NO'] describe('Field.NationalIdentityNumber with adultValidator', () => { + const errorBelowAge = nb.NationalIdentityNumber.errorBelowAge.replace( + '{age}', + '18' + ) + const adultValidator = createAboveAgeValidator(18) const extendingDnrAndFnrValidatorWithAdultValidator: Validator< string > = (value, { validators }) => { - const { adultValidator, dnrAndFnrValidator } = validators + const { dnrAndFnrValidator } = validators return [dnrAndFnrValidator, adultValidator] } @@ -19,7 +25,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { value, { validators } ) => { - const { adultValidator, dnrValidator } = validators + const { dnrValidator } = validators return [dnrValidator, adultValidator] } @@ -28,14 +34,12 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { value, { validators } ) => { - const { adultValidator, fnrValidator } = validators + const { fnrValidator } = validators return [fnrValidator, adultValidator] } - const myAdultValidator: Validator = (value, { validators }) => { - const { adultValidator } = validators - + const myAdultValidator: Validator = () => { return [adultValidator] } @@ -56,20 +60,18 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { }) }) - it('should display error when validateInitially and value', async () => { + it.skip('should display error when value is invalid', async () => { render( ) await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() - expect(screen.queryByRole('alert')).toHaveTextContent( - nb.NationalIdentityNumber.errorAdult - ) + expect(screen.queryByRole('alert')).toHaveTextContent(errorBelowAge) }) }) @@ -213,7 +215,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - nb.NationalIdentityNumber.errorAdult + errorBelowAge ) }) } @@ -251,7 +253,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - nb.NationalIdentityNumber.errorAdult + errorBelowAge ) }) } @@ -291,7 +293,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - nb.NationalIdentityNumber.errorAdult + errorBelowAge ) }) } @@ -373,7 +375,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - nb.NationalIdentityNumber.errorAdult + errorBelowAge ) }) } @@ -455,7 +457,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - nb.NationalIdentityNumber.errorAdult + errorBelowAge ) }) } @@ -513,7 +515,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - nb.NationalIdentityNumber.errorAdult + errorBelowAge ) }) } @@ -574,7 +576,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - nb.NationalIdentityNumber.errorAdult + errorBelowAge ) }) } @@ -632,7 +634,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - nb.NationalIdentityNumber.errorAdult + errorBelowAge ) }) } diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/stories/NationalIdentityNumber.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/stories/NationalIdentityNumber.stories.tsx index 27c2c212d11..7d30083599b 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/stories/NationalIdentityNumber.stories.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/stories/NationalIdentityNumber.stories.tsx @@ -1,6 +1,7 @@ import React from 'react' import { Field, Validator } from '../../..' import { Wrapper } from 'storybook-utils/helpers' +import { createAboveAgeValidator } from '../NationalIdentityNumber' export default { title: 'Eufemia/Extensions/Forms/NationalIdentityNumber', @@ -10,13 +11,9 @@ const simpleValidator = (value) => { return value?.length < 4 ? Error('At least 4 characters') : undefined } -// const alwaysErrorValidator = (value) => { -// return Error('Always Error Validator') -// } - -const myAdultValidator: Validator = (value, { validators }) => { - const { adultValidator } = validators +const adultValidator = createAboveAgeValidator(18) +const myAdultValidator: Validator = () => { return [adultValidator] } @@ -24,7 +21,7 @@ const myAdultFnrDnrValidator: Validator = ( value, { validators } ) => { - const { adultValidator, dnrAndFnrValidator } = validators + const { dnrAndFnrValidator } = validators return [dnrAndFnrValidator, adultValidator] } 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 2e69eb0b986..19730fd0634 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 @@ -120,7 +120,7 @@ export default { errorRequired: 'You must enter a national identity number.', errorFnr: 'Invalid national identity number.', errorDnr: 'Invalid D number.', - errorAdult: 'Must be atleast 18 years of age.', + errorBelowAge: 'Must be at least {age} years of age.', }, OrganizationNumber: { label: 'Organisation number', 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 fb93107b368..924bdbccb22 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 @@ -118,7 +118,7 @@ export default { errorRequired: 'Du må fylle inn et fødselsnummer.', errorFnr: 'Ugyldig fødselsnummer.', errorDnr: 'Ugyldig d-nummer.', - errorAdult: 'Må være minst 18 år.', + errorBelowAge: 'Må være minst {age} år.', }, OrganizationNumber: { label: 'Organisasjonsnummer', From 9e2b51118916f736066e044c9e014c98812f8fdb Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 10 Oct 2024 23:22:41 +0200 Subject: [PATCH 04/12] improves validator and docs (#4110) --- .../feature-fields/NationalIdentityNumber/properties.mdx | 2 +- .../Field/NationalIdentityNumber/NationalIdentityNumber.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx index bb27162d59b..40ca1549a31 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx @@ -26,7 +26,7 @@ import { NationalIdentityNumberProperties } from '@dnb/eufemia/src/extensions/fo - `dnrAndFnrValidator`: - validates the identification number as a D number when first digit is 4 or greater (because a D number has its first number increased by 4). - validates the identification number as a national identity number (fødselsnummer) when first digit is 3 or less. -- `adultValidator`: validates if the identification number has a date of birth that is 18 years or older. It uses only the 9 first digits of an identification number to validate, first 6 digits representing the birth of date, and the next three digits are individual numbers. It does not validate the identification number, therefore it's quite common to use this validator together with one of the validators above (`dnrValidator`, `fnrValidator` or `dnrAndFnrValidator`) to validate the identification number as well. +- `adultValidator`: validates if the identification number has a date of birth that is 18 years or older. It uses only the 7 first digits of an identification number to validate, first 6 digits representing the birth of date, and the next digit represents the century. It does not validate the identification number, therefore it's quite common to use this validator together with one of the validators above (`dnrValidator`, `fnrValidator` or `dnrAndFnrValidator`) to validate the identification number as well. You can create your own `adultValidator` by using the `createAboveAgeValidator` function. It takes an age as a parameter and returns a validator function. The validator function takes a value and returns an error message if the value is not above the given age. diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx index b983325debc..e9fe32f58f1 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx @@ -133,9 +133,9 @@ export function getBirthDateByFnrOrDnr(value: string) { } const yearPart = value.substring(4, 6) - const individualNumber = Number.parseInt(value.substring(6, 9)) + const centuryNumber = Number.parseInt(value.substring(6, 7)) - const isBornIn20XX = individualNumber >= 500 && individualNumber <= 999 + const isBornIn20XX = centuryNumber >= 5 const year = isBornIn20XX ? `20${yearPart}` : `19${yearPart}` const month = Number.parseInt(value.substring(2, 4)) From fa655f99b7548ebb00319daf034630e06b17b7ff Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 11 Oct 2024 06:28:21 +0200 Subject: [PATCH 05/12] Update packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx --- .../Field/NationalIdentityNumber/NationalIdentityNumber.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx index e9fe32f58f1..c6583973844 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx @@ -152,7 +152,7 @@ export function getBirthDateByFnrOrDnr(value: string) { export function createAboveAgeValidator(age: number) { return (value: string) => { - if (typeof value !== 'string' || value.length < 9) { + if (typeof value !== 'string' || value.length < 7) { return } From b6eac3aec2191ebf3b16f3c521c881467117c843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Fri, 11 Oct 2024 06:49:55 +0200 Subject: [PATCH 06/12] Ensure invalid/short numbers are not age calculated --- .../NationalIdentityNumber.tsx | 20 +++++++++++-------- ...ionalIdentityNumberAdultValidator.test.tsx | 4 ++-- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx index c6583973844..fa1ff7e969f 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx @@ -152,17 +152,21 @@ export function getBirthDateByFnrOrDnr(value: string) { export function createAboveAgeValidator(age: number) { return (value: string) => { - if (typeof value !== 'string' || value.length < 7) { - return + if (typeof value !== 'string') { + return // stop here } - const date = getBirthDateByFnrOrDnr(value) - if (getAgeByBirthDate(date) < age) { - return new FormError('NationalIdentityNumber.errorBelowAge', { - validationRule: 'errorBelowAge', // "validationRule" Will be removed in future PR - messageValues: { age: String(age) }, - }) + if (value.length > 6) { + const date = getBirthDateByFnrOrDnr(value) + if (getAgeByBirthDate(date) >= age) { + return // stop here + } } + + return new FormError('NationalIdentityNumber.errorBelowAge', { + validationRule: 'errorBelowAge', // "validationRule" Will be removed in future PR + messageValues: { age: String(age) }, + }) } } diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx index 86e76284fc8..be60fbbc4c9 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx @@ -60,12 +60,12 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { }) }) - it.skip('should display error when value is invalid', async () => { + it('should display error when value is invalid', async () => { render( ) From 2d9b94c1e11038d4fe3c5c48a9e252b9f6c01f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20H=C3=B8egh?= Date: Fri, 11 Oct 2024 06:52:19 +0200 Subject: [PATCH 07/12] Rename to `createAgeValidator` --- .../feature-fields/NationalIdentityNumber/Examples.tsx | 10 +++++----- .../NationalIdentityNumber/properties.mdx | 8 ++++---- .../NationalIdentityNumber/NationalIdentityNumber.tsx | 2 +- .../NationalIdentityNumberAdultValidator.test.tsx | 4 ++-- .../stories/NationalIdentityNumber.stories.tsx | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/Examples.tsx index e860513b988..ecc7344a894 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/Examples.tsx @@ -1,5 +1,5 @@ import ComponentBox from '../../../../../../shared/tags/ComponentBox' -import { createAboveAgeValidator } from '@dnb/eufemia/src/extensions/forms/Field/NationalIdentityNumber' +import { createAgeValidator } from '@dnb/eufemia/src/extensions/forms/Field/NationalIdentityNumber' import { Field } from '@dnb/eufemia/src/extensions/forms' export const Empty = () => { @@ -192,9 +192,9 @@ export const ValidationExtendValidator = () => { export const ValidationExtendValidatorAdult = () => { return ( - + {() => { - const adultValidator = createAboveAgeValidator(18) + const adultValidator = createAgeValidator(18) const myAdultValidator = (value, { validators }) => { const { dnrAndFnrValidator } = validators @@ -216,9 +216,9 @@ export const ValidationExtendValidatorAdult = () => { export const ValidationFnrAdult = () => { return ( - + {() => { - const adultValidator = createAboveAgeValidator(18) + const adultValidator = createAgeValidator(18) const myFnrAdultValidator = (value, { validators }) => { const { fnrValidator } = validators diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx index 40ca1549a31..5c3e8cad7c8 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx @@ -28,15 +28,15 @@ import { NationalIdentityNumberProperties } from '@dnb/eufemia/src/extensions/fo - validates the identification number as a national identity number (fødselsnummer) when first digit is 3 or less. - `adultValidator`: validates if the identification number has a date of birth that is 18 years or older. It uses only the 7 first digits of an identification number to validate, first 6 digits representing the birth of date, and the next digit represents the century. It does not validate the identification number, therefore it's quite common to use this validator together with one of the validators above (`dnrValidator`, `fnrValidator` or `dnrAndFnrValidator`) to validate the identification number as well. -You can create your own `adultValidator` by using the `createAboveAgeValidator` function. It takes an age as a parameter and returns a validator function. The validator function takes a value and returns an error message if the value is not above the given age. +You can create your own `adultValidator` by using the `createAgeValidator` function. It takes an age as a parameter and returns a validator function. The validator function takes a value and returns an error message if the value is not above the given age. -You need to import the `createAboveAgeValidator` function from the `Field.NationalIdentityNumber` component: +You need to import the `createAgeValidator` function from the `Field.NationalIdentityNumber` component: ```tsx -import { createAboveAgeValidator } from '@dnb/eufemia/extensions/forms/Field/NationalIdentityNumber' +import { createAgeValidator } from '@dnb/eufemia/extensions/forms/Field/NationalIdentityNumber' // Create a validator that validates if the value is above 18 years old -const above18Validator = createAboveAgeValidator(18) +const above18Validator = createAgeValidator(18) ``` See the following [example](/uilib/extensions/forms/feature-fields/NationalIdentityNumber/#extend-validation-with-custom-validation-function) on how to extend validation using the exposed validators. diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx index fa1ff7e969f..a796094ae4e 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx @@ -150,7 +150,7 @@ export function getBirthDateByFnrOrDnr(value: string) { return new Date(Number.parseInt(year), month - 1, day) } -export function createAboveAgeValidator(age: number) { +export function createAgeValidator(age: number) { return (value: string) => { if (typeof value !== 'string') { return // stop here diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx index be60fbbc4c9..b8c4f2d6291 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx @@ -1,7 +1,7 @@ import React from 'react' import { render, waitFor, screen } from '@testing-library/react' import { Field, Validator } from '../../..' -import { createAboveAgeValidator } from '../NationalIdentityNumber' +import { createAgeValidator } from '../NationalIdentityNumber' import nbNO from '../../../constants/locales/nb-NO' @@ -12,7 +12,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { '{age}', '18' ) - const adultValidator = createAboveAgeValidator(18) + const adultValidator = createAgeValidator(18) const extendingDnrAndFnrValidatorWithAdultValidator: Validator< string > = (value, { validators }) => { diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/stories/NationalIdentityNumber.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/stories/NationalIdentityNumber.stories.tsx index 7d30083599b..0b6284f1d0d 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/stories/NationalIdentityNumber.stories.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/stories/NationalIdentityNumber.stories.tsx @@ -1,7 +1,7 @@ import React from 'react' import { Field, Validator } from '../../..' import { Wrapper } from 'storybook-utils/helpers' -import { createAboveAgeValidator } from '../NationalIdentityNumber' +import { createAgeValidator } from '../NationalIdentityNumber' export default { title: 'Eufemia/Extensions/Forms/NationalIdentityNumber', @@ -11,7 +11,7 @@ const simpleValidator = (value) => { return value?.length < 4 ? Error('At least 4 characters') : undefined } -const adultValidator = createAboveAgeValidator(18) +const adultValidator = createAgeValidator(18) const myAdultValidator: Validator = () => { return [adultValidator] From 6a0d0ae47d328390cc7fc7fd3170ba3c1300f4b4 Mon Sep 17 00:00:00 2001 From: -l Date: Fri, 11 Oct 2024 09:27:11 +0200 Subject: [PATCH 08/12] errorBelowAge -> errorAgeValidator --- .../NationalIdentityNumber.tsx | 8 +++--- ...ionalIdentityNumberAdultValidator.test.tsx | 26 +++++++++---------- .../forms/constants/locales/en-GB.ts | 2 +- .../forms/constants/locales/nb-NO.ts | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx index a796094ae4e..e7b4bb685a2 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx @@ -14,14 +14,14 @@ export type Props = Omit & { function NationalIdentityNumber(props: Props) { const translations = useTranslation().NationalIdentityNumber - const { label, errorRequired, errorFnr, errorDnr, errorBelowAge } = + const { label, errorRequired, errorFnr, errorDnr, errorAgeValidator } = translations const errorMessages = useErrorMessage(props.path, props.errorMessages, { required: errorRequired, pattern: errorFnr, errorFnr, errorDnr, - errorBelowAge, + errorAgeValidator, }) const fnrValidator = useCallback( @@ -163,8 +163,8 @@ export function createAgeValidator(age: number) { } } - return new FormError('NationalIdentityNumber.errorBelowAge', { - validationRule: 'errorBelowAge', // "validationRule" Will be removed in future PR + return new FormError('NationalIdentityNumber.errorAgeValidator', { + validationRule: 'errorAgeValidator', // "validationRule" Will be removed in future PR messageValues: { age: String(age) }, }) } diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx index b8c4f2d6291..acca1adc76a 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx @@ -8,10 +8,8 @@ import nbNO from '../../../constants/locales/nb-NO' const nb = nbNO['nb-NO'] describe('Field.NationalIdentityNumber with adultValidator', () => { - const errorBelowAge = nb.NationalIdentityNumber.errorBelowAge.replace( - '{age}', - '18' - ) + const errorAgeValidator = + nb.NationalIdentityNumber.errorAgeValidator.replace('{age}', '18') const adultValidator = createAgeValidator(18) const extendingDnrAndFnrValidatorWithAdultValidator: Validator< string @@ -71,7 +69,9 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() - expect(screen.queryByRole('alert')).toHaveTextContent(errorBelowAge) + expect(screen.queryByRole('alert')).toHaveTextContent( + errorAgeValidator + ) }) }) @@ -215,7 +215,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - errorBelowAge + errorAgeValidator ) }) } @@ -253,7 +253,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - errorBelowAge + errorAgeValidator ) }) } @@ -293,7 +293,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - errorBelowAge + errorAgeValidator ) }) } @@ -375,7 +375,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - errorBelowAge + errorAgeValidator ) }) } @@ -457,7 +457,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - errorBelowAge + errorAgeValidator ) }) } @@ -515,7 +515,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - errorBelowAge + errorAgeValidator ) }) } @@ -576,7 +576,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - errorBelowAge + errorAgeValidator ) }) } @@ -634,7 +634,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - errorBelowAge + errorAgeValidator ) }) } 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 19730fd0634..1a577705ffc 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 @@ -120,7 +120,7 @@ export default { errorRequired: 'You must enter a national identity number.', errorFnr: 'Invalid national identity number.', errorDnr: 'Invalid D number.', - errorBelowAge: 'Must be at least {age} years of age.', + errorAgeValidator: 'Must be at least {age} years of age.', }, OrganizationNumber: { label: 'Organisation number', 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 924bdbccb22..924432e7bf6 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 @@ -118,7 +118,7 @@ export default { errorRequired: 'Du må fylle inn et fødselsnummer.', errorFnr: 'Ugyldig fødselsnummer.', errorDnr: 'Ugyldig d-nummer.', - errorBelowAge: 'Må være minst {age} år.', + errorAgeValidator: 'Må være minst {age} år.', }, OrganizationNumber: { label: 'Organisasjonsnummer', From fc633ee08e1a02b44eb9f080e812a51a5efc2948 Mon Sep 17 00:00:00 2001 From: -l Date: Fri, 11 Oct 2024 09:47:21 +0200 Subject: [PATCH 09/12] update docs --- .../NationalIdentityNumber/Examples.tsx | 10 +++++----- .../feature-fields/NationalIdentityNumber/demos.mdx | 4 ++-- .../NationalIdentityNumber/properties.mdx | 11 ++++++----- .../NationalIdentityNumber/NationalIdentityNumber.tsx | 2 +- .../NationalIdentityNumberAdultValidator.test.tsx | 4 ++-- .../stories/NationalIdentityNumber.stories.tsx | 4 ++-- 6 files changed, 18 insertions(+), 17 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/Examples.tsx index ecc7344a894..7bf53f93db6 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/Examples.tsx @@ -1,5 +1,5 @@ import ComponentBox from '../../../../../../shared/tags/ComponentBox' -import { createAgeValidator } from '@dnb/eufemia/src/extensions/forms/Field/NationalIdentityNumber' +import { createMinimumAgeValidator } from '@dnb/eufemia/src/extensions/forms/Field/NationalIdentityNumber' import { Field } from '@dnb/eufemia/src/extensions/forms' export const Empty = () => { @@ -192,9 +192,9 @@ export const ValidationExtendValidator = () => { export const ValidationExtendValidatorAdult = () => { return ( - + {() => { - const adultValidator = createAgeValidator(18) + const adultValidator = createMinimumAgeValidator(18) const myAdultValidator = (value, { validators }) => { const { dnrAndFnrValidator } = validators @@ -216,9 +216,9 @@ export const ValidationExtendValidatorAdult = () => { export const ValidationFnrAdult = () => { return ( - + {() => { - const adultValidator = createAgeValidator(18) + const adultValidator = createMinimumAgeValidator(18) const myFnrAdultValidator = (value, { validators }) => { const { fnrValidator } = validators diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/demos.mdx index fdd5f59f534..446131cc0b7 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/demos.mdx @@ -66,13 +66,13 @@ You can provide your own validation function, either to `validator` or `onBlurVa ### Extend validation with custom validation function -You can [extend the existing validations](/uilib/extensions/forms/create-component/useFieldProps/info/#validators)(`dnrValidator`, `fnrValidator`, `dnrAndFnrValidator`, and `adultValidator`) with your own validation function. +You can [extend the existing validations](/uilib/extensions/forms/create-component/useFieldProps/info/#validators)(`dnrValidator`, `fnrValidator`, `dnrAndFnrValidator`, and make your own age validator by using the `createMinimumAgeValidator` function) with your own validation function. ### Extend validation with adult validator -You can [extend the existing validations](/uilib/extensions/forms/create-component/useFieldProps/info/#validators)(`dnrValidator`, `fnrValidator`, and `dnrAndFnrValidator`) with the `adultValidator`. +You can [extend the existing validations](/uilib/extensions/forms/create-component/useFieldProps/info/#validators)(`dnrValidator`, `fnrValidator`, and `dnrAndFnrValidator`) with your own age validator, by using the `createMinimumAgeValidator` function. diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx index 5c3e8cad7c8..faa37919def 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx @@ -26,17 +26,18 @@ import { NationalIdentityNumberProperties } from '@dnb/eufemia/src/extensions/fo - `dnrAndFnrValidator`: - validates the identification number as a D number when first digit is 4 or greater (because a D number has its first number increased by 4). - validates the identification number as a national identity number (fødselsnummer) when first digit is 3 or less. -- `adultValidator`: validates if the identification number has a date of birth that is 18 years or older. It uses only the 7 first digits of an identification number to validate, first 6 digits representing the birth of date, and the next digit represents the century. It does not validate the identification number, therefore it's quite common to use this validator together with one of the validators above (`dnrValidator`, `fnrValidator` or `dnrAndFnrValidator`) to validate the identification number as well. -You can create your own `adultValidator` by using the `createAgeValidator` function. It takes an age as a parameter and returns a validator function. The validator function takes a value and returns an error message if the value is not above the given age. +You can create your own age validator by using the `createMinimumAgeValidator` function. It takes an age as a parameter and returns a validator function. The validator function takes a value and returns an error message if the value is not above the given age. +It validates if the identification number has a date of birth that is 18 years or older. It uses only the 7 first digits of an identification number to validate, first 6 digits representing the birth of date, and the next digit represents the century. +As it only use the 7 first digits, it does not validate the identification number, therefore it's quite common to use this validator together with one of the validators above (`dnrValidator`, `fnrValidator` or `dnrAndFnrValidator`) to validate the identification number as well. -You need to import the `createAgeValidator` function from the `Field.NationalIdentityNumber` component: +You need to import the `createMinimumAgeValidator` function from the `Field.NationalIdentityNumber` component: ```tsx -import { createAgeValidator } from '@dnb/eufemia/extensions/forms/Field/NationalIdentityNumber' +import { createMinimumAgeValidator } from '@dnb/eufemia/extensions/forms/Field/NationalIdentityNumber' // Create a validator that validates if the value is above 18 years old -const above18Validator = createAgeValidator(18) +const above18Validator = createMinimumAgeValidator(18) ``` See the following [example](/uilib/extensions/forms/feature-fields/NationalIdentityNumber/#extend-validation-with-custom-validation-function) on how to extend validation using the exposed validators. diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx index e7b4bb685a2..fe3e2139077 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx @@ -150,7 +150,7 @@ export function getBirthDateByFnrOrDnr(value: string) { return new Date(Number.parseInt(year), month - 1, day) } -export function createAgeValidator(age: number) { +export function createMinimumAgeValidator(age: number) { return (value: string) => { if (typeof value !== 'string') { return // stop here diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx index acca1adc76a..1a3d269d760 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx @@ -1,7 +1,7 @@ import React from 'react' import { render, waitFor, screen } from '@testing-library/react' import { Field, Validator } from '../../..' -import { createAgeValidator } from '../NationalIdentityNumber' +import { createMinimumAgeValidator } from '../NationalIdentityNumber' import nbNO from '../../../constants/locales/nb-NO' @@ -10,7 +10,7 @@ const nb = nbNO['nb-NO'] describe('Field.NationalIdentityNumber with adultValidator', () => { const errorAgeValidator = nb.NationalIdentityNumber.errorAgeValidator.replace('{age}', '18') - const adultValidator = createAgeValidator(18) + const adultValidator = createMinimumAgeValidator(18) const extendingDnrAndFnrValidatorWithAdultValidator: Validator< string > = (value, { validators }) => { diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/stories/NationalIdentityNumber.stories.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/stories/NationalIdentityNumber.stories.tsx index 0b6284f1d0d..02b55500e24 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/stories/NationalIdentityNumber.stories.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/stories/NationalIdentityNumber.stories.tsx @@ -1,7 +1,7 @@ import React from 'react' import { Field, Validator } from '../../..' import { Wrapper } from 'storybook-utils/helpers' -import { createAgeValidator } from '../NationalIdentityNumber' +import { createMinimumAgeValidator } from '../NationalIdentityNumber' export default { title: 'Eufemia/Extensions/Forms/NationalIdentityNumber', @@ -11,7 +11,7 @@ const simpleValidator = (value) => { return value?.length < 4 ? Error('At least 4 characters') : undefined } -const adultValidator = createAgeValidator(18) +const adultValidator = createMinimumAgeValidator(18) const myAdultValidator: Validator = () => { return [adultValidator] From 5751a0bbbfe292d9334ac2758689b7c263d5f60e Mon Sep 17 00:00:00 2001 From: -l Date: Fri, 11 Oct 2024 09:48:58 +0200 Subject: [PATCH 10/12] errorAgeValidator -> errorMinimumAgeValidator --- .../NationalIdentityNumber.tsx | 22 ++++++++++------ ...ionalIdentityNumberAdultValidator.test.tsx | 25 +++++++++++-------- .../forms/constants/locales/en-GB.ts | 2 +- .../forms/constants/locales/nb-NO.ts | 2 +- 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx index fe3e2139077..277f1f3419f 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/NationalIdentityNumber.tsx @@ -14,14 +14,19 @@ export type Props = Omit & { function NationalIdentityNumber(props: Props) { const translations = useTranslation().NationalIdentityNumber - const { label, errorRequired, errorFnr, errorDnr, errorAgeValidator } = - translations + const { + label, + errorRequired, + errorFnr, + errorDnr, + errorMinimumAgeValidator, + } = translations const errorMessages = useErrorMessage(props.path, props.errorMessages, { required: errorRequired, pattern: errorFnr, errorFnr, errorDnr, - errorAgeValidator, + errorMinimumAgeValidator, }) const fnrValidator = useCallback( @@ -163,10 +168,13 @@ export function createMinimumAgeValidator(age: number) { } } - return new FormError('NationalIdentityNumber.errorAgeValidator', { - validationRule: 'errorAgeValidator', // "validationRule" Will be removed in future PR - messageValues: { age: String(age) }, - }) + return new FormError( + 'NationalIdentityNumber.errorMinimumAgeValidator', + { + validationRule: 'errorMinimumAgeValidator', // "validationRule" Will be removed in future PR + messageValues: { age: String(age) }, + } + ) } } diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx index 1a3d269d760..17f7605768d 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx @@ -8,8 +8,11 @@ import nbNO from '../../../constants/locales/nb-NO' const nb = nbNO['nb-NO'] describe('Field.NationalIdentityNumber with adultValidator', () => { - const errorAgeValidator = - nb.NationalIdentityNumber.errorAgeValidator.replace('{age}', '18') + const errorMinimumAgeValidator = + nb.NationalIdentityNumber.errorMinimumAgeValidator.replace( + '{age}', + '18' + ) const adultValidator = createMinimumAgeValidator(18) const extendingDnrAndFnrValidatorWithAdultValidator: Validator< string @@ -70,7 +73,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - errorAgeValidator + errorMinimumAgeValidator ) }) }) @@ -215,7 +218,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - errorAgeValidator + errorMinimumAgeValidator ) }) } @@ -253,7 +256,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - errorAgeValidator + errorMinimumAgeValidator ) }) } @@ -293,7 +296,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - errorAgeValidator + errorMinimumAgeValidator ) }) } @@ -375,7 +378,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - errorAgeValidator + errorMinimumAgeValidator ) }) } @@ -457,7 +460,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - errorAgeValidator + errorMinimumAgeValidator ) }) } @@ -515,7 +518,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - errorAgeValidator + errorMinimumAgeValidator ) }) } @@ -576,7 +579,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - errorAgeValidator + errorMinimumAgeValidator ) }) } @@ -634,7 +637,7 @@ describe('Field.NationalIdentityNumber with adultValidator', () => { await waitFor(() => { expect(screen.queryByRole('alert')).toBeInTheDocument() expect(screen.queryByRole('alert')).toHaveTextContent( - errorAgeValidator + errorMinimumAgeValidator ) }) } 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 1a577705ffc..f11dacd1276 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 @@ -120,7 +120,7 @@ export default { errorRequired: 'You must enter a national identity number.', errorFnr: 'Invalid national identity number.', errorDnr: 'Invalid D number.', - errorAgeValidator: 'Must be at least {age} years of age.', + errorMinimumAgeValidator: 'Must be at least {age} years of age.', }, OrganizationNumber: { label: 'Organisation number', 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 924432e7bf6..e37d5d82b2d 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 @@ -118,7 +118,7 @@ export default { errorRequired: 'Du må fylle inn et fødselsnummer.', errorFnr: 'Ugyldig fødselsnummer.', errorDnr: 'Ugyldig d-nummer.', - errorAgeValidator: 'Må være minst {age} år.', + errorMinimumAgeValidator: 'Må være minst {age} år.', }, OrganizationNumber: { label: 'Organisasjonsnummer', From f0f37309a1b8669b90b5787ff3fb55078728138e Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 11 Oct 2024 10:17:16 +0200 Subject: [PATCH 11/12] Update packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx --- .../forms/feature-fields/NationalIdentityNumber/properties.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx index faa37919def..5345828a1c7 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx @@ -37,7 +37,7 @@ You need to import the `createMinimumAgeValidator` function from the `Field.Nati import { createMinimumAgeValidator } from '@dnb/eufemia/extensions/forms/Field/NationalIdentityNumber' // Create a validator that validates if the value is above 18 years old -const above18Validator = createMinimumAgeValidator(18) +const above18YearsValidator = createMinimumAgeValidator(18) ``` See the following [example](/uilib/extensions/forms/feature-fields/NationalIdentityNumber/#extend-validation-with-custom-validation-function) on how to extend validation using the exposed validators. From b9c0f0a25aadd77c91d3eaa9b34d9b90992572e4 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 11 Oct 2024 10:17:23 +0200 Subject: [PATCH 12/12] Update packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx --- .../forms/feature-fields/NationalIdentityNumber/properties.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx index 5345828a1c7..4cd7b9dc2e4 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/NationalIdentityNumber/properties.mdx @@ -28,7 +28,7 @@ import { NationalIdentityNumberProperties } from '@dnb/eufemia/src/extensions/fo - validates the identification number as a national identity number (fødselsnummer) when first digit is 3 or less. You can create your own age validator by using the `createMinimumAgeValidator` function. It takes an age as a parameter and returns a validator function. The validator function takes a value and returns an error message if the value is not above the given age. -It validates if the identification number has a date of birth that is 18 years or older. It uses only the 7 first digits of an identification number to validate, first 6 digits representing the birth of date, and the next digit represents the century. +It validates if the identification number has a date of birth that is 18 years or older. It uses only the 7 first digits of the identification number to validate. The first 6 digits representing the birth of date, and the next digit represents the century. As it only use the 7 first digits, it does not validate the identification number, therefore it's quite common to use this validator together with one of the validators above (`dnrValidator`, `fnrValidator` or `dnrAndFnrValidator`) to validate the identification number as well. You need to import the `createMinimumAgeValidator` function from the `Field.NationalIdentityNumber` component: