Skip to content

Migrate Google Maps integration to @vis.gl/react-google-maps#1619

Open
kaligrafy wants to merge 1 commit into
chairemobilite:mainfrom
kaligrafy:migrateGoogleMapsLib
Open

Migrate Google Maps integration to @vis.gl/react-google-maps#1619
kaligrafy wants to merge 1 commit into
chairemobilite:mainfrom
kaligrafy:migrateGoogleMapsLib

Conversation

@kaligrafy
Copy link
Copy Markdown
Contributor

@kaligrafy kaligrafy commented May 23, 2026

Summary

Replaces @react-google-maps/api with @vis.gl/react-google-maps and rewrites the two Google map widgets (InputMapGoogle, InfoMapGoogle) on top of <APIProvider>/<Map> and the useMap()/useApiIsLoaded() hooks.

Addresses #453 (replace deprecated package). Sets the foundation for #1146 (replace deprecated PlacesService — separate follow-up) and #1156 (missing photos — same follow-up).

What changed

  • Deps: @react-google-maps/api removed, @vis.gl/react-google-maps (+ transitive @types/google.maps) added.
  • googleMaps.config.ts: now exposes the APIProvider config (apiKey/libraries/region/language) and a getGoogleMapId() helper that reads process.env.GOOGLE_MAP_ID.
  • Widgets rewritten:
    • <Map> runs uncontrolled (defaultCenter/defaultZoom); programmatic recentering goes through map.panTo(). This fixes the freezing-pan bug we hit when <Map> was given a controlled center prop.
    • Markers use <AdvancedMarker> when a Google Map ID is configured (vector maps), otherwise fall back to the legacy <Marker> so existing deployments without a Map ID keep working.
    • Polyline/Polygon are not provided by @vis.gl/react-google-maps, so InfoMap renders them via small imperative wrappers (MapPolyline/MapPolygon in GoogleMapOverlays.tsx) mounted on the current map via useMap().
    • withTranslation HOC → useTranslation hook in both widgets.
  • Bundle wiring: webpackCommon.ts forwards GOOGLE_MAP_ID into process.env.
  • tsconfig.json: includes google.maps in types (the global namespace was previously pulled in transitively by the old package).
  • .env.example: documents the optional GOOGLE_MAP_ID with links to the official Google docs (Map ID + AdvancedMarker migration).

Tests

Refactored existing geocoding tests to use parameterized test.each and a shared renderWidget helper (less duplication, easier to debug). Added one missing case in InputMapFindPlace:

Multi-result list → user picks a non-default option → confirms → the chosen place_id is what gets saved.

This covers the legal-compliance path (place_id preservation under Google ToS / OIQ data-retention requirements) for the multi-select branch, which was previously only covered for the single-result auto-select branch.

All map tests pass: 13/13 (vs 17 before; coverage equal-or-better thanks to parameterization).

Coded by Claude Opus 4.7, reviewed and tested by @kaligrafy

Out of scope (follow-ups)

  • #1146 deprecation of google.maps.places.PlacesService → replace with google.maps.places.Place.searchByText in GoogleGeocoder.tsx.
  • #1156 missing photos → pre-compute photo URLs in the geocoder and tighten placeData.photos to string[].
  • GoogleGeocoder parameterized unit tests.
  • README / CHANGELOG updates.
  • #888 OSM/MapLibre provider — unblocked by the existing MapProviderAdapter.

Test plan

  • Frontend builds (yarn compile in evolution-frontend).
  • Map tests pass (yarn test --testPathPatterns="InputMap|InfoMap").
  • Manual: home location renders, marker shows, user can pan/zoom (works locally with GOOGLE_MAP_ID set).
  • Manual: trip InfoMap renders polylines/polygons.
  • Manual: deployments without GOOGLE_MAP_ID still render maps via the legacy <Marker> fallback.

Summary by CodeRabbit

  • New Features

    • Optional GOOGLE_MAP_ID to enable vector maps and enhanced markers (AdvancedMarker)
    • Added polyline and polygon overlays for richer map visuals
  • Behavior Changes

    • Map rendering migrated to a new Google Maps implementation (vector maps when Map ID provided); automatic fallback to legacy markers when not
  • Tests

    • Geocoding/map input tests refactored and made more robust

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 23, 2026

Walkthrough

