Skip to content

Conversation

@anurag2787
Copy link
Contributor

Resolves #2895

Proposed change

This PR fixes the following issues with precise location on Chapter maps

  • Fixed the issue when hovering over interactive buttons, focus on the map is lost and the overlay (Click to interact with map) reappears
  • Fixed the behavior so disabling shared location mode fully removes and resets the precise location pin from the map.

Checklist

  • I read and followed the contributing guidelines
  • I ran make check-test locally and all tests passed
  • I used AI for code, documentation, or tests in this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 14, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Location sharing is now enabled on map components across the app. User location markers are displayed on maps, and map views automatically adjust to show both chapters and user locations simultaneously for improved visibility and navigation.
  • Tests

    • Enhanced test suite with more flexible assertions for map behavior to improve test stability.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

Adds a new boolean prop showLocationSharing to ChapterMapWrapper usages and tests; updates ChapterMapWrapper to strip that prop before forwarding; and updates ChapterMap to manage a single user-location marker via refs, preserve/restore initial view, include user location in bounds, use fitBounds, and improve mouseout containment handling.

Changes

Cohort / File(s) Change Summary
Unit tests / mocks
frontend/__tests__/unit/components/CardDetailsPage.test.tsx, frontend/__tests__/unit/components/ChapterMap.test.tsx
Mock for ChapterMapWrapper now accepts/destructures optional showLocationSharing?: boolean. ChapterMap tests add mocked map methods (getCenter, getZoom, getContainer) and relax strict setView count assertions to reduce brittleness.
Wrapper component
frontend/src/components/ChapterMapWrapper.tsx
Extracts showLocationSharing from incoming props into a mapProps object, forwards mapProps to ChapterMap (removing showLocationSharing from forwarded props), changes wrapper className to h-full w-full, and adds an eslint-disable for the unused destructured variable.
Map implementation
frontend/src/components/ChapterMap.tsx
Adds userMarkerRef and initialViewRef; captures initial center/zoom after init; clears existing user marker before creating a new one; includes user location as the first point when computing bounds; switches to fitBounds for centering and falls back to initialViewRef if no bounds; reduces slice size for non-user local chapters; expands mouseout containment check to consider parent element; maintains marker clustering.
Pages / usages
frontend/src/app/page.tsx, frontend/src/app/community/snapshots/[id]/page.tsx, frontend/src/components/CardDetailsPage.tsx
Pass showLocationSharing={true} to ChapterMapWrapper at three render sites (home page, community snapshot page, and card details).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Files needing extra attention:
    • frontend/src/components/ChapterMap.tsx — verify userMarkerRef lifecycle, single-marker guarantees, fitBounds logic, and initial view fallback across scenarios.
    • frontend/src/components/ChapterMapWrapper.tsx — ensure prop stripping (showLocationSharing) doesn't drop other required props.
    • Tests: frontend/__tests__/unit/components/ChapterMap.test.tsx — ensure relaxed assertions still validate intended behavior.

Possibly related PRs

Suggested reviewers

  • arkid15r
  • kasya

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Enhance Precise Location Manipulation on Chapters Map' accurately describes the main objective of the PR - enhancing how location sharing and precise location pins are handled on the chapters map component.
Description check ✅ Passed The PR description is directly related to the changeset, explaining the fixes for map focus loss on button hover and removal of precise location pins when disabling shared location mode.
Linked Issues check ✅ Passed The PR successfully addresses the three main requirements from issue #2895: prevents button hover from losing map focus, ensures precise location pins are removed when disabling shared location mode, and prevents multiple pins by properly managing marker state with refs.
Out of Scope Changes check ✅ Passed All changes are focused on location sharing and map behavior. The updates to ChapterMap.tsx (marker refs, bounds fitting), ChapterMapWrapper.tsx (prop extraction), and test files are directly aligned with the stated objectives.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link
Contributor

@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: 0

🧹 Nitpick comments (2)
frontend/src/components/ChapterMap.tsx (1)

