From 61ca0ce198f80a5845d1192eac3356440b678bcb Mon Sep 17 00:00:00 2001 From: Camille Moussu Date: Thu, 11 Sep 2025 11:41:31 +0200 Subject: [PATCH 1/2] [#73] added button to send mail --- public/.env.example.js | 1 + src/features/Events/EventDisplayPreview.tsx | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/public/.env.example.js b/public/.env.example.js index 743ac79..6dfa2d2 100644 --- a/public/.env.example.js +++ b/public/.env.example.js @@ -6,3 +6,4 @@ var SSO_RESPONSE_TYPE = "code"; var SSO_CODE_CHALLENGE_METHOD = "S256"; var SSO_POST_LOGOUT_REDIRECT = "http://example.com?logout=1"; var CALENDAR_BASE_URL = "https://calendar.example.com"; +var MAIL_SPA_URL = "https://mail.example.com" diff --git a/src/features/Events/EventDisplayPreview.tsx b/src/features/Events/EventDisplayPreview.tsx index a70fb77..7350380 100644 --- a/src/features/Events/EventDisplayPreview.tsx +++ b/src/features/Events/EventDisplayPreview.tsx @@ -20,6 +20,7 @@ import { PopoverPosition, } from "@mui/material"; import EditIcon from "@mui/icons-material/Edit"; +import EmailIcon from "@mui/icons-material/Email"; import DeleteIcon from "@mui/icons-material/Delete"; import VisibilityIcon from "@mui/icons-material/Visibility"; import CloseIcon from "@mui/icons-material/Close"; @@ -61,6 +62,7 @@ export default function EventPreviewModal({ const user = useAppSelector((state) => state.user); const [showAllAttendees, setShowAllAttendees] = useState(false); const [openFullDisplay, setOpenFullDisplay] = useState(false); + const mailSpaUrl = (window as any).MAIL_SPA_URL ?? null; useEffect(() => { if (!event || !calendar) { @@ -120,6 +122,20 @@ export default function EventPreviewModal({ gap: 1, }} > + {mailSpaUrl && attendees.length > 1 && ( + + window.open( + `${mailSpaUrl}/mailto/?uri=mailto:${event.attendee + .map((a) => a.cal_address) + .join(",")}?subject=${event.title}` + ) + } + > + + + )} {user.userData.email !== event.organizer?.cal_address && ( Date: Thu, 11 Sep 2025 12:19:17 +0200 Subject: [PATCH 2/2] [#73] added tests --- .../features/Events/EventDisplay.test.tsx | 81 +++++++++++++++++++ src/features/Events/EventDisplayPreview.tsx | 2 +- 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/__test__/features/Events/EventDisplay.test.tsx b/__test__/features/Events/EventDisplay.test.tsx index 9ae32d5..28ded30 100644 --- a/__test__/features/Events/EventDisplay.test.tsx +++ b/__test__/features/Events/EventDisplay.test.tsx @@ -15,6 +15,7 @@ describe("Event Preview Display", () => { beforeEach(() => { jest.clearAllMocks(); + (window as any).MAIL_SPA_URL = null; }); const preloadedState = { @@ -63,6 +64,14 @@ describe("Event Preview Display", () => { }, ], }, + event2: { + id: "event2", + title: "Test Event", + calId: "667037022b752d0026472254/cal1", + start: day.toISOString(), + end: day.toISOString(), + organizer: { cn: "test", cal_address: "test@test.com" }, + }, }, }, "otherCal/cal": { @@ -505,6 +514,78 @@ describe("Event Preview Display", () => { }); expect(screen.getByText("Edit Event")).toBeInTheDocument(); }); + it("properly render message button when MAIL_SPA_URL is not null and event has attendees", () => { + (window as any).MAIL_SPA_URL = "test"; + renderWithProviders( + , + preloadedState + ); + expect(screen.getByTestId("EmailIcon")).toBeInTheDocument(); + }); + it("doesnt render message button when MAIL_SPA_URL is not null and event has no attendees", () => { + (window as any).MAIL_SPA_URL = "test"; + renderWithProviders( + , + preloadedState + ); + expect(screen.queryByTestId("EmailIcon")).not.toBeInTheDocument(); + }); + it("doesnt render message button when MAIL_SPA_URL is null and event has attendees", () => { + renderWithProviders( + , + preloadedState + ); + expect(screen.queryByTestId("EmailIcon")).not.toBeInTheDocument(); + }); + it("message button opens url with attendees as uri and title as subject", () => { + (window as any).MAIL_SPA_URL = "test"; + const mockOpen = jest.fn(); + window.open = mockOpen; + + renderWithProviders( + , + preloadedState + ); + + const emailButton = screen.getByTestId("EmailIcon"); + expect(emailButton).toBeInTheDocument(); + + fireEvent.click(emailButton.closest("button")!); + + const event = + preloadedState.calendars.list["667037022b752d0026472254/cal1"].events[ + "event1" + ]; + const expectedUrl = `test/mailto/?uri=mailto:${event.attendee + .map((a) => a.cal_address) + .join(",")}?subject=${event.title}`; + + expect(mockOpen).toHaveBeenCalledWith(expectedUrl); + }); }); describe("Event Full Display", () => { diff --git a/src/features/Events/EventDisplayPreview.tsx b/src/features/Events/EventDisplayPreview.tsx index 7350380..a37e48c 100644 --- a/src/features/Events/EventDisplayPreview.tsx +++ b/src/features/Events/EventDisplayPreview.tsx @@ -122,7 +122,7 @@ export default function EventPreviewModal({ gap: 1, }} > - {mailSpaUrl && attendees.length > 1 && ( + {mailSpaUrl && attendees.length > 0 && (