From 29e45e9c6f5b151a48f88870463c3049297d2b72 Mon Sep 17 00:00:00 2001 From: mortezaei Date: Wed, 26 Aug 2026 18:57:01 +0330 Subject: [PATCH] fix phone --- .../Componentes/question-phone.test.tsx | 24 ++++++ src/components/Componentes/question-phone.tsx | 79 +++++++++++++------ 2 files changed, 77 insertions(+), 26 deletions(-) diff --git a/src/components/Componentes/question-phone.test.tsx b/src/components/Componentes/question-phone.test.tsx index 895d93d..e796aa4 100644 --- a/src/components/Componentes/question-phone.test.tsx +++ b/src/components/Componentes/question-phone.test.tsx @@ -311,4 +311,28 @@ describe("QuestionPhone IP country detection and shimmer", () => { ).toBeNull(); expect(container.querySelector(".border-\\[\\#F2465F\\]")).toBeNull(); }); + + it("formats Tajikistan (+992) phone numbers and caps to 9 national digits without breaking spaces", async () => { + setStoredUserGeoRegion({ country: "Tajikistan", countryCode: "TJ", phoneCode: "+992" }); + + render(); + + const input = screen.getByRole("textbox") as HTMLInputElement; + + // Type initial digits + fireEvent.change(input, { target: { value: "91712" } }); + expect(input.value).toBe("91 712"); + + // Complete 9-digit national number + fireEvent.change(input, { target: { value: "917123456" } }); + expect(input.value).toBe("91 712 3456"); + + // Type overflow / extra digits (should be capped at 9 digits and NOT collapse/break) + fireEvent.change(input, { target: { value: "917123456789" } }); + expect(input.value).toBe("91 712 3456"); + + // Test with leading 0 (e.g. 0917123456) -> strips leading 0 and formats correctly + fireEvent.change(input, { target: { value: "0917123456" } }); + expect(input.value).toBe("91 712 3456"); + }); }); diff --git a/src/components/Componentes/question-phone.tsx b/src/components/Componentes/question-phone.tsx index a77dbc6..d99ef6b 100644 --- a/src/components/Componentes/question-phone.tsx +++ b/src/components/Componentes/question-phone.tsx @@ -3,6 +3,7 @@ import { AsYouTypeFormatter, PhoneNumberFormat, + PhoneNumberType, PhoneNumberUtil, } from "google-libphonenumber"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; @@ -139,30 +140,46 @@ function readPhoneValue(value: unknown, fallbackCode: string): PhoneValueParts { }; } -function getMaxLengthForCountry(codeValue: string): number { +export function getMaxDigitsForCountry(codeValue: string): number { try { const cleanCode = codeValue.replace(/[^\d]/g, ""); - if (!cleanCode) return 18; - if (cleanCode === "98") return 12; // 3 + 1 + 3 + 1 + 4 = 12 chars + if (!cleanCode) return 15; + if (cleanCode === "98") return 10; const countryCode = Number(cleanCode); const regionCode = phoneUtil.getRegionCodeForCountryCode(countryCode); - if (!regionCode || regionCode === "ZZ") return 18; - - const exampleMobile = phoneUtil.getExampleNumberForType( - regionCode, - 1, // MOBILE - ); - const exampleGeneral = phoneUtil.getExampleNumber(regionCode); - - const lenMobile = exampleMobile - ? String(exampleMobile.getNationalNumber()).length - : 0; - const lenGeneral = exampleGeneral - ? String(exampleGeneral.getNationalNumber()).length - : 0; - const baseLen = Math.max(lenMobile, lenGeneral, 8); + if (!regionCode || regionCode === "ZZ") return 15; + + const types = [ + PhoneNumberType.MOBILE, + PhoneNumberType.FIXED_LINE, + PhoneNumberType.FIXED_LINE_OR_MOBILE, + PhoneNumberType.PERSONAL_NUMBER, + ]; + let maxDigits = 0; + for (const t of types) { + const ex = phoneUtil.getExampleNumberForType(regionCode, t); + if (ex) { + const len = String(ex.getNationalNumber()).length; + if (len > maxDigits) maxDigits = len; + } + } + if (!maxDigits) { + const ex = phoneUtil.getExampleNumber(regionCode); + if (ex) maxDigits = String(ex.getNationalNumber()).length; + } + return maxDigits || 15; + } catch { + return 15; + } +} - return baseLen + 6; +function getMaxLengthForCountry(codeValue: string): number { + try { + const cleanCode = codeValue.replace(/[^\d]/g, ""); + if (!cleanCode) return 18; + if (cleanCode === "98") return 12; // 3 + 1 + 3 + 1 + 4 = 12 chars + const maxDigits = getMaxDigitsForCountry(codeValue); + return maxDigits + 5; } catch { return 18; } @@ -191,7 +208,7 @@ function getCountryPlaceholder( const example = phoneUtil.getExampleNumberForType( regionCode, - 1, // MOBILE + PhoneNumberType.MOBILE, ) || phoneUtil.getExampleNumber(regionCode); if (!example) { @@ -214,16 +231,26 @@ function formatPhoneNumberAsYouType( codeValue: string, ): string { const cleanCode = codeValue.replace(/[^\d]/g, ""); - const rawDigits = rawInput.replace(/\D/g, ""); + let rawDigits = rawInput.replace(/\D/g, ""); if (!rawDigits) return ""; + // Strip leading domestic trunk 0 (except for Italy +39 which retains leading zero) + if (cleanCode !== "39" && rawDigits.startsWith("0")) { + rawDigits = rawDigits.replace(/^0+/, ""); + } + if (!rawDigits) return ""; + + const maxDigits = getMaxDigitsForCountry(codeValue); + if (rawDigits.length > maxDigits) { + rawDigits = rawDigits.slice(0, maxDigits); + } + // Special telegram-style handling for Iran (+98): 3 - 3 - 4 (e.g. 912 345 6789) if (cleanCode === "98") { - const digits = rawDigits.startsWith("0") ? rawDigits.slice(1) : rawDigits; - const max10 = digits.slice(0, 10); - if (max10.length <= 3) return max10; - if (max10.length <= 6) return `${max10.slice(0, 3)} ${max10.slice(3)}`; - return `${max10.slice(0, 3)} ${max10.slice(3, 6)} ${max10.slice(6)}`; + if (rawDigits.length <= 3) return rawDigits; + if (rawDigits.length <= 6) + return `${rawDigits.slice(0, 3)} ${rawDigits.slice(3)}`; + return `${rawDigits.slice(0, 3)} ${rawDigits.slice(3, 6)} ${rawDigits.slice(6)}`; } const countryCode = Number(cleanCode);