Skip to content

Commit aef82e3

Browse files
committed
test(web): add unit tests for all-day event menu interactions
- Introduce tests for single-day and multi-day all-day events in AgendaEventMenu. - Validate menu visibility, focus behavior, and outside click interactions for all-day events. - Ensure no time/date information is displayed for all-day events in the menu.
1 parent 8d9be0d commit aef82e3

File tree

3 files changed

+148
-16
lines changed

3 files changed

+148
-16
lines changed

packages/web/src/views/Day/components/Agenda/Events/AgendaEventMenu/AgendaEventMenu.test.tsx

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,4 +229,122 @@ describe("AgendaEventMenu", () => {
229229
// Menu should still be visible
230230
expect(screen.getByText("Test Event")).toBeInTheDocument();
231231
});
232+
233+
describe("All-day events", () => {
234+
const singleDayAllDayEvent: Schema_Event = {
235+
_id: "test-allday-1",
236+
title: "Single Day All Day Event",
237+
description: "This is a single day all day event",
238+
startDate: "2024-01-15T00:00:00Z",
239+
endDate: "2024-01-15T23:59:59Z",
240+
isAllDay: true,
241+
};
242+
243+
const multiDayAllDayEvent: Schema_Event = {
244+
_id: "test-allday-2",
245+
title: "Multi Day All Day Event",
246+
description: "This is a multi day all day event",
247+
startDate: "2024-01-15T00:00:00Z",
248+
endDate: "2024-01-17T23:59:59Z",
249+
isAllDay: true,
250+
};
251+
252+
it("should show menu for single-day all-day event without time display", async () => {
253+
const user = userEvent.setup();
254+
render(<TestComponent event={singleDayAllDayEvent} />);
255+
256+
const trigger = screen.getByRole("button", {
257+
name: "Event Trigger",
258+
});
259+
await user.hover(trigger);
260+
261+
await waitFor(() => {
262+
expect(
263+
screen.getByText("Single Day All Day Event"),
264+
).toBeInTheDocument();
265+
expect(
266+
screen.getByText("This is a single day all day event"),
267+
).toBeInTheDocument();
268+
});
269+
270+
// Should not show any time/date info for all-day events
271+
expect(screen.queryByText(/2024-01-15/)).not.toBeInTheDocument();
272+
expect(screen.queryByText(/2024-01-17/)).not.toBeInTheDocument();
273+
});
274+
275+
it("should show menu for multi-day all-day event without time display", async () => {
276+
const user = userEvent.setup();
277+
render(<TestComponent event={multiDayAllDayEvent} />);
278+
279+
const trigger = screen.getByRole("button", {
280+
name: "Event Trigger",
281+
});
282+
await user.hover(trigger);
283+
284+
await waitFor(() => {
285+
expect(screen.getByText("Multi Day All Day Event")).toBeInTheDocument();
286+
expect(
287+
screen.getByText("This is a multi day all day event"),
288+
).toBeInTheDocument();
289+
});
290+
291+
// Should not show any time/date info for all-day events
292+
expect(screen.queryByText(/2024-01-15/)).not.toBeInTheDocument();
293+
expect(screen.queryByText(/2024-01-17/)).not.toBeInTheDocument();
294+
});
295+
296+
it("should show menu on focus for all-day events", async () => {
297+
const user = userEvent.setup();
298+
render(<TestComponent event={singleDayAllDayEvent} />);
299+
300+
const trigger = screen.getByRole("button", {
301+
name: "Event Trigger",
302+
});
303+
await act(async () => {
304+
await user.tab();
305+
});
306+
307+
expect(document.activeElement).toBe(trigger);
308+
309+
await waitFor(() => {
310+
expect(
311+
screen.getByText("Single Day All Day Event"),
312+
).toBeInTheDocument();
313+
});
314+
});
315+
316+
it("should close menu on outside click for all-day events", async () => {
317+
const user = userEvent.setup();
318+
render(
319+
<div>
320+
<TestComponent event={multiDayAllDayEvent} />
321+
<button>Outside Button</button>
322+
</div>,
323+
);
324+
325+
const trigger = screen.getByRole("button", {
326+
name: "Event Trigger",
327+
});
328+
await act(async () => {
329+
await user.hover(trigger);
330+
});
331+
332+
await waitFor(() => {
333+
expect(screen.getByText("Multi Day All Day Event")).toBeInTheDocument();
334+
});
335+
336+
const outsideButton = screen.getByRole("button", {
337+
name: "Outside Button",
338+
});
339+
await act(async () => {
340+
await user.click(outsideButton);
341+
});
342+
343+
await waitFor(() => {
344+
expect(
345+
screen.queryByText("Multi Day All Day Event"),
346+
).not.toBeInTheDocument();
347+
});
348+
});
349+
});
232350
});

