Skip to content

Commit 22734b5

Browse files
Copilothotlong
andcommitted
Add ARIA attributes for screen reader accessibility to CalendarView
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 52f5093 commit 22734b5

File tree

1 file changed

+21
-7
lines changed

1 file changed

+21
-7
lines changed

packages/plugin-calendar/src/CalendarView.tsx

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -148,18 +148,19 @@ function CalendarView({
148148
}
149149

150150
return (
151-
<div className={cn("flex flex-col h-full bg-background", className)}>
151+
<div role="region" aria-label="Calendar" className={cn("flex flex-col h-full bg-background", className)}>
152152
{/* Header */}
153153
<div className="flex items-center justify-between p-4 border-b">
154154
<div className="flex items-center gap-4">
155155
<div className="flex items-center bg-muted/50 rounded-lg p-1 gap-1">
156-
<Button variant="ghost" size="sm" onClick={handleToday} className="h-8">
156+
<Button variant="ghost" size="sm" onClick={handleToday} className="h-8" aria-label="Go to today">
157157
Today
158158
</Button>
159159
<div className="h-4 w-px bg-border mx-1" />
160160
<Button
161161
variant="ghost"
162162
size="icon"
163+
aria-label="Previous period"
163164
onClick={handlePrevious}
164165
className="h-8 w-8"
165166
>
@@ -168,6 +169,7 @@ function CalendarView({
168169
<Button
169170
variant="ghost"
170171
size="icon"
172+
aria-label="Next period"
171173
onClick={handleNext}
172174
className="h-8 w-8"
173175
>
@@ -179,6 +181,7 @@ function CalendarView({
179181
<PopoverTrigger asChild>
180182
<Button
181183
variant="ghost"
184+
aria-label={`Current date: ${getDateLabel()}`}
182185
className={cn(
183186
"text-xl font-semibold h-auto px-3 py-1 hover:bg-muted/50 transition-colors",
184187
"flex items-center gap-2"
@@ -393,10 +396,11 @@ function MonthView({ date, events, onEventClick, onDateClick, onEventDrop }: Mon
393396
return (
394397
<div className="flex flex-col h-full">
395398
{/* Week day headers */}
396-
<div className="grid grid-cols-7 border-b">
399+
<div role="row" className="grid grid-cols-7 border-b">
397400
{weekDays.map((day) => (
398401
<div
399402
key={day}
403+
role="columnheader"
400404
className="p-2 text-center text-sm font-medium text-muted-foreground border-r last:border-r-0"
401405
>
402406
{day}
@@ -405,7 +409,7 @@ function MonthView({ date, events, onEventClick, onDateClick, onEventDrop }: Mon
405409
</div>
406410

407411
{/* Calendar days */}
408-
<div className="grid grid-cols-7 flex-1 auto-rows-fr">
412+
<div role="grid" aria-label="Calendar grid" className="grid grid-cols-7 flex-1 auto-rows-fr">
409413
{days.map((day, index) => {
410414
const dayEvents = getEventsForDate(day, events)
411415
const isCurrentMonth = day.getMonth() === date.getMonth()
@@ -414,6 +418,8 @@ function MonthView({ date, events, onEventClick, onDateClick, onEventDrop }: Mon
414418
return (
415419
<div
416420
key={index}
421+
role="gridcell"
422+
aria-label={`${day.toLocaleDateString("default", { weekday: "long", month: "long", day: "numeric", year: "numeric" })}${dayEvents.length > 0 ? `, ${dayEvents.length} event${dayEvents.length > 1 ? "s" : ""}` : ""}`}
417423
className={cn(
418424
"border-b border-r last:border-r-0 p-2 min-h-[100px] cursor-pointer hover:bg-accent/50",
419425
!isCurrentMonth && "bg-muted/30 text-muted-foreground",
@@ -430,13 +436,16 @@ function MonthView({ date, events, onEventClick, onDateClick, onEventDrop }: Mon
430436
isToday &&
431437
"inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground h-6 w-6"
432438
)}
439+
{...(isToday ? { "aria-current": "date" as const } : {})}
433440
>
434441
{day.getDate()}
435442
</div>
436443
<div className="space-y-1">
437444
{dayEvents.slice(0, 3).map((event) => (
438445
<div
439446
key={event.id}
447+
role="button"
448+
aria-label={event.title}
440449
draggable={!!onEventDrop}
441450
onDragStart={(e) => handleDragStart(e, event)}
442451
onDragEnd={handleDragEnd}
@@ -517,19 +526,23 @@ function WeekView({ date, events, onEventClick, onDateClick }: WeekViewProps) {
517526
</div>
518527

519528
{/* Week events */}
520-
<div className="grid grid-cols-7 flex-1">
529+
<div role="grid" className="grid grid-cols-7 flex-1">
521530
{weekDays.map((day) => {
522531
const dayEvents = getEventsForDate(day, events)
523532
return (
524533
<div
525534
key={day.toISOString()}
535+
role="gridcell"
536+
aria-label={`${day.toLocaleDateString("default", { weekday: "long", month: "long", day: "numeric", year: "numeric" })}${dayEvents.length > 0 ? `, ${dayEvents.length} event${dayEvents.length > 1 ? "s" : ""}` : ""}`}
526537
className="border-r last:border-r-0 p-2 min-h-[400px] cursor-pointer hover:bg-accent/50"
527538
onClick={() => onDateClick?.(day)}
528539
>
529540
<div className="space-y-2">
530541
{dayEvents.map((event) => (
531542
<div
532543
key={event.id}
544+
role="button"
545+
aria-label={event.title}
533546
className={cn(
534547
"text-sm px-3 py-2 rounded cursor-pointer hover:opacity-80",
535548
event.color || DEFAULT_EVENT_COLOR
@@ -576,7 +589,7 @@ function DayView({ date, events, onEventClick }: DayViewProps) {
576589

577590
return (
578591
<div className="flex flex-col h-full">
579-
<div className="flex-1 overflow-auto">
592+
<div role="list" className="flex-1 overflow-auto">
580593
{hours.map((hour) => {
581594
const hourEvents = dayEvents.filter((event) => {
582595
if (event.allDay) return hour === 0
@@ -585,7 +598,7 @@ function DayView({ date, events, onEventClick }: DayViewProps) {
585598
})
586599

587600
return (
588-
<div key={hour} className="flex border-b min-h-[60px]">
601+
<div key={hour} role="listitem" className="flex border-b min-h-[60px]">
589602
<div className="w-20 p-2 text-sm text-muted-foreground border-r">
590603
{hour === 0
591604
? "12 AM"
@@ -599,6 +612,7 @@ function DayView({ date, events, onEventClick }: DayViewProps) {
599612
{hourEvents.map((event) => (
600613
<div
601614
key={event.id}
615+
aria-label={event.title}
602616
className={cn(
603617
"px-3 py-2 rounded cursor-pointer hover:opacity-80",
604618
event.color || DEFAULT_EVENT_COLOR

0 commit comments

Comments
 (0)