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..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,4 +1,5 @@
import ComponentBox from '../../../../../../shared/tags/ComponentBox'
+import { createMinimumAgeValidator } from '@dnb/eufemia/src/extensions/forms/Field/NationalIdentityNumber'
import { Field } from '@dnb/eufemia/src/extensions/forms'
export const Empty = () => {
@@ -188,3 +189,51 @@ export const ValidationExtendValidator = () => {
)
}
+
+export const ValidationExtendValidatorAdult = () => {
+ return (
+
+ {() => {
+ const adultValidator = createMinimumAgeValidator(18)
+ const myAdultValidator = (value, { validators }) => {
+ const { dnrAndFnrValidator } = validators
+
+ return [dnrAndFnrValidator, adultValidator]
+ }
+
+ return (
+
+ )
+ }}
+
+ )
+}
+
+export const ValidationFnrAdult = () => {
+ return (
+
+ {() => {
+ const adultValidator = createMinimumAgeValidator(18)
+ const myFnrAdultValidator = (value, { validators }) => {
+ const { 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 2d20256438d..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
@@ -54,18 +54,28 @@ 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 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 your own age validator, by using the `createMinimumAgeValidator` function.
+
+
+
+### 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 694c8830f82..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
@@ -21,12 +21,25 @@ 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.
+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 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:
+
+```tsx
+import { createMinimumAgeValidator } from '@dnb/eufemia/extensions/forms/Field/NationalIdentityNumber'
+
+// Create a validator that validates if the value is above 18 years old
+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.
## 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 5e3420b525f..277f1f3419f 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,18 +14,28 @@ export type Props = Omit & {
function NationalIdentityNumber(props: Props) {
const translations = useTranslation().NationalIdentityNumber
- const { label, errorRequired, errorFnr, errorDnr } = translations
+ const {
+ label,
+ errorRequired,
+ errorFnr,
+ errorDnr,
+ errorMinimumAgeValidator,
+ } = translations
const errorMessages = useErrorMessage(props.path, props.errorMessages, {
required: errorRequired,
pattern: errorFnr,
errorFnr,
errorDnr,
+ errorMinimumAgeValidator,
})
const fnrValidator = useCallback(
(value: string) => {
- // have to check for undefined as @navikt/fnrvalidator does not support 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)
}
},
@@ -34,8 +44,11 @@ function NationalIdentityNumber(props: Props) {
const dnrValidator = useCallback(
(value: string) => {
- // have to check for undefined as @navikt/fnrvalidator does not support 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)
}
},
@@ -106,5 +119,64 @@ function NationalIdentityNumber(props: Props) {
return
}
+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()
+
+ if (month < 0 || (month === 0 && day < 0)) {
+ return age - 1
+ }
+
+ return age
+}
+
+export function getBirthDateByFnrOrDnr(value: string) {
+ if (value === undefined) {
+ return // stop here
+ }
+
+ const yearPart = value.substring(4, 6)
+ const centuryNumber = Number.parseInt(value.substring(6, 7))
+
+ const isBornIn20XX = centuryNumber >= 5
+ const year = isBornIn20XX ? `20${yearPart}` : `19${yearPart}`
+ const month = Number.parseInt(value.substring(2, 4))
+
+ 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
+ : Number.parseInt(value.substring(0, 2))
+
+ return new Date(Number.parseInt(year), month - 1, day)
+}
+
+export function createMinimumAgeValidator(age: number) {
+ return (value: string) => {
+ if (typeof value !== 'string') {
+ return // stop here
+ }
+
+ if (value.length > 6) {
+ const date = getBirthDateByFnrOrDnr(value)
+ if (getAgeByBirthDate(date) >= age) {
+ return // stop here
+ }
+ }
+
+ return new FormError(
+ 'NationalIdentityNumber.errorMinimumAgeValidator',
+ {
+ validationRule: 'errorMinimumAgeValidator', // "validationRule" Will be removed in future PR
+ messageValues: { age: String(age) },
+ }
+ )
+ }
+}
+
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..17f7605768d
--- /dev/null
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/NationalIdentityNumber/__tests__/NationalIdentityNumberAdultValidator.test.tsx
@@ -0,0 +1,668 @@
+import React from 'react'
+import { render, waitFor, screen } from '@testing-library/react'
+import { Field, Validator } from '../../..'
+import { createMinimumAgeValidator } from '../NationalIdentityNumber'
+
+import nbNO from '../../../constants/locales/nb-NO'
+
+const nb = nbNO['nb-NO']
+
+describe('Field.NationalIdentityNumber with adultValidator', () => {
+ const errorMinimumAgeValidator =
+ nb.NationalIdentityNumber.errorMinimumAgeValidator.replace(
+ '{age}',
+ '18'
+ )
+ const adultValidator = createMinimumAgeValidator(18)
+ const extendingDnrAndFnrValidatorWithAdultValidator: Validator<
+ string
+ > = (value, { validators }) => {
+ const { dnrAndFnrValidator } = validators
+
+ return [dnrAndFnrValidator, adultValidator]
+ }
+
+ const extendingDnrValidatorWithAdultValidator: Validator = (
+ value,
+ { validators }
+ ) => {
+ const { dnrValidator } = validators
+
+ return [dnrValidator, adultValidator]
+ }
+
+ const extendingFnrValidatorWithAdultValidator: Validator = (
+ value,
+ { validators }
+ ) => {
+ const { fnrValidator } = validators
+
+ return [fnrValidator, adultValidator]
+ }
+
+ const myAdultValidator: Validator = () => {
+ 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 value is invalid', async () => {
+ render(
+
+ )
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).toBeInTheDocument()
+ expect(screen.queryByRole('alert')).toHaveTextContent(
+ errorMinimumAgeValidator
+ )
+ })
+ })
+
+ 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',
+ ]
+ 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(
+ errorMinimumAgeValidator
+ )
+ })
+ }
+ )
+ })
+
+ 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(
+ errorMinimumAgeValidator
+ )
+ })
+ }
+ )
+ })
+
+ 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(
+ errorMinimumAgeValidator
+ )
+ })
+ }
+ )
+
+ 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(
+ errorMinimumAgeValidator
+ )
+ })
+ }
+ )
+
+ 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 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(
+ errorMinimumAgeValidator
+ )
+ })
+ }
+ )
+
+ 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(
+ errorMinimumAgeValidator
+ )
+ })
+ }
+ )
+
+ 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(
+ errorMinimumAgeValidator
+ )
+ })
+ }
+ )
+
+ 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(
+ errorMinimumAgeValidator
+ )
+ })
+ }
+ )
+
+ 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
+ )
+ })
+ })
+ })
+ })
+})
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..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,16 +1,758 @@
import React from 'react'
-import { Field } from '../../..'
+import { Field, Validator } from '../../..'
+import { Wrapper } from 'storybook-utils/helpers'
+import { createMinimumAgeValidator } from '../NationalIdentityNumber'
export default {
title: 'Eufemia/Extensions/Forms/NationalIdentityNumber',
}
-export function NationalIdentityNumber() {
+const simpleValidator = (value) => {
+ return value?.length < 4 ? Error('At least 4 characters') : undefined
+}
+
+const adultValidator = createMinimumAgeValidator(18)
+
+const myAdultValidator: Validator = () => {
+ return [adultValidator]
+}
+
+const myAdultFnrDnrValidator: Validator = (
+ value,
+ { validators }
+) => {
+ const { 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..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,6 +120,7 @@ export default {
errorRequired: 'You must enter a national identity number.',
errorFnr: 'Invalid national identity number.',
errorDnr: 'Invalid D number.',
+ 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 309b6f1266b..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,6 +118,7 @@ export default {
errorRequired: 'Du må fylle inn et fødselsnummer.',
errorFnr: 'Ugyldig fødselsnummer.',
errorDnr: 'Ugyldig d-nummer.',
+ errorMinimumAgeValidator: 'Må være minst {age} år.',
},
OrganizationNumber: {
label: 'Organisasjonsnummer',