packages/web/src/views/Day/components/Agenda/Events/AgendaEventMenu/AgendaEventMenuContent.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,15 @@ export const AgendaEventMenuContent = forwardRef<
1515
const context = useAgendaEventMenuContext();
1616
const ref = useMergeRefs([context.refs.setFloating, propRef]);
1717

18-
const startTime = event.startDate ? getAgendaEventTime(event.startDate) : "";
19-
const endTime = event.endDate ? getAgendaEventTime(event.endDate) : "";
20-
const timeRange = startTime && endTime ? `${startTime} - ${endTime}` : "";
18+
let timeDisplay = "";
19+
if (!event.isAllDay) {
20+
const startTime = event.startDate
21+
? getAgendaEventTime(event.startDate)
22+
: "";
23+
const endTime = event.endDate ? getAgendaEventTime(event.endDate) : "";
24+
timeDisplay = startTime && endTime ? `${startTime} - ${endTime}` : "";
25+
}
26+
// All-day events: timeDisplay stays empty (no dates shown)
2127

2228
return (
2329
<FloatingPortal>
@@ -45,12 +51,12 @@ export const AgendaEventMenuContent = forwardRef<
4551
>
4652
{event.title || "Untitled Event"}
4753
</h3>
48-
{timeRange && (
54+
{timeDisplay && (
4955
<time
5056
className="text-xs font-medium text-gray-600"
5157
dateTime={`${event.startDate}/${event.endDate}`}
5258
>
53-
{timeRange}
59+
{timeDisplay}
5460
</time>
5561
)}
5662
{event.description && (
Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
11
import { Schema_Event } from "@core/types/event.types";
2+
import { AgendaEventMenu } from "../AgendaEventMenu/AgendaEventMenu";
3+
import { AgendaEventMenuContent } from "../AgendaEventMenu/AgendaEventMenuContent";
4+
import { AgendaEventMenuTrigger } from "../AgendaEventMenu/AgendaEventMenuTrigger";
25

36
export const AllDayAgendaEvent = ({ event }: { event: Schema_Event }) => {
47
if (!event.title) return null;
58

69
const isPast = event.endDate ? new Date(event.endDate) < new Date() : false;
710

811
return (
9-
<div
10-
className={`text-white-100 flex items-center rounded bg-blue-200 px-2 py-1 text-xs focus:ring-2 focus:ring-yellow-200 focus:outline-none ${
11-
isPast ? "opacity-60" : ""
12-
}`}
13-
title={event.title}
14-
tabIndex={0}
15-
role="button"
16-
data-event-id={event._id}
17-
>
18-
<span className="flex-1 truncate">{event.title}</span>
19-
</div>
12+
<AgendaEventMenu>
13+
<AgendaEventMenuTrigger asChild>
14+
<div
15+
className={`text-white-100 flex items-center rounded bg-blue-200 px-2 py-1 text-xs focus:ring-2 focus:ring-yellow-200 focus:outline-none ${
16+
isPast ? "opacity-60" : ""
17+
}`}
18+
title={event.title}
19+
tabIndex={0}
20+
role="button"
21+
data-event-id={event._id}
22+
>
23+
<span className="flex-1 truncate">{event.title}</span>
24+
</div>
25+
</AgendaEventMenuTrigger>
26+
<AgendaEventMenuContent event={event} />
27+
</AgendaEventMenu>
2028
);
2129
};

0 commit comments

Comments
 (0)