feat: add DigitInput component; force English digits for phone & national code

New reusable DigitInput normalizes Persian/Arabic digits to English on
input, strips non-digits, enforces maxDigits, and is always LTR + numeric
keyboard. Use it for the phone and national-code fields on the appointment
create page (phone previously kept raw Persian digits; national code lost
Persian digits to the \D strip).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-07-16 09:45:04 +03:30
co-authored by Claude Opus 4.8
parent b2347f00e7
commit e1fb63eb0f
3 changed files with 67 additions and 5 deletions
@@ -0,0 +1,29 @@
import { describe, it, expect, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import DigitInput from './DigitInput';
describe('DigitInput — ورودی عددی با تبدیل رقم فارسی', () => {
it('converts Persian/Arabic digits to English (happy path)', () => {
const onChange = vi.fn();
render(<DigitInput value="" onChange={onChange} placeholder="p" />);
fireEvent.change(screen.getByPlaceholderText('p'), { target: { value: '۰۹۱۲٣٤' } });
expect(onChange).toHaveBeenCalledWith('091234');
});
it('strips non-digit characters (error/garbage input)', () => {
const onChange = vi.fn();
render(<DigitInput value="" onChange={onChange} placeholder="p" />);
fireEvent.change(screen.getByPlaceholderText('p'), { target: { value: '0a9 1-2b' } });
expect(onChange).toHaveBeenCalledWith('0912');
});
it('enforces maxDigits and handles empty input (boundary)', () => {
const onChange = vi.fn();
render(<DigitInput value="" onChange={onChange} maxDigits={10} placeholder="p" />);
const input = screen.getByPlaceholderText('p');
fireEvent.change(input, { target: { value: '۱۲۳۴۵۶۷۸۹۰۱۲' } });
expect(onChange).toHaveBeenCalledWith('1234567890');
expect(input).toHaveAttribute('dir', 'ltr');
expect(input).toHaveAttribute('inputmode', 'numeric');
});
});
+32
View File
@@ -0,0 +1,32 @@
import React from 'react';
import { toEnglishDigits } from '../../lib/utils';
interface DigitInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'type'> {
value: string;
onChange: (value: string) => void;
/** حداکثر تعداد رقم (مثلاً ۱۱ برای موبایل، ۱۰ برای کد ملی). */
maxDigits?: number;
}
/**
* ورودیِ فقط-عددی که ارقام فارسی/عربی را همان لحظه به انگلیسی تبدیل می‌کند و هر
* کاراکتر غیررقمی را حذف می‌کند؛ همیشه LTR و کیبورد عددی. مناسب شماره تماس و کد ملی.
*/
export default function DigitInput({ value, onChange, maxDigits, ...rest }: DigitInputProps) {
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
let digits = toEnglishDigits(e.target.value).replace(/\D/g, '');
if (maxDigits) digits = digits.slice(0, maxDigits);
onChange(digits);
};
return (
<input
{...rest}
value={value}
onChange={handleChange}
dir="ltr"
inputMode="numeric"
maxLength={maxDigits}
/>
);
}