feat: implement input length constraints and enhanced validation for international phone numbers

This commit is contained in:
estifanos
2026-08-20 08:58:07 +00:00
parent caa209306f
commit bc31cec0b2
5 changed files with 66 additions and 13 deletions

View File

@@ -8,7 +8,7 @@ import {
type CountryCode,
} from 'libphonenumber-js';
import { CountryFlag, getCountryName, resolveLang } from './CountrySelect';
import { formatNational, nextNationalDigits, toE164, toNationalDigits } from './phone';
import { exceedsMaxLength, formatNational, maxNationalLength, nextNationalDigits, toE164, toNationalDigits } from './phone';
// Static list, computed once at module load — same as CountrySelect's dataset.
const COUNTRY_CODES = getCountries();
@@ -45,7 +45,11 @@ export interface PhoneInputProps {
/** E.164 (`+14155552671`), or '' when empty. */
value: string;
onChange: (value: string) => void;
/** Fired when the number box loses focus — wire to the form's `trigger`. */
/**
* Fired when focus leaves a field that has digits in it — wire to the
* form's `trigger`. A blank field is left to submit-time validation, like
* the form's other inputs, so tabbing past it doesn't raise an error.
*/
onBlur?: () => void;
label?: React.ReactNode;
placeholder?: string;
@@ -124,6 +128,8 @@ export function PhoneInput({
}
function applyDigits(digits: string, forCountry: CountryCode) {
// Keystroke past the longest possible number is ignored, not stored.
if (exceedsMaxLength(digits, forCountry)) return;
// Once the number is valid, show the true national part: someone who
// types a trunk prefix (0911111111) or the country code (251911111111)
// shouldn't end up with it doubled beside the "+251" selector. Not
@@ -155,7 +161,8 @@ export function PhoneInput({
if (!next) return;
const code = next as CountryCode;
setCountry(code);
applyDigits(national.replace(/\D/g, ''), code);
// A number carried over from a longer plan is cut to fit the new one.
applyDigits(national.replace(/\D/g, '').slice(0, maxNationalLength(code)), code);
}
return (
@@ -177,7 +184,7 @@ export function PhoneInput({
// country select mustn't validate a half-typed number.
wrapperProps={{
onBlur: (e: React.FocusEvent<HTMLDivElement>) => {
if (!e.currentTarget.contains(e.relatedTarget)) onBlur?.();
if (national && !e.currentTarget.contains(e.relatedTarget)) onBlur?.();
},
}}
leftSectionWidth={92}