121-147: Ensure user-marker removal is always safe; consider unmount cleanup
Calling userMarkerRef.current.remove() is fine for de-duping, but if this component can ever unmount/remount without Leaflet map teardown, you may leak handlers/layers. Consider adding a useEffect(() => () => mapRef.current?.remove(), []) teardown (and nulling refs) if remounts are possible in your app’s navigation patterns.

frontend/src/components/ChapterMapWrapper.tsx (1)

23-29: Drop the eslint-disable by using the destructured value (or underscore it)
Current code disables @typescript-eslint/no-unused-vars just to omit the prop. Prefer either (a) derive enableLocationSharing from the destructured value, or (b) rename it to _showLocationSharing when destructuring.

-import React, { useState } from 'react'
+import React, { useState } from 'react'
  import type { Chapter } from 'types/chapter'
  import {
    getUserLocationFromBrowser,
    sortChaptersByDistance,
    type UserLocation,
  } from 'utils/geolocationUtils'

  const ChapterMap = dynamic(() => import('./ChapterMap'), { ssr: false })

  const ChapterMapWrapper: React.FC<ChapterMapWrapperProps> = (props) => {
    const [userLocation, setUserLocation] = useState<UserLocation | null>(null)
    const [sortedData, setSortedData] = useState<Chapter[] | null>(null)

-   const enableLocationSharing = props.showLocationSharing === true
-   // eslint-disable-next-line @typescript-eslint/no-unused-vars
-   const { showLocationSharing, ...mapProps } = props
+   const { showLocationSharing, ...mapProps } = props
+   const enableLocationSharing = showLocationSharing === true

    if (!enableLocationSharing) {
      return <ChapterMap {...mapProps} />
    }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 065c275 and 53cee24.

📒 Files selected for processing (6)
  • frontend/__tests__/unit/components/CardDetailsPage.test.tsx (1 hunks)
  • frontend/src/app/community/snapshots/[id]/page.tsx (1 hunks)
  • frontend/src/app/page.tsx (1 hunks)
  • frontend/src/components/CardDetailsPage.tsx (1 hunks)
  • frontend/src/components/ChapterMap.tsx (4 hunks)
  • frontend/src/components/ChapterMapWrapper.tsx (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-06-20T16:12:59.256Z
Learnt from: ahmedxgouda
Repo: OWASP/Nest PR: 1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.

Applied to files:

  • frontend/src/components/CardDetailsPage.tsx
📚 Learning: 2025-07-12T17:36:57.255Z
Learnt from: Rajgupta36
Repo: OWASP/Nest PR: 1717
File: frontend/__tests__/unit/pages/createProgram.test.tsx:70-86
Timestamp: 2025-07-12T17:36:57.255Z
Learning: When testing React page components that use mocked form components, validation logic should be tested at the form component level, not the page level. Page-level tests should focus on authentication, role checking, submission handling, and navigation logic.

Applied to files:

  • frontend/__tests__/unit/components/CardDetailsPage.test.tsx
🔇 Additional comments (7)
frontend/src/components/ChapterMap.tsx (2)

31-32: Good: persist user marker instance via userMarkerRef
This is the right direction for enforcing “at most one” precise-location pin across renders.


56-69: Fix matches objective: don’t deactivate map when moving to controls near the map
The mapParent.contains(relatedTarget) guard should prevent the “Click to interact with map” overlay from reappearing when hovering/clicking UI that lives in the map wrapper (e.g., location-sharing button).

frontend/src/app/community/snapshots/[id]/page.tsx (1)

138-143: LGTM: enables consistent location sharing on snapshot “New Chapters” map

frontend/src/components/CardDetailsPage.tsx (1)

174-189: LGTM: chapter details map now opts into location sharing

frontend/src/components/ChapterMapWrapper.tsx (1)

55-62: Verify wrapper sizing change doesn’t break maps in non-fixed-height parents
Switching from "space-y-4" to "h-full w-full" may rely on parent containers providing an explicit height; otherwise Leaflet can render a 0px-tall map.

frontend/__tests__/unit/components/CardDetailsPage.test.tsx (1)

104-125: LGTM: test mock updated for new showLocationSharing prop

frontend/src/app/page.tsx (1)

311-322: LGTM: enables location sharing on home “Chapters Worldwide” map

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 14, 2025
@anurag2787 anurag2787 marked this pull request as ready for review December 14, 2025 19:37
Copy link
Collaborator

@kasya kasya left a comment

Choose a reason for hiding this comment

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

@anurag2787 Thanks for working on these updates so quickly!
A couple of things I noticed:

  1. Precise location button works differently on chapters page and on the home page. On chapters page resetting shared location moves back to a general view. On the home page, however, it stays in that zoomed in local view (as if I'm still on the precise location view, but without the pin).

2 more I shared in comment below 🔽

<ChapterMapWrapper
geoLocData={geolocationData}
showLocal={true}
showLocationSharing={true}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This didn't work for me on an individual chapter's page. It's still showing my hardcoded IP location instead of the precise geolocation.

Image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hy @kasya for individual chapter it actually focusing on the individual chapter location but currently fixed it so when user share location it will properly shows both the user location pin and the chapter pins

<ChapterMapWrapper
geoLocData={snapshot.newChapters}
showLocal={false}
showLocationSharing={true}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This also doesn't work for me right now. It just refreshes the chapter without any movement to a precise location

Copy link
Contributor

@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: 0

🧹 Nitpick comments (1)
frontend/src/components/ChapterMap.tsx (1)

35-190: Add cleanup function to prevent potential memory leaks.

The useEffect creates Leaflet objects (map, marker cluster, user marker) and attaches event listeners, but doesn't clean them up on unmount. Consider adding a cleanup function:

useEffect(() => {
  // ... existing code ...

  return () => {
    if (userMarkerRef.current) {
      userMarkerRef.current.remove()
      userMarkerRef.current = null
    }
    if (markerClusterRef.current) {
      markerClusterRef.current.clearLayers()
      markerClusterRef.current = null
    }
    if (mapRef.current) {
      mapRef.current.off()
      mapRef.current.remove()
      mapRef.current = null
    }
  }
}, [geoLocData, showLocal, userLocation])

Note: Place the return statement at the end of the useEffect, after all the existing logic.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 53cee24 and ac4ba5a.

📒 Files selected for processing (1)
  • frontend/src/components/ChapterMap.tsx (6 hunks)
🔇 Additional comments (7)
frontend/src/components/ChapterMap.tsx (7)

31-32: LGTM! Refs enable proper marker lifecycle management.

The userMarkerRef ensures only one user location pin exists at a time, and initialViewRef enables restoring the default map view when location sharing is disabled—both directly address the PR objectives.


47-50: LGTM! Initial view correctly captured.

Capturing the initial center and zoom immediately after map creation ensures the default view can be restored when location sharing is disabled.


66-71: LGTM! Mouseout logic correctly handles button interactions.

Checking whether the relatedTarget is within the map's parent element prevents the "Click to interact with map" overlay from reappearing when hovering over the share location button, directly addressing one of the PR objectives.


127-130: LGTM! Prevents duplicate user location pins.

Removing any existing user marker before creating a new one guarantees at most one pin exists, directly addressing the PR objective about preventing multiple pins when activating shared location mode repeatedly.


152-152: LGTM! Completes marker lifecycle tracking.

Storing the newly created marker in the ref enables its removal on subsequent renders, completing the single-pin guarantee mechanism.


158-164: LGTM! Bounds calculation correctly includes user location.

Constructing locationsForBounds with the user location followed by nearby chapters ensures the map view encompasses both, improving the user experience when location sharing is enabled. The filter at lines 89-93 ensures all coordinates are numbers at runtime.


187-188: LGTM! Properly restores initial view.

When location sharing is disabled and no local chapters are displayed, restoring the initial view ensures a consistent default state, directly addressing the PR objective about fully resetting the map when disabling shared location mode.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 15, 2025
@anurag2787
Copy link
Contributor Author

The issue has been fixed
Screencast from 2025-12-15 14-08-20.webm

@anurag2787 anurag2787 requested a review from kasya December 15, 2025 08:52
@anurag2787 anurag2787 marked this pull request as draft December 15, 2025 09:00
@anurag2787 anurag2787 marked this pull request as ready for review December 15, 2025 09:41
Copy link
Contributor

@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: 0

🧹 Nitpick comments (2)
frontend/src/components/ChapterMap.tsx (2)

158-168: LGTM! User location included in bounds calculation.

Including the user's location as the first point in the bounds array ensures the map fits both the user's position and nearby chapters, providing good spatial context.

Note: This branch considers 5 chapters while the showLocal-only branch (line 173) considers 4. This appears intentional but might warrant a code comment explaining the difference.


35-193: Consider extracting bounds calculation logic for improved readability.

The useEffect contains complex view management logic with multiple conditional branches. While the current implementation is correct, extracting the bounds calculation logic into separate helper functions could improve maintainability.

Example structure:

const calculateBoundsWithUserLocation = (userLocation, chapters, maxCount) => { ... }
const calculateLocalBounds = (chapters, maxCount) => { ... }

This would make the main effect logic more declarative and easier to test in isolation.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ac4ba5a and 2c4a2fe.

📒 Files selected for processing (2)
  • frontend/__tests__/unit/components/ChapterMap.test.tsx (2 hunks)
  • frontend/src/components/ChapterMap.tsx (6 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-17T17:30:42.139Z
Learnt from: anurag2787
Repo: OWASP/Nest PR: 2671
File: frontend/__tests__/unit/components/MultiSearch.test.tsx:169-171
Timestamp: 2025-11-17T17:30:42.139Z
Learning: In the OWASP/Nest frontend tests (PR #2671 context), wrapper functions like `expectChaptersCountEqualsThree` that simply call another helper with a fixed parameter (e.g., `expectChaptersCountEquals(3)`) are intentionally used to avoid arrow function callbacks in `waitFor` calls. This pattern prevents adding nesting depth that would trigger rule typescript:S2004. Example: `await waitFor(expectChaptersCountEqualsThree)` avoids the extra nesting from `await waitFor(() => expectChaptersCountEquals(3))`.

Applied to files:

  • frontend/__tests__/unit/components/ChapterMap.test.tsx
🔇 Additional comments (7)
frontend/__tests__/unit/components/ChapterMap.test.tsx (2)

11-13: LGTM! Mock additions support new map view management.

The mock implementations for getCenter, getZoom, and getContainer correctly support the new functionality in ChapterMap that captures the initial view and handles mouseout containment checks.


357-361: LGTM! More resilient test assertion strategy.

Capturing the initial call count and asserting a relative increase (rather than an exact total) makes the test more robust as the view management logic evolves with new features like user location markers and initial view restoration.

frontend/src/components/ChapterMap.tsx (5)

31-32: LGTM! Refs enable single-marker pattern and view restoration.

The userMarkerRef ensures only one user location marker exists at a time (preventing duplicates per PR objectives), while initialViewRef enables restoring the original map view when disabling location sharing.


47-50: LGTM! Initial view captured at the right time.

Capturing the map's center and zoom immediately after initialization ensures the fallback restoration (lines 190-191) resets to the correct default view.


66-71: LGTM! Mouseout logic prevents overlay reappearance on button hover.

By checking whether the relatedTarget is contained within the map's parent element, the mouseout handler correctly prevents the "Click to interact with map" overlay from reappearing when hovering over the location sharing button. This directly addresses the PR objective to prevent button hover from causing loss of map focus.


127-130: LGTM! User marker cleanup prevents duplicates.

Removing the existing user marker before potentially creating a new one ensures that only one precise location pin exists on the map at any time, directly addressing PR objective to prevent multiple pins.


190-191: LGTM! Fallback ensures clean reset behavior.

Restoring the initial view when neither user location nor local view conditions are met provides clean reset behavior when disabling location sharing, directly supporting the PR objective to fully reset the map state.

@sonarqubecloud
Copy link

Copy link
Collaborator

@kasya kasya left a comment

Choose a reason for hiding this comment

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

@anurag2787 Works good! Thanks!

@kasya kasya added this pull request to the merge queue Dec 17, 2025
Merged via the queue into OWASP:main with commit 287e6e9 Dec 17, 2025
26 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Dec 26, 2025
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enhance Precise Location Manipulation on Chapters Map

2 participants