This PR migrates Google map widgets from @react-google-maps/api to @vis.gl/react-google-maps, adds an optional GOOGLE_MAP_ID env variable to enable vector maps/AdvancedMarker, refactors googleMaps.config to a typed GoogleMapApiConfig with getGoogleMapId(), adds MapPolyline/MapPolygon wrappers, rewrites InfoMapGoogle and InputMapGoogle to use APIProvider/Map and vis.gl hooks (with conditional AdvancedMarker), and consolidates geocoding tests with shared helpers and parameterized cases.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • tahini
  • samuel-duhaime
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly summarizes the primary change: migrating the Google Maps library from @react-google-maps/api to @vis.gl/react-google-maps, which is the core objective of this PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@kaligrafy kaligrafy requested review from samuel-duhaime and tahini and removed request for tahini May 23, 2026 13:45
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/evolution-frontend/src/components/inputs/maps/google/GoogleMapOverlays.tsx`:
- Around line 30-42: When creating overlays in the map-mount effect (the
React.useEffect that runs when map becomes non-null) include the current options
so initial style/path isn't missed: construct the Polyline/overlay with both map
and options or call polylineRef.current.setOptions(options) immediately after
assigning polylineRef.current. Update the effect that creates the overlay (the
one that constructs google.maps.Polyline and assigns polylineRef.current) to
apply the provided options from the options variable (and do the same for any
other overlay creation effects around the other block that creates overlays),
ensuring polylineRef.current is initialized with options on mount.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 53e94673-809a-49b1-b577-75d61088b281

📥 Commits

Reviewing files that changed from the base of the PR and between d10660b and 1fb28c1.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (10)
  • .env.example
  • packages/evolution-frontend/package.json
  • packages/evolution-frontend/src/components/inputs/__tests__/InputMapFindPlace.test.tsx
  • packages/evolution-frontend/src/components/inputs/__tests__/InputMapPoint.test.tsx
  • packages/evolution-frontend/src/components/inputs/maps/google/GoogleMapOverlays.tsx
  • packages/evolution-frontend/src/components/inputs/maps/google/InfoMapGoogle.tsx
  • packages/evolution-frontend/src/components/inputs/maps/google/InputMapGoogle.tsx
  • packages/evolution-frontend/src/config/googleMaps.config.ts
  • packages/evolution-frontend/src/utils/dev/webpackCommon.ts
  • packages/evolution-frontend/tsconfig.json
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: test-sequential (24.x)
  • GitHub Check: check-format
  • GitHub Check: build-and-test (24.x)
  • GitHub Check: generate-and-build-generator (24.x)
  • GitHub Check: pr-build-check
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2026-04-10T18:04:30.397Z
Learnt from: tahini
Repo: chairemobilite/evolution PR: 1491
File: packages/evolution-common/src/services/questionnaire/sections/visitedPlaces/widgetNextPlaceCategory.ts:25-31
Timestamp: 2026-04-10T18:04:30.397Z
Learning: In `packages/evolution-common/src/services/odSurvey/helpers.ts`, ensure `getHomeAddressOneLine` and the internal `getAddressOneLine` HTML-escape every address field (e.g., via lodash `_escape`) before assembling the returned one-line HTML string. Put the escaping in these helpers so all callers automatically receive a safe HTML string, and avoid requiring individual call sites to perform escaping.

Applied to files:

  • .env.example
📚 Learning: 2026-04-17T16:19:46.635Z
Learnt from: samuel-duhaime
Repo: chairemobilite/evolution PR: 1505
File: packages/evolution-generator/src/scripts/generate_choices.py:189-202
Timestamp: 2026-04-17T16:19:46.635Z
Learning: In packages/evolution-generator/src/scripts/generate_choices.py, when generating i18next translation keys for choices, do not escape dots in the choice value. Build translation keys using the literal format `choices:<choiceName>.<value>` (e.g., `choices:<choiceName>.some.value`) so that dot-containing values remain unmodified and match the existing i18next key convention. Do not add extra escaping logic just to handle dots.

Applied to files:

  • .env.example
📚 Learning: 2026-03-31T18:20:32.322Z
Learnt from: kaligrafy
Repo: chairemobilite/evolution PR: 1462
File: packages/evolution-common/src/services/baseObjects/Completable.ts:1-6
Timestamp: 2026-03-31T18:20:32.322Z
Learning: In this repository (chairemobilite/evolution), do not flag or require a copyright year in file headers. For new files, the header format `Copyright Polytechnique Montreal and contributors` (without a year) is acceptable. Only verify the header presence/consistency, not the presence of the year.

Applied to files:

  • packages/evolution-frontend/src/components/inputs/maps/google/GoogleMapOverlays.tsx
  • packages/evolution-frontend/src/utils/dev/webpackCommon.ts
  • packages/evolution-frontend/src/config/googleMaps.config.ts
  • packages/evolution-frontend/src/components/inputs/__tests__/InputMapFindPlace.test.tsx
  • packages/evolution-frontend/src/components/inputs/maps/google/InfoMapGoogle.tsx
  • packages/evolution-frontend/src/components/inputs/__tests__/InputMapPoint.test.tsx
  • packages/evolution-frontend/src/components/inputs/maps/google/InputMapGoogle.tsx
🔇 Additional comments (9)
packages/evolution-frontend/src/components/inputs/__tests__/InputMapFindPlace.test.tsx (1)

158-338: LGTM!

packages/evolution-frontend/src/components/inputs/__tests__/InputMapPoint.test.tsx (1)

9-9: LGTM!

Also applies to: 30-30, 36-36, 44-46, 58-58, 75-77, 88-88, 90-90, 110-112, 136-176

.env.example (1)

21-27: LGTM!

packages/evolution-frontend/package.json (1)

40-40: LGTM!

packages/evolution-frontend/tsconfig.json (1)

8-11: LGTM!

packages/evolution-frontend/src/utils/dev/webpackCommon.ts (1)

113-113: LGTM!

packages/evolution-frontend/src/config/googleMaps.config.ts (1)

9-20: LGTM!

Also applies to: 25-44, 46-54

packages/evolution-frontend/src/components/inputs/maps/google/InfoMapGoogle.tsx (1)

2-27: LGTM!

Also applies to: 33-345

packages/evolution-frontend/src/components/inputs/maps/google/InputMapGoogle.tsx (1)

2-27: LGTM!

Also applies to: 35-44, 48-49, 66-110, 112-217, 260-340

@kaligrafy
Copy link
Copy Markdown
Contributor Author

Reviewer / deployer note: setting up a Google Map ID

To take advantage of the new vector maps and AdvancedMarker (and to silence the deprecation warning logged in the browser console for the legacy Marker), each deployment needs to create a Map ID in Google Cloud and set it as GOOGLE_MAP_ID in .env.

How to create one

  1. Open the Google Cloud Console → Map Management (the same project that owns the GOOGLE_API_KEY).
  2. Click Create Map ID.
  3. Pick JavaScript as the platform and Vector as the map type (Raster also works but you lose advanced markers and tilt/rotation).
  4. Optionally associate a Map style (cloud-based styling).
  5. Copy the generated ID and add it to .env:
    GOOGLE_MAP_ID = "<myGoogleMapId>"
    
  6. Rebuild the frontend bundle (the value is injected at build time via webpackCommon.ts).

Official docs:

What happens without a Map ID

The widgets still work — they fall back to the legacy raster <Map> + google.maps.Marker. Existing deployments are not broken by this PR; they just won't get the new rendering until a Map ID is added.

Restrictions

When creating the Map ID, you can restrict it to specific HTTP referrers (same as the API key) to prevent reuse on other domains. Recommended for production.

@kaligrafy kaligrafy force-pushed the migrateGoogleMapsLib branch from 1fb28c1 to 97ba2c2 Compare May 23, 2026 13:56
Copy link
Copy Markdown
Contributor

@tahini tahini left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quelques commentaires, globalement, ça me semble bien, si ça a été bien testé.

1- Svp squasher les commits dans un seul, ils ne semblent pas indépendants. Par exemple, le commit qui ne fait que swapper les packages ne doit pas fonctionner tout seul...
2- Ajouter dans le CHANGELOG.md un one-liner qui explique les implications de ça (créer un nouvel ID changer une valeur dans le .env, on pourra éventuellement faire un lien au commit ID une fois la PR mergée, si le commit a plus de détails). Par exemple, faudra-t-il donner des instructiosn spécifiques à nos partenaires qui ont l'habitude de nous fournir une clé Google?
3- Aussi ajouter au changelog.md, sous dev le changement de package.

Comment thread packages/evolution-frontend/src/components/inputs/maps/google/InfoMapGoogle.tsx Outdated
Comment thread .env.example Outdated
Replaces the deprecated @react-google-maps/api package with
@vis.gl/react-google-maps and rewrites the Google map widgets
(InputMapGoogle, InfoMapGoogle) on top of <APIProvider>/<Map> and the
useMap()/useApiIsLoaded() hooks (fixes chairemobilite#453).

Maps run uncontrolled (defaultCenter) and recenter via map.panTo() so user
pan is no longer fought by re-renders. Markers always use <AdvancedMarker>;
the legacy <Marker> fallback was dropped, so a Google Map ID is now required.
Polyline/Polygon are not provided by @vis.gl/react-google-maps, so InfoMap
renders them through small imperative helpers (MapPolyline/MapPolygon)
mounted via useMap(), initialized with their options on mount.

Config / build:
- googleMaps.config.ts exposes the APIProvider config and getGoogleMapId().
- webpackCommon forwards GOOGLE_MAP_ID into the frontend bundle.
- tsconfig includes @types/google.maps (was transitive in the old package).
- .env.example documents the now-required GOOGLE_MAP_ID with official links.

Tests:
- Parameterize the geocoding flows in InputMapPoint and InputMapFindPlace and
  share a renderWidget helper, while keeping the original inline comments.
- Add a case covering "user picks a non-default option from the multi-result
  list, confirms, and the chosen place_id is what gets saved" (legal
  compliance: place_id must be preserved).
@kaligrafy kaligrafy force-pushed the migrateGoogleMapsLib branch from 97ba2c2 to e63981d Compare May 29, 2026 19:09
@kaligrafy kaligrafy requested a review from tahini May 29, 2026 19:18
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/evolution-frontend/src/utils/dev/webpackCommon.ts (1)

107-114: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use params.env for GOOGLE_MAP_ID too.

CommonWebpackConfigParams.env is documented to override process.env, but this new entry ignores it. Builds that pass the map id through the webpack helper/env arg will silently miss mapId.

Suggested fix
         'process.env': {
             IS_BROWSER: JSON.stringify(true),
             HOST: JSON.stringify(process.env.HOST),
             APP_NAME: JSON.stringify('survey'),
             IS_TESTING: JSON.stringify(currentNodeEnv === 'test'),
             GOOGLE_API_KEY: JSON.stringify(process.env.GOOGLE_API_KEY),
-            GOOGLE_MAP_ID: JSON.stringify(process.env.GOOGLE_MAP_ID),
+            GOOGLE_MAP_ID: JSON.stringify(params.env.GOOGLE_MAP_ID ?? process.env.GOOGLE_MAP_ID),
             ...stringifyEnvValues(params.extraEnvs)
         },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/evolution-frontend/src/utils/dev/webpackCommon.ts` around lines 107
