🚸 (phone) Improve phone input behavior and validation
Now accepting landline phone numbers. Consistent select UI on every browser. Auto focus on country select.
This commit is contained in:
@ -37,7 +37,7 @@ test.describe('Phone input block', () => {
|
|||||||
|
|
||||||
await page.click('text=Restart')
|
await page.click('text=Restart')
|
||||||
await page.locator(`input[placeholder="+33 XX XX XX XX"]`).type('+33 6 73')
|
await page.locator(`input[placeholder="+33 XX XX XX XX"]`).type('+33 6 73')
|
||||||
await expect(page.getByRole('combobox')).toHaveText(/🇫🇷.+/)
|
await expect(page.getByText('🇫🇷')).toBeVisible()
|
||||||
await page.locator('button >> text="Go"').click()
|
await page.locator('button >> text="Go"').click()
|
||||||
await expect(page.locator('text=Try again bro')).toBeVisible()
|
await expect(page.locator('text=Try again bro')).toBeVisible()
|
||||||
await page
|
await page
|
||||||
|
@ -23,11 +23,11 @@
|
|||||||
"db": "workspace:*",
|
"db": "workspace:*",
|
||||||
"google-spreadsheet": "3.3.0",
|
"google-spreadsheet": "3.3.0",
|
||||||
"got": "12.5.3",
|
"got": "12.5.3",
|
||||||
|
"libphonenumber-js": "^1.10.21",
|
||||||
"next": "13.1.6",
|
"next": "13.1.6",
|
||||||
"nextjs-cors": "^2.1.2",
|
"nextjs-cors": "^2.1.2",
|
||||||
"nodemailer": "6.9.1",
|
"nodemailer": "6.9.1",
|
||||||
"openai": "^3.2.1",
|
"openai": "^3.2.1",
|
||||||
"phone": "^3.1.34",
|
|
||||||
"qs": "6.11.0",
|
"qs": "6.11.0",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import phone from 'phone'
|
import { parsePhoneNumber } from 'libphonenumber-js'
|
||||||
|
|
||||||
export const formatPhoneNumber = (phoneNumber: string) =>
|
export const formatPhoneNumber = (phoneNumber: string) =>
|
||||||
phone(phoneNumber).phoneNumber
|
parsePhoneNumber(phoneNumber).formatInternational()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { phone } from 'phone'
|
import { isValidPhoneNumber } from 'libphonenumber-js'
|
||||||
|
|
||||||
export const validatePhoneNumber = (phoneNumber: string) =>
|
export const validatePhoneNumber = (phoneNumber: string) =>
|
||||||
phone(phoneNumber).isValid
|
isValidPhoneNumber(phoneNumber)
|
||||||
|
@ -50,13 +50,13 @@ export const continueBotFlow =
|
|||||||
message: 'Current block is not an input block',
|
message: 'Current block is not an input block',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (reply && !isReplyValid(reply, block)) return parseRetryMessage(block)
|
||||||
|
|
||||||
const formattedReply = formatReply(reply, block.type)
|
const formattedReply = formatReply(reply, block.type)
|
||||||
|
|
||||||
if (!formattedReply && !canSkip(block.type)) {
|
if (!formattedReply && !canSkip(block.type)) {
|
||||||
return parseRetryMessage(block)
|
return parseRetryMessage(block)
|
||||||
}
|
}
|
||||||
if (formattedReply && !isReplyValid(formattedReply, block))
|
|
||||||
return parseRetryMessage(block)
|
|
||||||
|
|
||||||
const newSessionState = await processAndSaveAnswer(
|
const newSessionState = await processAndSaveAnswer(
|
||||||
state,
|
state,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@typebot.io/js",
|
"name": "@typebot.io/js",
|
||||||
"version": "0.0.21",
|
"version": "0.0.22",
|
||||||
"description": "Javascript library to display typebots on your website",
|
"description": "Javascript library to display typebots on your website",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
16
packages/js/src/components/icons/ChevronDownIcon.tsx
Normal file
16
packages/js/src/components/icons/ChevronDownIcon.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { JSX } from 'solid-js/jsx-runtime'
|
||||||
|
|
||||||
|
export const ChevronDownIcon = (props: JSX.SvgSVGAttributes<SVGSVGElement>) => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2px"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<polyline points="6 9 12 15 18 9" />
|
||||||
|
</svg>
|
||||||
|
)
|
@ -1,4 +1,5 @@
|
|||||||
import { ShortTextInput } from '@/components'
|
import { ShortTextInput } from '@/components'
|
||||||
|
import { ChevronDownIcon } from '@/components/icons/ChevronDownIcon'
|
||||||
import { SendButton } from '@/components/SendButton'
|
import { SendButton } from '@/components/SendButton'
|
||||||
import { InputSubmitContent } from '@/types'
|
import { InputSubmitContent } from '@/types'
|
||||||
import { isMobile } from '@/utils/isMobileSignal'
|
import { isMobile } from '@/utils/isMobileSignal'
|
||||||
@ -75,7 +76,13 @@ export const PhoneInput = (props: PhoneInputProps) => {
|
|||||||
const selectNewCountryCode = (
|
const selectNewCountryCode = (
|
||||||
event: Event & { currentTarget: { value: string } }
|
event: Event & { currentTarget: { value: string } }
|
||||||
) => {
|
) => {
|
||||||
setSelectedCountryCode(event.currentTarget.value)
|
const code = event.currentTarget.value
|
||||||
|
setSelectedCountryCode(code)
|
||||||
|
const dial_code = phoneCountries.find(
|
||||||
|
(country) => country.code === code
|
||||||
|
)?.dial_code
|
||||||
|
if (inputValue() === '' && dial_code) setInputValue(dial_code)
|
||||||
|
inputRef?.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
@ -91,30 +98,37 @@ export const PhoneInput = (props: PhoneInputProps) => {
|
|||||||
}}
|
}}
|
||||||
onKeyDown={submitWhenEnter}
|
onKeyDown={submitWhenEnter}
|
||||||
>
|
>
|
||||||
<div class="flex flex-1">
|
<div class="flex">
|
||||||
<select
|
<div class="relative typebot-country-select flex justify-center items-center rounded-md">
|
||||||
onChange={selectNewCountryCode}
|
<div class="pl-2 pr-1 flex items-center gap-2">
|
||||||
class="w-12 pl-2 focus:outline-none rounded-lg typebot-country-select"
|
<span>
|
||||||
>
|
|
||||||
<option selected>
|
|
||||||
{
|
{
|
||||||
phoneCountries.find(
|
phoneCountries.find(
|
||||||
(country) => selectedCountryCode() === country.code
|
(country) => selectedCountryCode() === country.code
|
||||||
)?.flag
|
)?.flag
|
||||||
}
|
}
|
||||||
</option>
|
</span>
|
||||||
<For
|
<ChevronDownIcon class="w-3" />
|
||||||
each={phoneCountries.filter(
|
</div>
|
||||||
(country) => country.code !== selectedCountryCode()
|
|
||||||
)}
|
<select
|
||||||
|
onChange={selectNewCountryCode}
|
||||||
|
class="absolute top-0 left-0 w-full h-full cursor-pointer opacity-0"
|
||||||
>
|
>
|
||||||
|
<For each={phoneCountries}>
|
||||||
{(country) => (
|
{(country) => (
|
||||||
<option value={country.code}>
|
<option
|
||||||
{country.name} ({country.dial_code})
|
value={country.code}
|
||||||
|
selected={country.code === selectedCountryCode()}
|
||||||
|
>
|
||||||
|
{country.name}{' '}
|
||||||
|
{country.dial_code ? `(${country.dial_code})` : ''}
|
||||||
</option>
|
</option>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
</select>
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ShortTextInput
|
<ShortTextInput
|
||||||
type="tel"
|
type="tel"
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@typebot.io/react",
|
"name": "@typebot.io/react",
|
||||||
"version": "0.0.21",
|
"version": "0.0.22",
|
||||||
"description": "React library to display typebots on your website",
|
"description": "React library to display typebots on your website",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
13
pnpm-lock.yaml
generated
13
pnpm-lock.yaml
generated
@ -370,6 +370,7 @@ importers:
|
|||||||
google-auth-library: 8.7.0
|
google-auth-library: 8.7.0
|
||||||
google-spreadsheet: 3.3.0
|
google-spreadsheet: 3.3.0
|
||||||
got: 12.5.3
|
got: 12.5.3
|
||||||
|
libphonenumber-js: ^1.10.21
|
||||||
models: workspace:*
|
models: workspace:*
|
||||||
next: 13.1.6
|
next: 13.1.6
|
||||||
next-transpile-modules: 10.0.0
|
next-transpile-modules: 10.0.0
|
||||||
@ -378,7 +379,6 @@ importers:
|
|||||||
nodemailer: 6.9.1
|
nodemailer: 6.9.1
|
||||||
openai: ^3.2.1
|
openai: ^3.2.1
|
||||||
papaparse: 5.3.2
|
papaparse: 5.3.2
|
||||||
phone: ^3.1.34
|
|
||||||
qs: 6.11.0
|
qs: 6.11.0
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0
|
react-dom: 18.2.0
|
||||||
@ -400,11 +400,11 @@ importers:
|
|||||||
db: link:../../packages/db
|
db: link:../../packages/db
|
||||||
google-spreadsheet: 3.3.0
|
google-spreadsheet: 3.3.0
|
||||||
got: 12.5.3
|
got: 12.5.3
|
||||||
|
libphonenumber-js: 1.10.21
|
||||||
next: 13.1.6_6m24vuloj5ihw4zc5lbsktc4fu
|
next: 13.1.6_6m24vuloj5ihw4zc5lbsktc4fu
|
||||||
nextjs-cors: 2.1.2_next@13.1.6
|
nextjs-cors: 2.1.2_next@13.1.6
|
||||||
nodemailer: 6.9.1
|
nodemailer: 6.9.1
|
||||||
openai: 3.2.1
|
openai: 3.2.1
|
||||||
phone: 3.1.34
|
|
||||||
qs: 6.11.0
|
qs: 6.11.0
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0_react@18.2.0
|
react-dom: 18.2.0_react@18.2.0
|
||||||
@ -14284,6 +14284,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-MDZ1zLIkfSDZV5xBta3nuvbEOlsnKCPe4z5r3hyup/AXveevkl9A1eSWmLhd2FX4k7pJDe4MrLeQsux0HI/VWg==}
|
resolution: {integrity: sha512-MDZ1zLIkfSDZV5xBta3nuvbEOlsnKCPe4z5r3hyup/AXveevkl9A1eSWmLhd2FX4k7pJDe4MrLeQsux0HI/VWg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/libphonenumber-js/1.10.21:
|
||||||
|
resolution: {integrity: sha512-/udZhx49av2r2gZR/+xXSrwcR8smX/sDNrVpOFrvW+CA26TfYTVZfwb3MIDvmwAYMLs7pXuJjZX0VxxGpqPhsA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/lie/3.1.1:
|
/lie/3.1.1:
|
||||||
resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==}
|
resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -15983,11 +15987,6 @@ packages:
|
|||||||
sha.js: 2.4.11
|
sha.js: 2.4.11
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/phone/3.1.34:
|
|
||||||
resolution: {integrity: sha512-h+nJqLyzA4vbPlD9poMS6fqeW4Dz1lRNPK9qHmrM7Pqkac/0RdiFZrbSJTVxE5xj/HFAaggQTZyfj6XmSjootA==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/php-parser/3.1.3:
|
/php-parser/3.1.3:
|
||||||
resolution: {integrity: sha512-hPvBmnRYPqWEtMfIFOlyjQv1q75UUtxt4U+YscKIQViGmEE2Xa4BuS1B1/cZdjy7MVcwtnr0WkEsr915LgRKOw==}
|
resolution: {integrity: sha512-hPvBmnRYPqWEtMfIFOlyjQv1q75UUtxt4U+YscKIQViGmEE2Xa4BuS1B1/cZdjy7MVcwtnr0WkEsr915LgRKOw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
Reference in New Issue
Block a user