diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/data-value-write-events.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/data-value-write-events.mdx index bbf36931b0e..adcbec6e393 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/data-value-write-events.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/data-value-write-events.mdx @@ -1,5 +1,5 @@ -| Event | Description | -| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `onChange` | _(optional)_ Will be called on value changes made by the user, with the new value as argument. | -| `onFocus` | _(optional)_ Will be called when the component gets into focus. Like clicking inside a text input or opening a dropdown. Called with active value as argument. | -| `onBlur` | _(optional)_ Will be called when the component stop being in focus. Like when going to next field, or closing a dropdown. Called with active value as argument. | +| Event | Description | +| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `onChange` | _(optional)_ Will be called on value changes made by the user, with the new value e.g. `+47 99999999`. The second parameter is an object: `{ countryCode, phoneNumber }`. | +| `onFocus` | _(optional)_ Will be called when the component gets into focus. Like clicking inside a text input or opening a dropdown. Called with active value as argument. | +| `onBlur` | _(optional)_ Will be called when the component stop being in focus. Like when going to next field, or closing a dropdown. Called with active value as argument. | diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/Examples.tsx index 46bbbf75597..95656ea94d1 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/Examples.tsx @@ -110,6 +110,19 @@ export const ValidationRequired = () => { ) } +export const ValidationPattern = () => { + return ( + + console.log('onChange', ...args)} + pattern="((?=\\+47)^\\+47 [49]\\d{7}$)|((?!\\+47)^\\+\\d{2} \\d{6})" + /> + + ) +} + export const WithFilter = () => { return ( diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/demos.mdx index 7f7176cd134..82ac57c1c52 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PhoneNumber/demos.mdx @@ -42,6 +42,12 @@ import * as Examples from './Examples' +### Validation - Pattern + +This pattern will strictly match Norwegian mobile numbers, which are defined as having a "+47" country code, followed by a number starting with 4 or 9, and exactly 7 more digits. If the country code is set to any other two-digit code, the pattern will match witch 6 digits after the country code. + + + diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/PhoneNumber.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/PhoneNumber.tsx index 7f55de307a4..37fbcb998d3 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/PhoneNumber.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/PhoneNumber.tsx @@ -6,7 +6,7 @@ import countries, { CountryType } from '../../constants/countries' import StringField, { Props as StringFieldProps } from '../String' import FieldBlock from '../../FieldBlock' import { useDataValue } from '../../hooks' -import { FieldHelpProps, FieldProps } from '../../types' +import { FieldHelpProps, FieldProps, JSONSchema } from '../../types' import { pickSpacingProps } from '../../../../components/flex/utils' import SharedContext from '../../../../shared/Context' import { @@ -103,12 +103,21 @@ function PhoneNumber(props: Props) { [] ) + const schema = useMemo( + () => + props.schema ?? { + type: 'string', + pattern: props.pattern, + }, + [props.schema, props.pattern] + ) const defaultProps: Partial = { + schema, errorMessages, } const preparedProps: Props = { - ...defaultProps, ...props, + ...defaultProps, validateRequired, fromExternal, toEvent, @@ -133,7 +142,6 @@ function PhoneNumber(props: Props) { disabled, width = 'large', help, - pattern, required, validateInitially, continuousValidation, @@ -349,7 +357,6 @@ function PhoneNumber(props: Props) { width={omitCountryCodeField ? 'medium' : 'stretch'} help={help} required={required} - pattern={pattern} errorMessages={errorMessages} validateInitially={validateInitially} continuousValidation={continuousValidation} diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/__tests__/PhoneNumber.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/__tests__/PhoneNumber.test.tsx index b8f39f321a9..2f617ed43c2 100644 --- a/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/__tests__/PhoneNumber.test.tsx +++ b/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/__tests__/PhoneNumber.test.tsx @@ -1,9 +1,9 @@ import React from 'react' import { wait, axeComponent } from '../../../../../core/jest/jestSetup' -import { fireEvent, render } from '@testing-library/react' +import { fireEvent, render, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { Provider } from '../../../../../shared' -import { Field, Form, JSONSchema } from '../../..' +import { Field, Form, FormError, JSONSchema } from '../../..' describe('Field.PhoneNumber', () => { it('should default to 47', () => { @@ -481,10 +481,10 @@ describe('Field.PhoneNumber', () => { ).not.toBeInTheDocument() }) - it('should handle "pattern" property', async () => { + it('should handle simple "pattern" property', async () => { render( - + ) @@ -497,7 +497,7 @@ describe('Field.PhoneNumber', () => { expect(document.querySelector('[role="alert"]')).toBeInTheDocument() expect(document.querySelector('[role="alert"]').textContent).toContain( - 'valid number' + 'You must enter a valid number' ) await userEvent.type(numberElement, '{Backspace>8}89') @@ -522,6 +522,64 @@ describe('Field.PhoneNumber', () => { ).not.toBeInTheDocument() }) + it('should handle "pattern" property with country code', () => { + const props = { + validateInitially: true, + pattern: + '((?=\\+47)^\\+47 [49]\\d{7}$)|((?!\\+47)^\\+\\d{2} \\d{6})', + } + const { rerender } = render( + + ) + + expect( + document.querySelector('[role="alert"]') + ).not.toBeInTheDocument() + + rerender() + + expect(document.querySelector('[role="alert"]')).toBeInTheDocument() + + rerender() + + expect( + document.querySelector('[role="alert"]') + ).not.toBeInTheDocument() + + rerender() + + expect(document.querySelector('[role="alert"]')).toBeInTheDocument() + }) + + it('should handle "validator" property with country code', async () => { + const validator = jest.fn(() => { + return new FormError('some error') + }) + + render( + + + + ) + + expect(validator).toHaveBeenCalledTimes(1) + expect(validator).toHaveBeenCalledWith('+41 9999', { + pattern: 'You must enter a valid number', + required: 'You must enter a valid number', + }) + + await waitFor(() => { + expect(document.querySelector('[role="alert"]')).toBeInTheDocument() + expect( + document.querySelector('[role="alert"]').textContent + ).toContain('some error') + }) + }) + it('should filter countries list with given filterCountries', () => { render( { - // update('+41 1') - update('+45') - }, []) + const { update } = Form.useData('uniqueId') + + React.useLayoutEffect(() => { + update('/phone', () => '+41 123') + }, [update]) + return ( - { - console.log('onChange', value) - update(value) - }} - /> + + + { + console.log('onChange', value) + update('/phone', () => value) + }} + /> + + + + + + ) }