From a4a64ac23c7caa472c82d715fc0efd7dea37cd16 Mon Sep 17 00:00:00 2001 From: Camille Moussu Date: Tue, 22 Jul 2025 14:12:24 +0200 Subject: [PATCH] [#37] added css and conditions to render mini calendar correctly --- .../components/MiniCalendarColor.test.tsx | 113 ++++++++++++++++++ src/components/Calendar/Calendar.css | 64 ++-------- src/components/Calendar/Calendar.tsx | 39 +++++- 3 files changed, 163 insertions(+), 53 deletions(-) create mode 100644 __test__/components/MiniCalendarColor.test.tsx diff --git a/__test__/components/MiniCalendarColor.test.tsx b/__test__/components/MiniCalendarColor.test.tsx new file mode 100644 index 0000000..4bbed9e --- /dev/null +++ b/__test__/components/MiniCalendarColor.test.tsx @@ -0,0 +1,113 @@ +import { renderWithProviders } from "../utils/Renderwithproviders"; +import { fireEvent, screen } from "@testing-library/react"; +import { jest } from "@jest/globals"; +import CalendarApp from "../../src/components/Calendar/Calendar"; +import * as appHooks from "../../src/app/hooks"; +import { ThunkDispatch } from "@reduxjs/toolkit"; + +describe("MiniCalendar", () => { + const day = new Date(); + beforeEach(() => { + jest.clearAllMocks(); + const dispatch = jest.fn() as ThunkDispatch; + jest.spyOn(appHooks, "useAppDispatch").mockReturnValue(dispatch); + }); + + const renderCalendar = () => { + const preloadedState = { + user: { + userData: { + sub: "test", + email: "test@test.com", + sid: "mockSid", + openpaasId: "667037022b752d0026472254", + }, + }, + calendars: { + list: { + "667037022b752d0026472254/cal1": { + name: "Calendar 1", + color: "#FF0000", + events: { + event1: { + id: "event1", + title: "Test Event", + start: day.toISOString(), + }, + }, + }, + }, + pending: false, + }, + }; + renderWithProviders(, preloadedState); + }; + + it("renders mini calendar with today in orange", async () => { + renderCalendar(); + const today = new Date().getDate().toString(); + const todayTile = screen + .getAllByText(today) + .find((el) => el.tagName.toLowerCase() === "abbr"); + expect(todayTile?.parentElement).toHaveClass("today"); + }); + + it("renders mini calendar with the week in gray (except for today) when full calendar in week view", async () => { + renderCalendar(); + const today = new Date(); + const sunday = new Date(today); + sunday.setDate(today.getDate() - today.getDay()); + + for (let i = 0; i < 7; i++) { + const date = new Date(sunday); + date.setDate(sunday.getDate() + i); + const tile = screen + .getAllByText(date.getDate()) + .find((el) => el.tagName.toLowerCase() === "abbr"); + if (date.getTime() !== today.setHours(0, 0, 0, 0)) { + expect(tile?.parentElement).toHaveClass("selectedWeek"); + } + } + }); + + it("renders mini calendar with the day in gray (except for today) when full calendar in day view", async () => { + renderCalendar(); + + // Simulate switching to day view + const dayViewButton = await screen.findByTitle(/day view/i); + fireEvent.click(dayViewButton); + + const today = new Date().getDate().toString(); + const todayTile = screen + .getAllByText(today) + .find((el) => el.tagName.toLowerCase() === "abbr"); + expect(todayTile?.parentElement).toHaveClass("selectedWeek"); + }); + + it("renders mini calendar with nothing colored (except for today) when full calendar in month view", async () => { + renderCalendar(); + + // Switch to month view + const monthButton = await screen.findByRole("button", { name: /month/i }); + fireEvent.click(monthButton); + + const tiles = await screen.findAllByRole("gridcell"); + + tiles.forEach((tile) => { + if (!tile.classList.contains("today")) { + expect(tile.className).not.toContain("selectedWeek"); + } + }); + }); + + it("renders mini calendar with dots on days with personal events", async () => { + renderCalendar(); + + const dot = document.querySelector(".event-dot"); + console.log(dot?.parentElement?.children[0].innerHTML); + expect(dot?.parentElement?.children[0].innerHTML).toBe( + day.getDate().toString() + ); + expect(dot).toBeInTheDocument(); + }); +}); diff --git a/src/components/Calendar/Calendar.css b/src/components/Calendar/Calendar.css index 9fbd9c1..7be5439 100644 --- a/src/components/Calendar/Calendar.css +++ b/src/components/Calendar/Calendar.css @@ -41,7 +41,6 @@ main { align-items: center; padding: 0.5rem 0; font-family: "Inter", sans-serif; - color: #1e293b; /* dark navy */ } /* Day number */ @@ -74,7 +73,7 @@ main { border: none; } -/* Container de la sidebar */ +/* sidebar*/ .sidebar-calendar { width: 280px; background-color: #fff; @@ -87,7 +86,7 @@ main { font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; } -/* Style du mini calendrier */ +/* mini calendar style */ .react-calendar { border: none; font-size: 14px; @@ -95,29 +94,28 @@ main { user-select: none; } -/* Jours sélectionnés et hover */ +/* selected day hover */ .react-calendar__tile { transition: background-color 0.3s ease, color 0.3s ease; - border-radius: 50%; border: none; background-color: white; width: 12px; height: 37.52px; } -.react-calendar__tile:hover { - background-color: rgb(224, 224, 224); - border: none; - border-radius: 50%; - cursor: pointer; +.react-calendar__tile--active { + background-color: rgb(255, 255, 255); } -.react-calendar__tile--active { - background-color: orange; +.today { + background: orange !important; color: white; } +.selectedWeek { + background-color: lightgrey; +} -/* Titre ou header de la sidebar */ +/* sidebar header */ .sidebar-calendar h2 { font-size: 1.3rem; font-weight: 700; @@ -125,7 +123,7 @@ main { margin: 0 0 10px 0; } -/* Liste des calendriers */ +/* Calendar list */ .calendar-list { list-style: none; padding: 0; @@ -147,47 +145,11 @@ main { cursor: pointer; } -/* Couleurs distinctes pour chaque type de calendrier */ -.calendar-list li.work { - background-color: #d0e8ff; - color: #1a73e8; -} - -.calendar-list li.work:hover { - background-color: #b0d4ff; -} - -.calendar-list li.personal { - background-color: #ffe0e0; - color: #d93025; -} - -.calendar-list li.personal:hover { - background-color: #ffb3b3; -} - -/* Petit indicateur couleur à gauche */ -.calendar-list li::before { - content: ""; - width: 12px; - height: 12px; - border-radius: 50%; - flex-shrink: 0; -} - -.calendar-list li.work::before { - background-color: #1a73e8; -} - -.calendar-list li.personal::before { - background-color: #d93025; -} - .event-dot { margin-top: 2px; width: 6px; height: 6px; - background-color: #d93025; /* adjust to your theme */ + background-color: #d93025; border-radius: 50%; margin-left: auto; margin-right: auto; diff --git a/src/components/Calendar/Calendar.tsx b/src/components/Calendar/Calendar.tsx index d0e4b03..42c3403 100644 --- a/src/components/Calendar/Calendar.tsx +++ b/src/components/Calendar/Calendar.tsx @@ -148,7 +148,7 @@ export default function CalendarApp() { date.toLocaleDateString(locale, { weekday: "narrow" }) } @@ -160,7 +160,38 @@ export default function CalendarApp() { prevLabel={null} nextLabel={null} showNavigation={false} + tileClassName={({ date }) => { + const classNames: string[] = []; + + const today = new Date().setHours(0, 0, 0, 0); + if (date.getTime() === today) { + classNames.push("today"); + } + const selected = new Date(selectedDate); + selected.setHours(0, 0, 0, 0); + if (calendarRef.current?.view.type === "timeGridWeek") { + const startOfWeek = new Date(selected); + startOfWeek.setDate(selected.getDate() - selected.getDay()); // Sunday + startOfWeek.setHours(0, 0, 0, 0); + + const endOfWeek = new Date(startOfWeek); + endOfWeek.setDate(startOfWeek.getDate() + 6); // Saturday + endOfWeek.setHours(23, 59, 59, 999); + + if (date <= endOfWeek && date >= startOfWeek) { + classNames.push("selectedWeek"); + } + } + if ( + calendarRef.current?.view.type === "timeGridDay" && + date.getTime() === selected.getTime() + ) { + classNames.push("selectedWeek"); + } + return classNames; + }} tileContent={({ date }) => { + const classNames: string[] = []; const hasEvents = personnalEvents.some((event) => { const eventDate = new Date(event.start); return ( @@ -169,7 +200,11 @@ export default function CalendarApp() { eventDate.getDate() === date.getDate() ); }); - return hasEvents ?
: null; + if (hasEvents) { + classNames.push("event-dot"); + } + + return
; }} />