From 13e5e9dddf8880baa9dbac61bdd6e7c636cc3c43 Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Thu, 5 Nov 2026 10:29:45 -0500 Subject: [PATCH 1/9] Fixed leaflet-container rendering blank by giving parent (#campus-map div) an explicit height --- style/app.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/style/app.scss b/style/app.scss index a967783e3..859245e8d 100644 --- a/style/app.scss +++ b/style/app.scss @@ -337,6 +337,10 @@ ol { fill: var(--secondary-color); } +#campus-map { + height: 500px; +} + #map-sidebar { overflow-y: auto; padding: 0; From d50a24186b970884ac07ed4c77916e5e6de3c8b9 Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Thu, 5 Nov 2026 10:46:40 -0500 Subject: [PATCH 2/9] Fixed Zoom to Fit button vertically overlapping the map sidebar --- style/app.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/style/app.scss b/style/app.scss index 859245e8d..930e77651 100644 --- a/style/app.scss +++ b/style/app.scss @@ -344,6 +344,7 @@ ol { #map-sidebar { overflow-y: auto; padding: 0; + margin-top: 3.75rem; } .accordian-collapsed { From 8723caa4d2aacf2eaceb9596670503551b1605ad Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Thu, 5 Nov 2026 20:41:13 -0500 Subject: [PATCH 3/9] Added zoom to fit support for single marker case --- js/components/common/CampusMap.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/js/components/common/CampusMap.tsx b/js/components/common/CampusMap.tsx index 3ba6d6072..6c835d156 100644 --- a/js/components/common/CampusMap.tsx +++ b/js/components/common/CampusMap.tsx @@ -32,13 +32,16 @@ export function CampusMap({ if (!map || !mapGroup) return - // markerCount is the number of markers on the map. - // Only adjust the map bounds if there are multiple markers because the zoom will - // be strange for 1 marker. Note that the map will be centered on the single marker const markerCount = mapGroup.getLayers().length if (markerCount > 1) { + // Multiple markers: fit bounds to show all markers map.fitBounds(mapGroup.getBounds(), { padding: [30, 30] }) + } else if (markerCount === 1) { + // Single marker: reset to default zoom level 16 and center on the marker + const bounds = mapGroup.getBounds() + const center = bounds.getCenter() + map.setView(center, 16) } } From 516c4b81b45219bf6b861d870ae39bf984d7a71c Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Thu, 5 Nov 2026 20:44:55 -0500 Subject: [PATCH 4/9] Added back comment that I accidentally deleted --- js/components/common/CampusMap.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/js/components/common/CampusMap.tsx b/js/components/common/CampusMap.tsx index 6c835d156..79d487833 100644 --- a/js/components/common/CampusMap.tsx +++ b/js/components/common/CampusMap.tsx @@ -32,6 +32,7 @@ export function CampusMap({ if (!map || !mapGroup) return + // markerCount is the number of markers on the map. const markerCount = mapGroup.getLayers().length if (markerCount > 1) { From bb89f66a96abe045ecc8078b07d052dbc98273d0 Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Fri, 6 Nov 2026 09:55:44 -0500 Subject: [PATCH 5/9] Added test coverage --- .../common/__tests__/CampusMap.test.js | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 js/components/common/__tests__/CampusMap.test.js diff --git a/js/components/common/__tests__/CampusMap.test.js b/js/components/common/__tests__/CampusMap.test.js new file mode 100644 index 000000000..4f149bbe5 --- /dev/null +++ b/js/components/common/__tests__/CampusMap.test.js @@ -0,0 +1,161 @@ +import { render, screen } from "@testing-library/react" +import userEvent from "@testing-library/user-event" +import { CampusMap } from "../CampusMap" + +// Mock Leaflet to avoid DOM rendering issues in tests +jest.mock("leaflet", () => ({ + Icon: jest.fn(() => ({})), + Map: jest.fn(), + latLngBounds: jest.fn(() => ({ + extend: jest.fn(), + getCenter: jest.fn(() => ({ lat: 43.6596426, lng: -79.3976676 })), + })), + latLng: jest.fn((lat, lng) => ({ lat, lng })), +})) + +jest.mock("react-leaflet", () => ({ + MapContainer: ({ children }) =>
{children}
, + TileLayer: ({ url }) =>
, + FeatureGroup: ({ children }) =>
{children}
, + Marker: ({ position, children }) => ( +
+ {children} +
+ ), + Popup: ({ children }) =>
{children}
, +})) + +describe("CampusMap", () => { + const mockLecturesByBuilding = [ + { + buildingName: "Bahen Centre for Information Technology", + buildingCode: "BA", + address: "40 St George St", + lat: 43.6596426, + lng: -79.3976676, + postalCode: "M5S 2E4", + days: [ + { + dayString: "Monday", + dayOrder: 0, + timeframes: [ + { + courseCode: "CSC309H1", + session: "L5101", + startTime: 11, + endTime: 13, + room: "BA3200", + }, + ], + }, + ], + }, + ] + + it("renders the map container", () => { + render( + + ) + + const mapContainer = screen.getByTestId("map-container") + expect(mapContainer).toBeTruthy() + }) + + it("renders markers for each building", () => { + render( + + ) + + const markers = screen.getAllByTestId("marker") + expect(markers).toHaveLength(1) + }) + + it("renders multiple markers for multiple buildings", () => { + const multipleBuildings = [ + ...mockLecturesByBuilding, + { + buildingName: "Myhal Centre", + buildingCode: "MY", + address: "55 St George St", + lat: 43.6597, + lng: -79.3975, + postalCode: "M5S 2E5", + days: [ + { + dayString: "Tuesday", + dayOrder: 1, + timeframes: [ + { + courseCode: "CSC301H1", + session: "L0101", + startTime: 14, + endTime: 16, + room: "MY150", + }, + ], + }, + ], + }, + ] + + render( + + ) + + const markers = screen.getAllByTestId("marker") + expect(markers).toHaveLength(2) + }) + + it("renders Zoom to Fit button", () => { + render( + + ) + + const zoomButton = screen.getByText("Zoom to Fit") + expect(zoomButton).toBeTruthy() + }) + + it("zoom button is clickable", async () => { + const user = userEvent.setup() + render( + + ) + + const zoomButton = screen.getByText("Zoom to Fit") + await user.click(zoomButton) + + // Button should not throw errors when clicked + expect(zoomButton).toBeTruthy() + }) + + it("does not render markers for buildings without coordinates", () => { + const buildingsWithoutCoords = [ + { + buildingName: "Unknown Building", + buildingCode: "UNK", + address: "Unknown", + lat: undefined, + lng: undefined, + postalCode: "M5S 2E4", + days: [], + }, + ] + + render( + + ) + + const markers = screen.queryAllByTestId("marker") + expect(markers).toHaveLength(0) + }) + + it("renders popup component for markers", () => { + render( + + ) + + // Check that popup component is rendered + const popups = screen.getAllByTestId("popup") + expect(popups.length).toBeGreaterThan(0) + }) +}) From c600906c38134713e881065cf70746c3e6a73c8f Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Fri, 6 Nov 2026 10:02:03 -0500 Subject: [PATCH 6/9] Added changes to CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3ce9beb4..7794df185 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,8 +10,12 @@ ### 🐛 Bug fixes +- Fixed campus map not rendering in timetable modal by adding explicit height to `#campus-map` container +- Fixed "Zoom to Fit" button: resolved sidebar overlap and enabled view reset for single-marker maps + ### 🔧 Internal changes +- Added test coverage for `CampusMap` component (7 test cases covering rendering, markers, and zoom functionality) - Extracted read-only selection, prerequisite, and status computations (`isSelected`, `arePrereqsSatisfied*`, `compute*Status`, `totalSelectedCredits`) from `Graph.js` into a typed `graphState.ts` module - Fixed lint-staged pre-commit hook running Prettier and ESLint over the whole repository instead of only staged files, and added `pnpm-lock.yaml` to `.prettierignore` - Extracted the root `` setup (viewBox calculation, shared filters/markers) of `Graph.js` into a typed `GraphCanvas.tsx` component From 9117869d8f3f105d33d677c3de54abe4113d50c6 Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Fri, 6 Nov 2026 10:33:47 -0500 Subject: [PATCH 7/9] Fixed ESLint prop-types errors in CampusMap test mocks --- .../common/__tests__/CampusMap.test.js | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/js/components/common/__tests__/CampusMap.test.js b/js/components/common/__tests__/CampusMap.test.js index 4f149bbe5..52fec03fb 100644 --- a/js/components/common/__tests__/CampusMap.test.js +++ b/js/components/common/__tests__/CampusMap.test.js @@ -13,6 +13,7 @@ jest.mock("leaflet", () => ({ latLng: jest.fn((lat, lng) => ({ lat, lng })), })) +/* eslint-disable react/prop-types */ jest.mock("react-leaflet", () => ({ MapContainer: ({ children }) =>
{children}
, TileLayer: ({ url }) =>
, @@ -24,6 +25,7 @@ jest.mock("react-leaflet", () => ({ ), Popup: ({ children }) =>
{children}
, })) +/* eslint-enable react/prop-types */ describe("CampusMap", () => { const mockLecturesByBuilding = [ @@ -54,7 +56,10 @@ describe("CampusMap", () => { it("renders the map container", () => { render( - + ) const mapContainer = screen.getByTestId("map-container") @@ -63,7 +68,10 @@ describe("CampusMap", () => { it("renders markers for each building", () => { render( - + ) const markers = screen.getAllByTestId("marker") @@ -108,7 +116,10 @@ describe("CampusMap", () => { it("renders Zoom to Fit button", () => { render( - + ) const zoomButton = screen.getByText("Zoom to Fit") @@ -118,7 +129,10 @@ describe("CampusMap", () => { it("zoom button is clickable", async () => { const user = userEvent.setup() render( - + ) const zoomButton = screen.getByText("Zoom to Fit") @@ -142,7 +156,10 @@ describe("CampusMap", () => { ] render( - + ) const markers = screen.queryAllByTestId("marker") @@ -151,7 +168,10 @@ describe("CampusMap", () => { it("renders popup component for markers", () => { render( - + ) // Check that popup component is rendered From 7a34d0f3b0a8957648b28bd00dcb73d646369010 Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Fri, 6 Nov 2026 12:36:24 -0500 Subject: [PATCH 8/9] Trigger CI rebuild From 4a55a938ad712d5bf7d1ec9f01ff5c7a760a31e0 Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Sat, 7 Nov 2026 22:48:10 -0500 Subject: [PATCH 9/9] Alphabetized css rules in #map-sidebar --- style/app.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/style/app.scss b/style/app.scss index 930e77651..8344cbfc7 100644 --- a/style/app.scss +++ b/style/app.scss @@ -342,9 +342,9 @@ ol { } #map-sidebar { + margin-top: 3.75rem; overflow-y: auto; padding: 0; - margin-top: 3.75rem; } .accordian-collapsed {