- 114, The build injects GOOGLE_MAP_ID from process.env but should honor
CommonWebpackConfigParams.env overrides; update the 'process.env' mapping for
GOOGLE_MAP_ID in webpackCommon.ts to read from params.env first (e.g. use
params.env?.GOOGLE_MAP_ID or equivalent) and fall back to
process.env.GOOGLE_MAP_ID, ensuring the value is JSON.stringify'd like the other
entries; reference the mapping block that includes IS_BROWSER, HOST, APP_NAME,
IS_TESTING, GOOGLE_API_KEY, GOOGLE_MAP_ID and the stringifyEnvValues helper so
the override behavior matches params.extraEnvs/params.env.
packages/evolution-frontend/src/components/inputs/maps/google/InfoMapGoogle.tsx (1)

91-106: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Include LineString coordinates in bounds fitting.

boundCoords ignores geojsons.linestrings, so a widget that only renders routes never reaches fitBounds and opens at the default center instead of the geometry.

Suggested fix
     const boundCoords = React.useMemo<{ lat: number; lng: number }[]>(() => {
         const coords: { lat: number; lng: number }[] = [];
         for (const point of geojsons.points?.features || []) {
             coords.push({ lat: point.geometry.coordinates[1], lng: point.geometry.coordinates[0] });
         }
+        for (const linestring of geojsons.linestrings?.features || []) {
+            for (const [lng, lat] of linestring.geometry.coordinates) {
+                coords.push({ lat, lng });
+            }
+        }
         for (const polygon of geojsons.polygons?.features || []) {
             const { minLat, maxLat, minLong, maxLong } = polygon.properties;
             if (minLat !== undefined && maxLat !== undefined && minLong !== undefined && maxLong !== undefined) {
                 coords.push({ lat: minLat, lng: minLong });
                 coords.push({ lat: maxLat, lng: maxLong });

Also applies to: 110-117

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/evolution-frontend/src/components/inputs/maps/google/InfoMapGoogle.tsx`
around lines 91 - 106, boundCoords currently only collects points and polygon
corner coordinates, so LineString geometries in geojsons.linestrings are ignored
and the map never calls fitBounds for routes; update the logic in the
boundCoords useMemo to also iterate over geojsons.linestrings?.features and for
each LineString feature push each coordinate as { lat: coord[1], lng: coord[0] }
into coords, and make the same update for the other similar bounds collection
block (the second boundCoords-like section) so LineString routes are included
when computing fitBounds.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/evolution-frontend/src/components/inputs/maps/google/InfoMapGoogle.tsx`:
- Around line 37-44: InfoMarker currently always renders AdvancedMarker which
fails when getGoogleMapId() is undefined; change InfoMarker to conditionally
render AdvancedMarker only when getGoogleMapId() returns a value and fall back
to the legacy Marker component otherwise (keep same props and icon handling).
Also update the bounds calculation that builds boundCoords (where boundCoords
currently includes geojsons.points and geojsons.polygons) to include coordinates
extracted from geojsons.linestrings (flatten polyline coordinates into the same
array) so fitBounds will include route-only widgets rendered as polylines.

In
`@packages/evolution-frontend/src/components/inputs/maps/google/InputMapGoogle.tsx`:
- Around line 69-85: MapMarker currently always returns <AdvancedMarker>; change
it to check for the presence of the mapId (prop or context value you use for the
map) and render <AdvancedMarker> only when mapId is defined, otherwise render
the legacy marker component (e.g., <Marker> or the google.maps.Marker wrapper
you use). Ensure both branches pass the same semantic values: position built
from markerData.position, draggable from markerData.draggable, onDragEnd wired
to markerData.draggable ? onDragEnd : undefined, onClick to markerData.onClick,
and the icon (markerData.icon.url/size) so behavior matches the AdvancedMarker
fallback. Update MapMarker to reference AdvancedMarker and the legacy Marker
branch accordingly.

---

Outside diff comments:
In
`@packages/evolution-frontend/src/components/inputs/maps/google/InfoMapGoogle.tsx`:
- Around line 91-106: boundCoords currently only collects points and polygon
corner coordinates, so LineString geometries in geojsons.linestrings are ignored
and the map never calls fitBounds for routes; update the logic in the
boundCoords useMemo to also iterate over geojsons.linestrings?.features and for
each LineString feature push each coordinate as { lat: coord[1], lng: coord[0] }
into coords, and make the same update for the other similar bounds collection
block (the second boundCoords-like section) so LineString routes are included
when computing fitBounds.

In `@packages/evolution-frontend/src/utils/dev/webpackCommon.ts`:
- Around line 107-114: The build injects GOOGLE_MAP_ID from process.env but
should honor CommonWebpackConfigParams.env overrides; update the 'process.env'
mapping for GOOGLE_MAP_ID in webpackCommon.ts to read from params.env first
(e.g. use params.env?.GOOGLE_MAP_ID or equivalent) and fall back to
process.env.GOOGLE_MAP_ID, ensuring the value is JSON.stringify'd like the other
entries; reference the mapping block that includes IS_BROWSER, HOST, APP_NAME,
IS_TESTING, GOOGLE_API_KEY, GOOGLE_MAP_ID and the stringifyEnvValues helper so
the override behavior matches params.extraEnvs/params.env.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4cdfbcb4-5324-4370-bbf3-f4970d7587cb

📥 Commits

Reviewing files that changed from the base of the PR and between 1fb28c1 and e63981d.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (11)
  • .env.example
  • CHANGELOG.md
  • packages/evolution-frontend/package.json
  • packages/evolution-frontend/src/components/inputs/__tests__/InputMapFindPlace.test.tsx
  • packages/evolution-frontend/src/components/inputs/__tests__/InputMapPoint.test.tsx
  • packages/evolution-frontend/src/components/inputs/maps/google/GoogleMapOverlays.tsx
  • packages/evolution-frontend/src/components/inputs/maps/google/InfoMapGoogle.tsx
  • packages/evolution-frontend/src/components/inputs/maps/google/InputMapGoogle.tsx
  • packages/evolution-frontend/src/config/googleMaps.config.ts
  • packages/evolution-frontend/src/utils/dev/webpackCommon.ts
  • packages/evolution-frontend/tsconfig.json
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: generate-and-build-generator (24.x)
  • GitHub Check: pr-build-check
  • GitHub Check: code-lint
  • GitHub Check: build-and-test (24.x)
  • GitHub Check: test-sequential (24.x)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2026-04-10T18:04:30.397Z
Learnt from: tahini
Repo: chairemobilite/evolution PR: 1491
File: packages/evolution-common/src/services/questionnaire/sections/visitedPlaces/widgetNextPlaceCategory.ts:25-31
Timestamp: 2026-04-10T18:04:30.397Z
Learning: In `packages/evolution-common/src/services/odSurvey/helpers.ts`, ensure `getHomeAddressOneLine` and the internal `getAddressOneLine` HTML-escape every address field (e.g., via lodash `_escape`) before assembling the returned one-line HTML string. Put the escaping in these helpers so all callers automatically receive a safe HTML string, and avoid requiring individual call sites to perform escaping.

Applied to files:

  • .env.example
📚 Learning: 2026-04-17T16:19:46.635Z
Learnt from: samuel-duhaime
Repo: chairemobilite/evolution PR: 1505
File: packages/evolution-generator/src/scripts/generate_choices.py:189-202
Timestamp: 2026-04-17T16:19:46.635Z
Learning: In packages/evolution-generator/src/scripts/generate_choices.py, when generating i18next translation keys for choices, do not escape dots in the choice value. Build translation keys using the literal format `choices:<choiceName>.<value>` (e.g., `choices:<choiceName>.some.value`) so that dot-containing values remain unmodified and match the existing i18next key convention. Do not add extra escaping logic just to handle dots.

Applied to files:

  • .env.example
📚 Learning: 2026-03-31T18:20:32.322Z
Learnt from: kaligrafy
Repo: chairemobilite/evolution PR: 1462
File: packages/evolution-common/src/services/baseObjects/Completable.ts:1-6
Timestamp: 2026-03-31T18:20:32.322Z
Learning: In this repository (chairemobilite/evolution), do not flag or require a copyright year in file headers. For new files, the header format `Copyright Polytechnique Montreal and contributors` (without a year) is acceptable. Only verify the header presence/consistency, not the presence of the year.

Applied to files:

  • packages/evolution-frontend/src/utils/dev/webpackCommon.ts
  • packages/evolution-frontend/src/components/inputs/__tests__/InputMapFindPlace.test.tsx
  • packages/evolution-frontend/src/components/inputs/maps/google/GoogleMapOverlays.tsx
  • packages/evolution-frontend/src/components/inputs/__tests__/InputMapPoint.test.tsx
  • packages/evolution-frontend/src/config/googleMaps.config.ts
  • packages/evolution-frontend/src/components/inputs/maps/google/InfoMapGoogle.tsx
  • packages/evolution-frontend/src/components/inputs/maps/google/InputMapGoogle.tsx
🪛 dotenv-linter (4.0.0)
.env.example

[warning] 26-26: [SpaceCharacter] The line has spaces around equal sign

(SpaceCharacter)

🔇 Additional comments (2)
packages/evolution-frontend/src/components/inputs/__tests__/InputMapFindPlace.test.tsx (1)

93-95: LGTM!

Also applies to: 158-351

packages/evolution-frontend/src/components/inputs/__tests__/InputMapPoint.test.tsx (1)

9-9: LGTM!

Also applies to: 30-36, 44-45, 58-58, 71-90, 107-178

Comment on lines +37 to +44
const InfoMarker: React.FC<{
position: google.maps.LatLngLiteral;
icon?: { url: string; size: [number, number] };
}> = ({ position, icon }) => (
<AdvancedMarker position={position}>
{icon && <img src={icon.url} width={icon.size[0]} height={icon.size[1]} alt="" />}
</AdvancedMarker>
);
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="packages/evolution-frontend/src/components/inputs/maps/google/InfoMapGoogle.tsx"

echo "== File = $FILE"
ls -l "$FILE"
echo

echo "== Lines 1-120"
sed -n '1,120p' "$FILE" | cat -n

echo
echo "== Lines 120-200"
sed -n '120,200p' "$FILE" | cat -n

echo
echo "== Lines 200-320"
sed -n '200,320p' "$FILE" | cat -n

echo
echo "== Search for getGoogleMapId and boundCoords"
rg -n "getGoogleMapId|boundCoords|linestring|bounds" "$FILE"

echo
echo "== Snippet around InfoMarker definition"
rg -n "const InfoMarker" -n "$FILE"
sed -n '30,70p' "$FILE" | cat -n

Repository: chairemobilite/evolution

Length of output: 18894


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Inspect getGoogleMapId implementation
rg -n "function getGoogleMapId|getGoogleMapId\s*=" -S packages/evolution-frontend/src/config || true
rg -n "getGoogleMapId" -S packages/evolution-frontend/src/config/googleMaps.config || true
ls -l packages/evolution-frontend/src/config/googleMaps.config || true
sed -n '1,200p' packages/evolution-frontend/src/config/googleMaps.config | cat -n || true

# 2) Find other Google map components for "no GOOGLE_MAP_ID" / <Marker> fallback patterns
rg -n "GOOGLE_MAP_ID|AdvancedMarker" packages/evolution-frontend/src/components/inputs/maps/google -S
rg -n "<Marker|\\bMarker\\b" packages/evolution-frontend/src/components/inputs/maps/google -S

# 3) Look for conditional marker rendering in the codebase
rg -n "mapId\\s*\\?|GOOGLE_MAP_ID.*fallback|AdvancedMarker.*mapId" packages/evolution-frontend/src -S

Repository: chairemobilite/evolution

Length of output: 2188


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect InputMapGoogle.tsx for marker fallback patterns
FILE="packages/evolution-frontend/src/components/inputs/maps/google/InputMapGoogle.tsx"
ls -l "$FILE"
sed -n '1,220p' "$FILE" | cat -n
sed -n '220,520p' "$FILE" | cat -n

# Search for conditional rendering based on mapId
rg -n "getGoogleMapId\\(|mapId\\b|GOOGLE_MAP_ID|AdvancedMarker\\b|\\<Marker\\b|\\bMarker\\b" \
  packages/evolution-frontend/src/components/inputs/maps/google -S

# Search for Marker import usage across whole evolution-frontend
rg -n "from '`@vis`\\.gl/react-google-maps'.*Marker|\\bMarker\\b\\s*," packages/evolution-frontend/src -S

Repository: chairemobilite/evolution

Length of output: 14813


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect googleMaps.config.ts implementation
FILE="packages/evolution-frontend/src/config/googleMaps.config.ts"
ls -l "$FILE"
sed -n '1,120p' "$FILE" | cat -n
rg -n "getGoogleMapId" "$FILE" -n

# Search for Marker imports/usages in google maps inputs
rg -n "from '`@vis`\\.gl/react-google-maps'|\\bMarker\\b" packages/evolution-frontend/src/components/inputs/maps/google -S

# Search for any Marker usage anywhere in evolution-frontend
rg -n "\\bMarker\\b" packages/evolution-frontend/src/components/inputs/maps -S

Repository: chairemobilite/evolution

Length of output: 3418


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect googleMaps.config.ts implementation
FILE="packages/evolution-frontend/src/config/googleMaps.config.ts"
ls -l "$FILE"
sed -n '1,120p' "$FILE" | cat -n
rg -n "getGoogleMapId" "$FILE" -n

# Search for Marker imports/usages in google maps inputs
rg -n "from '`@vis`\\.gl/react-google-maps'|\\bMarker\\b" packages/evolution-frontend/src/components/inputs/maps/google -S

# Search for any Marker usage anywhere in evolution-frontend
rg -n "\\bMarker\\b" packages/evolution-frontend/src/components/inputs/maps -S

Repository: chairemobilite/evolution

Length of output: 3418


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "\bMarker\b" packages/evolution-frontend/src/components/inputs/maps/google -S
rg -n "from '`@vis`\.gl/react-google-maps'[\s\S]*Marker" packages/evolution-frontend/src/components/inputs/maps/google -S || true
rg -n "import\s*{[^}]*\bMarker\b[^}]*}\s*from\s*'`@vis`\.gl/react-google-maps'" packages/evolution-frontend/src -S || true

Repository: chairemobilite/evolution

Length of output: 421


🏁 Script executed:

#!/bin/bash
set -euo pipefail
set -x

# Look for "Marker" identifier usage in the Google maps inputs folder
rg -n "\bMarker\b" packages/evolution-frontend/src/components/inputs/maps/google -S

# Look for imports from vis-gl that include Marker
rg -n "from '`@vis`\.gl/react-google-maps'" packages/evolution-frontend/src/components/inputs/maps/google -S
rg -n "Marker" packages/evolution-frontend/src/components/inputs/maps/google -S

# Check overlays file(s) too
ls -1 packages/evolution-frontend/src/components/inputs/maps/google
rg -n "\bMarker\b" packages/evolution-frontend/src/components/inputs/maps/google/GoogleMapOverlays.tsx -S || true
sed -n '1,220p' packages/evolution-frontend/src/components/inputs/maps/google/GoogleMapOverlays.tsx | cat -n

Repository: chairemobilite/evolution

Length of output: 8059


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE="packages/evolution-frontend/src/components/inputs/maps/google/InfoMapGoogle.tsx"
rg -n "<InfoMarker|boundCoords|linestrings|fitBounds|getGoogleMapId" "$FILE" -S

Repository: chairemobilite/evolution

Length of output: 675


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "from '`@vis`\.gl/react-google-maps'|from \"`@vis`\.gl/react-google-maps\"" -S packages/evolution-frontend/src
rg -n "Marker" packages/evolution-frontend/src -S

Repository: chairemobilite/evolution

Length of output: 11596


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE="packages/evolution-frontend/src/components/admin/validations/InterviewMap.tsx"
ls -l "$FILE"
sed -n '1,140p' "$FILE" | cat -n
sed -n '520,620p' "$FILE" | cat -n

Repository: chairemobilite/evolution

Length of output: 12579


Add legacy marker fallback when GOOGLE_MAP_ID is missing (and fix bounds for route-only widgets)

  • InfoMarker always renders <AdvancedMarker>, but getGoogleMapId() returns undefined when GOOGLE_MAP_ID isn’t set; this needs a fallback to the legacy <Marker> so point markers still render.
  • boundCoords includes only geojsons.points + geojsons.polygons; geojsons.linestrings are rendered as polylines but excluded from fitBounds, so route-only widgets may not zoom to the route.
Suggested marker fallback
 import {
     APIProvider,
     AdvancedMarker,
     Map,
+    Marker,
     MapCameraChangedEvent,
     useApiIsLoaded,
     useMap
 } from '`@vis.gl/react-google-maps`';
@@
 const InfoMarker: React.FC<{
     position: google.maps.LatLngLiteral;
     icon?: { url: string; size: [number, number] };
-}> = ({ position, icon }) => (
-    <AdvancedMarker position={position}>
-        {icon && <img src={icon.url} width={icon.size[0]} height={icon.size[1]} alt="" />}
-    </AdvancedMarker>
-);
+    mapId?: string;
+}> = ({ position, icon, mapId }) =>
+    mapId ? (
+        <AdvancedMarker position={position}>
+            {icon && <img src={icon.url} width={icon.size[0]} height={icon.size[1]} alt="" />}
+        </AdvancedMarker>
+    ) : (
+        <Marker
+            position={position}
+            icon={
+                icon
+                    ? {
+                          url: icon.url,
+                          scaledSize: new google.maps.Size(icon.size[0], icon.size[1])
+                      }
+                    : undefined
+            }
+        />
+    );
@@
-    const title =
+    const mapId = getGoogleMapId();
+    const title =
         surveyHelper.translateString(props.widgetConfig.title, i18n, props.interview, props.path, props.user) || '';
@@
             <InfoMarker
                 key={`gMarker_infoMap_${props.path}__${i}`}
                 position={{ lat: point.geometry.coordinates[1], lng: point.geometry.coordinates[0] }}
                 icon={point.properties.icon}
+                mapId={mapId}
             />
         );
     }
@@
-    const mapId = getGoogleMapId();
-
     return (
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/evolution-frontend/src/components/inputs/maps/google/InfoMapGoogle.tsx`
around lines 37 - 44, InfoMarker currently always renders AdvancedMarker which
fails when getGoogleMapId() is undefined; change InfoMarker to conditionally
render AdvancedMarker only when getGoogleMapId() returns a value and fall back
to the legacy Marker component otherwise (keep same props and icon handling).
Also update the bounds calculation that builds boundCoords (where boundCoords
currently includes geojsons.points and geojsons.polygons) to include coordinates
extracted from geojsons.linestrings (flatten polyline coordinates into the same
array) so fitBounds will include route-only widgets rendered as polylines.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

legacy removed completely, easier.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kaligrafy Got it — dropping legacy Marker entirely and requiring GOOGLE_MAP_ID is the cleaner approach. 👍

One remaining point from the original comment: boundCoords still excludes geojsons.linestrings, so fitBounds won't zoom to routes on route-only widgets. Would you like to open a follow-up issue for that, or address it in this PR?


🧠 Learnings used
Learnt from: kaligrafy
Repo: chairemobilite/evolution PR: 1462
File: packages/evolution-common/src/services/baseObjects/Completable.ts:1-6
Timestamp: 2026-03-31T18:20:32.322Z
Learning: In this repository (chairemobilite/evolution), do not flag or require a copyright year in file headers. For new files, the header format `Copyright Polytechnique Montreal and contributors` (without a year) is acceptable. Only verify the header presence/consistency, not the presence of the year.

Comment on lines +69 to +85
const MapMarker: React.FunctionComponent<{
markerData: MarkerData;
onDragEnd?: (e: google.maps.MapMouseEvent) => void;
}> = ({ markerData, onDragEnd }) => {
const position = {
lat: markerData.position.geometry.coordinates[1],
lng: markerData.position.geometry.coordinates[0]
};
return (
<AdvancedMarker
position={position}
draggable={markerData.draggable}
onDragEnd={markerData.draggable ? onDragEnd : undefined}
onClick={markerData.onClick}
>
<img src={markerData.icon.url} width={markerData.icon.size[0]} height={markerData.icon.size[1]} alt="" />
</AdvancedMarker>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Does Google Maps AdvancedMarkerElement require a Map ID, and what is the recommended fallback in @vis.gl/react-google-maps when mapId is undefined?

💡 Result:

Yes. Google requires a mapId to use AdvancedMarkerElement: “A map ID is required to use Advanced Markers… Set a map ID in map options” [1][2]. If the mapId is missing, Advanced Markers cannot load: “Advanced markers requires a map ID. If the map ID is missing, advanced markers cannot load.” [2]. In @vis.gl/react-google-maps, the component is “only… on maps using cloud-based map styling (i.e. the Map-component has a mapId specified)” [3]. So when mapId is undefined, the recommended fallback is to use a plain/raster map instead: the component docs state that if mapId is not set, the cloud config will fall back to raster as needed (“vector maps may not be available… and the map will fall back to a raster map as needed”) [4], but you should not expect to work without mapId [3]. Practical implication: if you need AdvancedMarker, pass mapId (e.g. DEMO_MAP_ID for testing) to the . Otherwise, render markers using something that does not depend on AdvancedMarker/cloud styling.

Citations:


Restore legacy marker fallback when GOOGLE_MAP_ID/mapId is missing

Google Advanced Markers require a mapId, and @vis.gl/react-google-maps notes <AdvancedMarker> only works on maps with a mapId. MapMarker currently renders <AdvancedMarker> unconditionally (lines 69-85), so the promised no-GOOGLE_MAP_ID fallback isn’t implemented—add conditional rendering to use the legacy marker when mapId is undefined.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/evolution-frontend/src/components/inputs/maps/google/InputMapGoogle.tsx`
around lines 69 - 85, MapMarker currently always returns <AdvancedMarker>;
change it to check for the presence of the mapId (prop or context value you use
for the map) and render <AdvancedMarker> only when mapId is defined, otherwise
render the legacy marker component (e.g., <Marker> or the google.maps.Marker
wrapper you use). Ensure both branches pass the same semantic values: position
built from markerData.position, draggable from markerData.draggable, onDragEnd
wired to markerData.draggable ? onDragEnd : undefined, onClick to
markerData.onClick, and the icon (markerData.icon.url/size) so behavior matches
the AdvancedMarker fallback. Update MapMarker to reference AdvancedMarker and
the legacy Marker branch accordingly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be fixed by removing old method/markers

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kaligrafy Thanks for the clarification. Just to confirm the intent: by removing old markers entirely and always using <AdvancedMarker>, GOOGLE_MAP_ID becomes effectively required for markers to render. Deployments that don't set it will silently get no markers (or a Google Maps error), rather than falling back to raster <Marker>.

If that's the intentional breaking change (and it's documented in the CHANGELOG), that's fine — just worth making sure .env.example and the CHANGELOG clearly state that GOOGLE_MAP_ID is required (not optional) for markers to work, not just needed for vector maps.


🧠 Learnings used
Learnt from: kaligrafy
Repo: chairemobilite/evolution PR: 1462
File: packages/evolution-common/src/services/baseObjects/Completable.ts:1-6
Timestamp: 2026-03-31T18:20:32.322Z
Learning: In this repository (chairemobilite/evolution), do not flag or require a copyright year in file headers. For new files, the header format `Copyright Polytechnique Montreal and contributors` (without a year) is acceptable. Only verify the header presence/consistency, not the presence of the year.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants