fix(holidays): honest dates and reachable fields on the holidays page
The table had two date columns for one date: "تاریخ" printed the raw 1405-05-13 string in Latin digits, and the column labelled "میلادی" ran the same day through formatDate — which returns Jalali. One date, twice, under a label that lied. It is now a single formatted Jalali column. The closure form used a native <input type="date">: Gregorian, an English mm/dd/yyyy placeholder in an RTL Persian panel, and a white box in dark mode because a native control does not follow the theme. It is the shared Persian picker now. That picker turned out to be a div with an onClick — no role, no tab stop, no accessible name, and its clear button was a span. Since every page that picks a date goes through it, it gained role/tabIndex/Enter-Space, an ariaLabel prop, and a real button for clear. The page passes labels for the year select and both form fields, and the global topbar search got an aria-label, which takes the runtime accessibility probe on this page to clean. useHolidays now returns an error, so a failed request reads as an error instead of an empty year — previously indistinguishable. The page had no test file; it has eight. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -38,7 +38,7 @@ export default function Topbar({ onMobileMenuOpen }: { onMobileMenuOpen?: () =>
|
||||
{/* Search bar — placeholder «جستجو» مطابق clinic-pro-tauri */}
|
||||
<div className="topbar-search">
|
||||
<MagnifyingGlassIcon style={{ width: 17, height: 17, flexShrink: 0 }} />
|
||||
<input placeholder="جستجو" readOnly />
|
||||
<input placeholder="جستجو" aria-label="جستجو" readOnly />
|
||||
</div>
|
||||
|
||||
<div style={{ flex: 1 }} />
|
||||
|
||||
@@ -6,6 +6,8 @@ interface Props {
|
||||
placeholder?: string;
|
||||
/** انتخاب سال→ماه→روز را فعال میکند (مناسب تاریخ تولد). */
|
||||
enableYearPicker?: boolean;
|
||||
/** نام دسترسپذیر؛ تریگر `input` نیست پس `<label htmlFor>` رویش نمینشیند. */
|
||||
ariaLabel?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,9 +12,14 @@ interface Props {
|
||||
minWidth?: number;
|
||||
/** انتخاب سال→ماه→روز را فعال میکند (پیشفرض خاموش). */
|
||||
enableYearPicker?: boolean;
|
||||
/**
|
||||
* نام دسترسپذیر. تریگر یک `div` است نه `input`، پس `<label htmlFor>` رویش نمینشیند
|
||||
* و بدون این، screen reader فقط «button» میشنود. پیشفرض روی placeholder میافتد.
|
||||
*/
|
||||
ariaLabel?: string;
|
||||
}
|
||||
|
||||
export default function PersianDatePicker({ value, onChange, placeholder = 'انتخاب تاریخ', height = 38, minWidth = 148, enableYearPicker = false }: Props) {
|
||||
export default function PersianDatePicker({ value, onChange, placeholder = 'انتخاب تاریخ', height = 38, minWidth = 148, enableYearPicker = false, ariaLabel }: Props) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const triggerRef = useRef<HTMLDivElement>(null);
|
||||
const [pos, setPos] = useState<{ top: number; right: number } | null>(null);
|
||||
@@ -29,7 +34,16 @@ export default function PersianDatePicker({ value, onChange, placeholder = 'ان
|
||||
return (
|
||||
<div ref={triggerRef} style={{ position: 'relative', display: 'inline-block' }}>
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-haspopup="dialog"
|
||||
aria-expanded={open}
|
||||
aria-label={ariaLabel ?? placeholder}
|
||||
onClick={toggle}
|
||||
// با موس باز میشد و با کیبورد نه؛ Enter/Space همان کاری را میکنند که کلیک.
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggle(); }
|
||||
}}
|
||||
style={{
|
||||
display: 'flex', alignItems: 'center', gap: 7,
|
||||
height, padding: '0 10px', borderRadius: 'var(--r-sm)',
|
||||
@@ -41,12 +55,17 @@ export default function PersianDatePicker({ value, onChange, placeholder = 'ان
|
||||
<CalendarDaysIcon style={{ width: 15, height: 15, color: 'var(--text-3)', flexShrink: 0 }} />
|
||||
<span style={{ flex: 1 }}>{value ? formatDate(value) : placeholder}</span>
|
||||
{value && (
|
||||
<span
|
||||
<button
|
||||
type="button"
|
||||
aria-label="پاک کردن تاریخ"
|
||||
onClick={(e) => { e.stopPropagation(); onChange(''); }}
|
||||
style={{ display: 'flex', alignItems: 'center', cursor: 'pointer', color: 'var(--text-3)' }}
|
||||
style={{
|
||||
display: 'flex', alignItems: 'center', cursor: 'pointer', color: 'var(--text-3)',
|
||||
background: 'none', border: 0, padding: 0,
|
||||
}}
|
||||
>
|
||||
<XMarkIcon style={{ width: 13, height: 13 }} />
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user