Skip to content

Commit

Permalink
fix(PhoneNumber): handle pattern, schema and validator with country c…
Browse files Browse the repository at this point in the history
…ode (#3249)
  • Loading branch information
tujoworker authored Jan 22, 2024
1 parent bc05141 commit ed115d5
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -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. |
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,19 @@ export const ValidationRequired = () => {
)
}

export const ValidationPattern = () => {
return (
<ComponentBox>
<Field.PhoneNumber
value="+47 12345678"
label="Label text"
onChange={(...args) => console.log('onChange', ...args)}
pattern="((?=\\+47)^\\+47 [49]\\d{7}$)|((?!\\+47)^\\+\\d{2} \\d{6})"
/>
</ComponentBox>
)
}

export const WithFilter = () => {
return (
<ComponentBox>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ import * as Examples from './Examples'

<Examples.ValidationRequired />

### 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.

<Examples.ValidationPattern />

<VisibleWhenVisualTest>
<Examples.LongLabel />
</VisibleWhenVisualTest>
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -103,12 +103,21 @@ function PhoneNumber(props: Props) {
[]
)

const schema = useMemo<JSONSchema>(
() =>
props.schema ?? {
type: 'string',
pattern: props.pattern,
},
[props.schema, props.pattern]
)
const defaultProps: Partial<Props> = {
schema,
errorMessages,
}
const preparedProps: Props = {
...defaultProps,
...props,
...defaultProps,
validateRequired,
fromExternal,
toEvent,
Expand All @@ -133,7 +142,6 @@ function PhoneNumber(props: Props) {
disabled,
width = 'large',
help,
pattern,
required,
validateInitially,
continuousValidation,
Expand Down Expand Up @@ -349,7 +357,6 @@ function PhoneNumber(props: Props) {
width={omitCountryCodeField ? 'medium' : 'stretch'}
help={help}
required={required}
pattern={pattern}
errorMessages={errorMessages}
validateInitially={validateInitially}
continuousValidation={continuousValidation}
Expand Down
Original file line number Diff line number Diff line change
@@ -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', () => {
Expand Down Expand Up @@ -481,10 +481,10 @@ describe('Field.PhoneNumber', () => {
).not.toBeInTheDocument()
})

it('should handle "pattern" property', async () => {
it('should handle simple "pattern" property', async () => {
render(
<Provider locale="en-GB">
<Field.PhoneNumber required pattern="^[49]+" />
<Field.PhoneNumber pattern="^\+47 [49]+" />
</Provider>
)

Expand All @@ -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')
Expand All @@ -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(
<Field.PhoneNumber value="+47 99999999" {...props} />
)

expect(
document.querySelector('[role="alert"]')
).not.toBeInTheDocument()

rerender(<Field.PhoneNumber value="+47 9999" {...props} />)

expect(document.querySelector('[role="alert"]')).toBeInTheDocument()

rerender(<Field.PhoneNumber value="+41 999999" {...props} />)

expect(
document.querySelector('[role="alert"]')
).not.toBeInTheDocument()

rerender(<Field.PhoneNumber value="+41 9999" {...props} />)

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(
<Provider locale="en-GB">
<Field.PhoneNumber
validator={validator}
validateInitially
value="+41 9999"
/>
</Provider>
)

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(
<Field.PhoneNumber
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,41 @@
import React from 'react'
import { Field } from '../../..'
import { Field, Form } from '../../..'
import { Flex } from '../../../../../components'

export default {
title: 'Eufemia/Extensions/Forms/PhoneNumber',
}

const initialData = { phone: '+47 423456789' }

export function PhoneNumber() {
const [state, update] = React.useState('+47 1')
// const [state, update] = React.useState(undefined)
React.useEffect(() => {
// update('+41 1')
update('+45')
}, [])
const { update } = Form.useData('uniqueId')

React.useLayoutEffect(() => {
update('/phone', () => '+41 123')
}, [update])

return (
<Field.PhoneNumber
required
pattern="^[49]+"
value={state}
onBlur={console.log}
onFocus={console.log}
onChange={(value) => {
console.log('onChange', value)
update(value)
}}
/>
<Form.Handler id="uniqueId" data={initialData}>
<Flex.Stack>
<Field.PhoneNumber
pattern="((?=\+47)^\+47 [49]\d{7}$)|((?!\+47)^\+\d{2} \d{6})"
// pattern="^\+47 [49]+"
// required
// validateInitially
path="/phone"
onBlur={console.log}
onFocus={console.log}
onChange={(value) => {
console.log('onChange', value)
update('/phone', () => value)
}}
/>
<Field.String label="Shadow" path="/phone" />
<Form.ButtonRow>
<Form.SubmitButton />
</Form.ButtonRow>
</Flex.Stack>
</Form.Handler>
)
}

0 comments on commit ed115d5

Please sign in to comment.