Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: usePhoneInput result not in national format (#84) #85

Merged
merged 1 commit into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"clean": "rimraf .turbo && rimraf node_modules && rimraf .next"
},
"dependencies": {
"@react-awesome/components": "1.0.14",
"@react-awesome/components": "1.0.15",
"classnames": "^2.5.1",
"lodash": "^4.17.21",
"lucide-react": "^0.315.0",
Expand Down
173 changes: 165 additions & 8 deletions apps/docs/src/pages/docs/phone-input/use-phone-input.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ npm i @react-awesome/phone-input
import { useState } from 'react'
import { Container } from '../../../components/Container'
import { usePhoneInput } from '@react-awesome/components'
import { Callout } from 'nextra/components'

export const Example = () => {
const [value, setValue] = useState({
Expand Down Expand Up @@ -83,13 +84,19 @@ const Example = () => {
}
```

## Local Phone Input
## Phone Input With National Format

By default **usePhoneInput** has `mode` is set to `international`.

When `mode` is `international` the value will be formatted as `e164`.

When `mode` is `national` the country code and the `+` sign will be ignored. Value is formatted as national format of the current selected country which provided via `country` property.
When `mode` is `national` the country code and the `+` sign will be ignored. Value is formatted as national format of the current selected country.

<Callout type="info" emoji="ℹ️">
Even though the phone number is formatted follow the current selected country
but phone input also automatically change the country when user paste a value
include a different country code.
</Callout>

export const LocalExample = () => {
const [value, setValue] = useState({
Expand All @@ -101,21 +108,23 @@ export const LocalExample = () => {
formattedValue: '',
isSupported: true,
});
const { register } = usePhoneInput({
const { register, phoneCode } = usePhoneInput({
onChange: (_, m) => {
setValue(m);
},
mode: 'local',
country: value.country
mode: 'national',
defaultCountry: 'VN'
});

return (

<div>
<div className="relative">
<div className="absolute left-2 top-1/2 -translate-y-1/2 z-10">+84</div>
<div className="absolute left-2 top-1/2 -translate-y-1/2 z-10">
+{phoneCode}
</div>
<input
className="w-full border rounded-md pl-10 pr-3 py-2"
className="w-full border rounded-md pl-12 pr-3 py-2"
placeholder="I am a local phone input"
{...register('use-phone-input')}
/>
Expand Down Expand Up @@ -149,7 +158,6 @@ import { usePhoneInput } from '@react-awesome/phone-input'
const Example = () => {
const { register } = usePhoneInput({
mode: 'national',
country: 'VN',
})

return (
Expand All @@ -161,6 +169,155 @@ const Example = () => {
}
```

## Phone Input With Fixed Country

**usePhoneInput** also accepts `country` prop.

When `country` is provided then the enterred value is formatted based on the provided country code and the country detection behaviour will be disabled.

export const FixedCountryExample = () => {
const [value, setValue] = useState({
isPossible: false,
isValid: false,
e164Value: '',
country: 'VN',
phoneCode: '84',
formattedValue: '',
isSupported: true,
});
const { register, phoneCode } = usePhoneInput({
onChange: (_, m) => {
setValue(m);
},
country: 'VN',
defaultCountry: 'VN'
});

return (

<div>
<div className="relative">
<input
className="w-full border rounded-md pl-3 pr-3 py-2"
placeholder="I only accept Vietnamese phone number"
{...register('use-phone-input')}
/>
</div>
<h3 className="mt-3 font-bold text-xl underline underline-offset-4">
onChange event
</h3>
<ul className="mt-2">
{Object.keys(value).map((key) => {
const v = value[key]
return (
<li key={key}>
<span className="font-medium">👉 {key}</span>
<code className="ml-2 nx-border-black nx-border-opacity-[0.04] nx-bg-opacity-[0.03] nx-bg-black nx-break-words nx-rounded-md nx-border nx-py-0.5 nx-px-[.25em] nx-text-[.9em] dark:nx-border-white/10 dark:nx-bg-white/10">
{v.toString()}
</code>
</li>
)
})}
</ul>
</div>
); };

<Container>
<FixedCountryExample />
</Container>

```jsx
import { usePhoneInput } from '@react-awesome/phone-input'

const Example = () => {
const { register } = usePhoneInput({
country: 'VN',
})

return (
<input
placeholder="I am a fixed country phone input"
{...register('use-phone-input')}
/>
)
}
```

`country` can also work with national format.

export const FixedCountryWithNationalExample = () => {
const [value, setValue] = useState({
isPossible: false,
isValid: false,
e164Value: '',
country: 'VN',
phoneCode: '84',
formattedValue: '',
isSupported: true,
});
const { register, phoneCode } = usePhoneInput({
onChange: (_, m) => {
setValue(m);
},
country: 'VN',
defaultCountry: 'VN',
mode: 'national'
});

return (

<div>
<div className="relative">
<div className="absolute left-2 top-1/2 -translate-y-1/2 z-10">
+{phoneCode}
</div>
<input
className="w-full border rounded-md pl-12 pr-3 py-2"
placeholder="I only accept Vietnamese phone number with national format"
{...register('use-phone-input')}
/>
</div>
<h3 className="mt-3 font-bold text-xl underline underline-offset-4">
onChange event
</h3>
<ul className="mt-2">
{Object.keys(value).map((key) => {
const v = value[key]
return (
<li key={key}>
<span className="font-medium">👉 {key}</span>
<code className="ml-2 nx-border-black nx-border-opacity-[0.04] nx-bg-opacity-[0.03] nx-bg-black nx-break-words nx-rounded-md nx-border nx-py-0.5 nx-px-[.25em] nx-text-[.9em] dark:nx-border-white/10 dark:nx-bg-white/10">
{v.toString()}
</code>
</li>
)
})}
</ul>
</div>
); };

<Container>
<FixedCountryWithNationalExample />
</Container>

```jsx
import { usePhoneInput } from '@react-awesome/phone-input'

const Example = () => {
const { register } = usePhoneInput({
country: 'VN',
mode: 'national',
})

return (
<input
placeholder="I only accept Vietnamese phone number with national format"
{...register('use-phone-input')}
/>
)
}
```

## Parameters

The `usePhoneInput` takes the following parameters:
Expand Down
7 changes: 7 additions & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# @react-awesome/components

## 1.0.15

### Patch Changes

- Updated dependencies
- @react-awesome/phone-input@1.1.3

## 1.0.14

### Patch Changes
Expand Down
4 changes: 2 additions & 2 deletions packages/components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@react-awesome/components",
"version": "1.0.14",
"version": "1.0.15",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
Expand Down Expand Up @@ -39,7 +39,7 @@
"access": "public"
},
"dependencies": {
"@react-awesome/phone-input": "1.1.2",
"@react-awesome/phone-input": "1.1.3",
"@react-awesome/use-click-outside": "0.0.3",
"@react-awesome/use-preserve-input-caret-position": "0.0.3",
"@react-awesome/use-selection-range": "0.0.3",
Expand Down
6 changes: 6 additions & 0 deletions packages/phone-input/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @react-awesome/phone-input

## 1.1.3

### Patch Changes

- Fix phone number is not in national format

## 1.1.2

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/phone-input/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@react-awesome/phone-input",
"version": "1.1.2",
"version": "1.1.3",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { formatWithFixedCountry } from './formatWithFixedCountry'

describe('formatWithFixedCountry', () => {
it('Should leave as-is when the value is already has valid country code.', () => {
expect(formatWithFixedCountry('+123456', 'US')).toBe('+123456')
})

it('Should format to the correct country code.', () => {
expect(formatWithFixedCountry('+123456', 'VN')).toBe('+84123456')
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {
CountryCode,
getCountryCallingCode,
parseIncompletePhoneNumber,
} from 'libphonenumber-js'

export const formatWithFixedCountry = (
phoneValue: string,
country: CountryCode,
) => {
if (!phoneValue) return ''

const prefix = `+${getCountryCallingCode(country)}`

if (phoneValue.startsWith(prefix))
return parseIncompletePhoneNumber(phoneValue)

if (phoneValue.startsWith('+'))
return `${prefix}${parseIncompletePhoneNumber(phoneValue.replace(/\+/g, ''))}`

if (phoneValue.startsWith('0'))
return `${prefix}${parseIncompletePhoneNumber(phoneValue.slice(1))}`

return `${prefix}${parseIncompletePhoneNumber(phoneValue)}`
}
1 change: 1 addition & 0 deletions packages/phone-input/src/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './formatInternational/formatInternational'
export * from './getPossibleCountriesByCallingCode/getPossibleCountriesByCallingCode'
export * from './checkCountryValidity/checkCountryValidity'
export * from './formatNational/formatNational'
export * from './formatWithFixedCountry/formatWithFixedCountry'
4 changes: 3 additions & 1 deletion packages/phone-input/src/hooks/usePhoneInput.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,9 @@ describe('usePhoneInput', () => {

it('Should only trigger change event when value is actually changed', async () => {
const onChange = vitest.fn()
const { container } = render(<Comp country="VN" onChange={onChange} />)
const { container } = render(
<Comp defaultCountry="VN" onChange={onChange} />,
)
const input = container.querySelector('input')

if (!input) {
Expand Down
Loading
Loading