fix: add phone mask overrides for Argentina and Finland to prevent digit truncation (#28203)
* fix: add phone mask overrides for Argentina and Finland to prevent digit truncation Co-Authored-By: susan <susan@cal.com> * refactor: extract CUSTOM_PHONE_MASKS to shared module to avoid duplication Address review feedback from cubic-dev-ai: extract CUSTOM_PHONE_MASKS into a shared phone-masks.ts module so both PhoneInput.tsx and the test file import from the same source of truth instead of duplicating the masks. Also fix non-null assertion lint warning in the test file. Original PR by devin-ai-integration[bot]. Co-Authored-By: bot_apk <apk@cognition.ai> * fix: move custom message to expect() for toBeGreaterThanOrEqual type compatibility toBeGreaterThanOrEqual only accepts 1 argument. Move the custom error message to the expect() call instead. Co-Authored-By: bot_apk <apk@cognition.ai> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: susan <susan@cal.com> Co-authored-by: bot_apk <apk@cognition.ai> Co-authored-by: Eunjae Lee <hey@eunjae.dev> Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
This commit is contained in:
co-authored by
bot_apk
Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
susan
Eunjae Lee
Udit Takkar
parent
1b21ead011
commit
2bf45677e8
@@ -0,0 +1,53 @@
|
||||
import { isValidPhoneNumber, parsePhoneNumberFromString } from "libphonenumber-js/max";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { CUSTOM_PHONE_MASKS } from "./phone-masks";
|
||||
|
||||
/** Count the digit placeholders (dots) in a react-phone-input-2 mask */
|
||||
const countMaskDigits = (mask: string): number => (mask.match(/\./g) || []).length;
|
||||
|
||||
/**
|
||||
* Test phone numbers that must be accepted for each country with a custom mask.
|
||||
* These are realistic mobile and landline numbers validated by libphonenumber-js.
|
||||
*/
|
||||
const COUNTRY_TEST_NUMBERS: Record<string, { number: string; description: string }[]> = {
|
||||
ci: [{ number: "+2250797764877", description: "Ivory Coast 10-digit mobile" }],
|
||||
bj: [{ number: "+2290165526657", description: "Benin 10-digit mobile" }],
|
||||
at: [{ number: "+436641234567", description: "Austria mobile" }],
|
||||
ar: [
|
||||
{ number: "+5491112345678", description: "Argentina mobile (Buenos Aires, 11 national digits)" },
|
||||
{ number: "+5493414123456", description: "Argentina mobile (Rosario, 11 national digits)" },
|
||||
{ number: "+541112345678", description: "Argentina landline (10 national digits)" },
|
||||
],
|
||||
fi: [
|
||||
{ number: "+3584012345678", description: "Finland 10-digit mobile" },
|
||||
{ number: "+358501234567", description: "Finland 9-digit mobile" },
|
||||
],
|
||||
};
|
||||
|
||||
describe("CUSTOM_PHONE_MASKS", () => {
|
||||
describe.each(Object.entries(CUSTOM_PHONE_MASKS))("mask for %s", (countryCode, mask) => {
|
||||
const maskDigits = countMaskDigits(mask);
|
||||
const testNumbers = COUNTRY_TEST_NUMBERS[countryCode] || [];
|
||||
|
||||
it("should have enough digit placeholders for all valid phone numbers", () => {
|
||||
for (const { number, description } of testNumbers) {
|
||||
const parsed = parsePhoneNumberFromString(number);
|
||||
expect(parsed, `Failed to parse ${description}: ${number}`).toBeTruthy();
|
||||
|
||||
const nationalDigits = parsed?.nationalNumber.length ?? 0;
|
||||
expect(
|
||||
maskDigits,
|
||||
`Mask for ${countryCode} has ${maskDigits} digit slots but ${description} (${number}) needs ${nationalDigits} national digits`
|
||||
).toBeGreaterThanOrEqual(nationalDigits);
|
||||
}
|
||||
});
|
||||
|
||||
it("should have test numbers that pass libphonenumber-js validation", () => {
|
||||
for (const { number, description } of testNumbers) {
|
||||
expect(isValidPhoneNumber(number), `${description} (${number}) should be a valid phone number`).toBe(
|
||||
true
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -2,20 +2,15 @@
|
||||
|
||||
import { isSupportedCountry } from "libphonenumber-js";
|
||||
import type { CSSProperties } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import PhoneInput from "react-phone-input-2";
|
||||
import "react-phone-input-2/lib/style.css";
|
||||
|
||||
import { useIsPlatform } from "@calcom/atoms/hooks/useIsPlatform";
|
||||
import { useBookerStore, type CountryCode } from "@calcom/features/bookings/Booker/store";
|
||||
import { type CountryCode, useBookerStore } from "@calcom/features/bookings/Booker/store";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import classNames from "@calcom/ui/classNames";
|
||||
|
||||
const CUSTOM_PHONE_MASKS = {
|
||||
ci: ".. .. .. .. ..",
|
||||
bj: ".. .. .. .. ..",
|
||||
at: "... ..........",
|
||||
};
|
||||
import { CUSTOM_PHONE_MASKS } from "./phone-masks";
|
||||
|
||||
export type PhoneInputProps = {
|
||||
value?: string;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Custom phone masks to override outdated masks in react-phone-input-2.
|
||||
* The library's built-in masks can lag behind countries' numbering plan changes,
|
||||
* causing valid phone numbers to be truncated (users can't type enough digits).
|
||||
*
|
||||
* Each dot (.) represents a digit placeholder. When a country updates its
|
||||
* numbering plan to use longer numbers, add an override here.
|
||||
*/
|
||||
export const CUSTOM_PHONE_MASKS = {
|
||||
/** Ivory Coast: migrated from 8 to 10 digits in 2021 */
|
||||
ci: ".. .. .. .. ..",
|
||||
/** Benin: migrated from 8 to 10 digits in 2025 */
|
||||
bj: ".. .. .. .. ..",
|
||||
/** Austria: variable-length numbers up to 13 digits */
|
||||
at: "... ..........",
|
||||
/** Argentina: mobile numbers require 11 national digits (9 + area code + subscriber) */
|
||||
ar: "(..) .........",
|
||||
/** Finland: some mobile numbers use 10 national digits */
|
||||
fi: ".. ... .. ...",
|
||||
};
|
||||
Reference in New Issue
Block a user