feat(date-input): refactor PersianDateInput to use PersianDatePicker for Jalali calendar support
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import PersianDateInput from '@/components/ui/PersianDateInput';
|
||||
|
||||
describe('PersianDateInput', () => {
|
||||
it('placeholder را وقتی مقدار خالی است نشان میدهد', () => {
|
||||
render(<PersianDateInput value="" onChange={vi.fn()} placeholder="از تاریخ" />);
|
||||
expect(screen.getByText('از تاریخ')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('از تقویم شمسی استفاده میکند نه input بومی میلادی', () => {
|
||||
const { container } = render(<PersianDateInput value="" onChange={vi.fn()} />);
|
||||
expect(container.querySelector('input[type="date"]')).toBeNull();
|
||||
});
|
||||
|
||||
it('با کلیک، تقویم شمسی باز میشود (نام ماه فارسی)', () => {
|
||||
const { container } = render(<PersianDateInput value="2024-03-25" onChange={vi.fn()} />);
|
||||
fireEvent.click((container.firstChild as HTMLElement).firstChild as Element);
|
||||
// 2024-03-25 میلادی = ۶ فروردین ۱۴۰۳ شمسی
|
||||
expect(screen.getByText('فروردین')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('انتخاب روز، onChange را با YYYY-MM-DD میلادی صدا میزند', () => {
|
||||
const onChange = vi.fn();
|
||||
const { container } = render(<PersianDateInput value="2024-03-25" onChange={onChange} />);
|
||||
fireEvent.click((container.firstChild as HTMLElement).firstChild as Element);
|
||||
const target = Array.from(container.querySelectorAll('button')).find(
|
||||
(b) => b.textContent?.trim() === '۱۵',
|
||||
);
|
||||
expect(target).toBeTruthy();
|
||||
fireEvent.click(target as Element);
|
||||
expect(onChange).toHaveBeenCalledWith(expect.stringMatching(/^\d{4}-\d{2}-\d{2}$/));
|
||||
});
|
||||
|
||||
it('دکمه پاککردن، onChange با رشته خالی میفرستد', () => {
|
||||
const onChange = vi.fn();
|
||||
const { container } = render(<PersianDateInput value="2024-03-25" onChange={onChange} />);
|
||||
const svgs = container.querySelectorAll('svg');
|
||||
const clearIcon = svgs[svgs.length - 1];
|
||||
fireEvent.click(clearIcon.parentElement as Element);
|
||||
expect(onChange).toHaveBeenCalledWith('');
|
||||
});
|
||||
});
|
||||
@@ -1,69 +1,17 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { CalendarDaysIcon, XMarkIcon } from '@heroicons/react/24/outline';
|
||||
import { formatDate } from '../../lib/utils';
|
||||
import PersianDatePicker from './PersianDatePicker';
|
||||
|
||||
interface Props {
|
||||
value: string;
|
||||
value: string; // YYYY-MM-DD میلادی
|
||||
onChange: (v: string) => void;
|
||||
placeholder?: string;
|
||||
min?: string;
|
||||
max?: string;
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
/** انتخاب سال→ماه→روز را فعال میکند (مناسب تاریخ تولد). */
|
||||
enableYearPicker?: boolean;
|
||||
}
|
||||
|
||||
export default function PersianDateInput({ value, onChange, placeholder = 'انتخاب تاریخ', min, max, style, className }: Props) {
|
||||
const hiddenRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const open = () => {
|
||||
const el = hiddenRef.current;
|
||||
if (!el) return;
|
||||
if (typeof el.showPicker === 'function') {
|
||||
try { el.showPicker(); } catch { el.focus(); }
|
||||
} else {
|
||||
el.focus();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ position: 'relative', display: 'inline-flex', alignItems: 'center', ...style }} className={className}>
|
||||
{/* visible text layer */}
|
||||
<div
|
||||
onClick={open}
|
||||
style={{
|
||||
display: 'flex', alignItems: 'center', gap: 7,
|
||||
height: 36, padding: '0 10px', borderRadius: 'var(--r-sm)',
|
||||
border: '1px solid var(--border)', background: 'var(--surface)',
|
||||
cursor: 'pointer', fontSize: 13, color: value ? 'var(--text)' : 'var(--text-3)',
|
||||
userSelect: 'none', minWidth: 148, whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
<CalendarDaysIcon style={{ width: 15, height: 15, color: 'var(--text-3)', flexShrink: 0 }} />
|
||||
<span style={{ flex: 1 }}>{value ? formatDate(value) : placeholder}</span>
|
||||
{value && (
|
||||
<span
|
||||
onClick={e => { e.stopPropagation(); onChange(''); }}
|
||||
style={{ display: 'flex', alignItems: 'center', cursor: 'pointer', color: 'var(--text-3)' }}
|
||||
>
|
||||
<XMarkIcon style={{ width: 13, height: 13 }} />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* hidden native input — opens picker on click */}
|
||||
<input
|
||||
ref={hiddenRef}
|
||||
type="date"
|
||||
value={value}
|
||||
min={min}
|
||||
max={max}
|
||||
onChange={e => onChange(e.target.value)}
|
||||
style={{
|
||||
position: 'absolute', opacity: 0, pointerEvents: 'none',
|
||||
width: 1, height: 1, top: 0, left: 0,
|
||||
}}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
/**
|
||||
* روکش نازک روی PersianDatePicker تا انتخاب تاریخ در همهی صفحات با تقویم شمسی باشد.
|
||||
* منبع واحد پیکر شمسی = PersianDatePicker/PersianCalendar.
|
||||
*/
|
||||
export default function PersianDateInput(props: Props) {
|
||||
return <PersianDatePicker {...props} />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user