Skip to content

Conversation

@mdkaifansari04
Copy link
Contributor

@mdkaifansari04 mdkaifansari04 commented Oct 28, 2025

Proposed change

Fixes: #1493
Implemented a click-to-activate interaction pattern that prevents accidental zooming while preserving full map functionality

  • Disabled default scroll wheel zoom in Leaflet map configuration
  • Added interactive overlay with visual indicator "Click to interact with map"
  • Map becomes interactive only when user clicks, indicating intent

Video Demonstration

2025-10-29.00-35-53.mov

Additional: Project detail test failing

The ProjectDetails test failure was NOT related to your ChapterMap changes. It was caused by:

  • The [IS_PROJECT_HEALTH_ENABLED]environment variable being disabled in tests
  • When disabled, the [HealthMetrics] component doesn't render
  • Therefore, texts like "Issues Trend" weren't found in the DOM

Checklist

  • I've read and followed the contributing guidelines.
  • I've run make check-test locally; all checks and tests passed.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 28, 2025

Summary by CodeRabbit

  • New Features

    • Map now displays an interactive overlay prompting users to click or use keyboard (Enter/Space) to activate interactions.
    • Scroll wheel zoom disabled by default, automatically enabled upon user engagement.
    • Enhanced keyboard navigation and accessibility attributes for improved usability.
  • Tests

    • Added comprehensive tests for overlay behavior and accessibility compliance.

Walkthrough

Leaflet map in ChapterMap now initializes with scrollWheelZoom disabled and shows an accessible overlay ("Click to interact with map"). Clicking or pressing Enter/Space on the overlay enables scrollWheelZoom and sets an active state; mouseout disables zoom unless cursor moves into map/popups. Tests updated for these behaviors and accessibility.

Changes

Cohort / File(s) Summary
Component: ChapterMap behavior
frontend/src/components/ChapterMap.tsx
Added isMapActive state; initialize Leaflet map with scrollWheelZoom: false; enable zoom and set active on click or Enter/Space; disable on mouseout with relatedTarget checks; render accessible overlay button and manage its dismissal.
Unit tests: ChapterMap interactions
frontend/__tests__/unit/components/ChapterMap.test.tsx
Expanded Leaflet mock with .on and scrollWheelZoom (enable/disable); capture render container for class assertions; added tests for map init (scrollWheelZoom: false), event wiring (click, mouseout), overlay presence and dismissal (click, Enter, Space), accessibility attrs (tabIndex, aria-label), and ensuring scrollWheelZoom enables after interaction.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Verify Leaflet API usage (map initialization options and scrollWheelZoom.enable/disable) matches real API.
  • Check correctness of map.on handler wiring and mouseout relatedTarget logic (ensuring popups/child elements don't incorrectly disable zoom).
  • Confirm tests' mocks accurately reflect runtime behavior (mocked on, scrollWheelZoom, and event dispatching).

Possibly related PRs

Suggested reviewers

  • arkid15r
  • abhayymishraa

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 pull request title "Fix: Prevent Accidental Map Zooming While Scrolling on Landing Page" is fully related to the main changes in the changeset. The raw_summary confirms that the implementation disables scroll wheel zoom by default, adds a click-to-activate overlay, and restructures the interaction flow—all of which directly address the title's stated objective. The title is concise, specific, and clearly communicates the primary fix without noise or ambiguity.
Linked Issues Check ✅ Passed The code changes directly address the requirements of issue #1493. The implementation disables scroll wheel zoom by default, requires an explicit user click to activate map interactivity, and includes a visual overlay indicator to communicate intent—exactly matching the expected behavior described in the issue. The changes also include comprehensive test coverage validating the click-to-activate pattern, event handlers, and accessibility features. All coding-related requirements from the linked issue are met by the changeset.
Out of Scope Changes Check ✅ Passed All changes in the pull request are directly related to fixing the issue described in #1493. The modifications to ChapterMap.tsx (isMapActive state, disabled scrollWheelZoom, click/mouseout handlers, overlay UI with accessibility) and comprehensive test coverage in ChapterMap.test.tsx (mocks, interaction tests, accessibility validation) are all essential components of implementing the click-to-activate interaction pattern. No unrelated refactoring, cleanup, or extraneous changes are present in the changeset.
Description Check ✅ Passed The pull request description is well-related to the changeset and provides meaningful information about the implementation. It specifically references the issue being fixed (#1493), explains the click-to-activate interaction pattern, lists the key implementation details (disabled scroll wheel zoom, interactive overlay, accessibility features), includes supporting evidence (video demonstration), and addresses an unrelated test failure context. The description demonstrates good communication and is neither vague nor off-topic.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent 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 f8ae74d and 34c2df7.

📒 Files selected for processing (1)
  • frontend/src/components/ChapterMap.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/components/ChapterMap.tsx
⏰ 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). (3)
  • GitHub Check: Run CI Denendencies Scan
  • GitHub Check: CodeQL (python)
  • GitHub Check: CodeQL (javascript-typescript)

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: 1

🧹 Nitpick comments (1)
frontend/__tests__/unit/components/ChapterMap.test.tsx (1)

164-168: Consider testing event listener behavior, not just registration.

While this test verifies that event listeners are registered, it doesn't verify what those handlers actually do when triggered. The overlay tests cover overlay interaction, but the map's own click and mouseout handlers' behavior remains untested.

Consider adding assertions to verify the behavior:

 it('sets up event listeners for map interaction', () => {
+  const L = require('leaflet')
   render(<ChapterMap {...defaultProps} />)
   expect(mockMap.on).toHaveBeenCalledWith('click', expect.any(Function))
   expect(mockMap.on).toHaveBeenCalledWith('mouseout', expect.any(Function))
+  
+  // Verify click handler behavior
+  const clickHandler = mockMap.on.mock.calls.find(call => call[0] === 'click')[1]
+  clickHandler()
+  expect(mockMap.scrollWheelZoom.enable).toHaveBeenCalled()
 })
📜 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 caa3092 and 7693215.

📒 Files selected for processing (2)
  • frontend/__tests__/unit/components/ChapterMap.test.tsx (7 hunks)
  • frontend/src/components/ChapterMap.tsx (4 hunks)
🔇 Additional comments (10)
frontend/__tests__/unit/components/ChapterMap.test.tsx (5)

1-1: LGTM! Mock setup properly supports new interaction testing.

The addition of fireEvent and the extended mock with on and scrollWheelZoom provide the necessary infrastructure to test the interactive overlay and scroll wheel zoom behavior.

Also applies to: 9-13


102-113: LGTM! Structural assertions properly validate layout changes.

The test correctly verifies that the map container has the expected Tailwind utility classes and that the parent container has the relative positioning needed for the overlay.


135-135: LGTM! Correctly expects scroll wheel zoom disabled by default.

This assertion properly validates that the map is initialized with scrollWheelZoom: false, which is the core of the fix to prevent accidental zooming during page scrolling.


240-290: LGTM! Comprehensive interactive overlay test coverage.

The test suite thoroughly covers:

  • Initial overlay presence
  • Click and keyboard interactions (Enter, Space)
  • ScrollWheelZoom enablement on interaction
  • Accessibility attributes (role, tabIndex, aria-label)

The repeated pattern of querying the overlay element is acceptable for test clarity, though you could optionally extract a helper function if the pattern becomes more complex.


349-361: LGTM! Properly validates style application hierarchy.

The test correctly verifies that custom styles are applied to the parent container while the map container maintains its Tailwind utility classes. This demonstrates good separation between user-provided styles and component-internal styling.

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

3-3: LGTM!

The useState import is correctly added to support the new interactive state management.


22-22: LGTM!

The state initialization correctly defaults to false, ensuring the map starts in inactive mode with the overlay visible.


26-34: LGTM!

Disabling scrollWheelZoom by default is the correct approach to prevent accidental map interaction during page scrolling.


41-45: LGTM!

The click handler enables scroll wheel zoom on user interaction, correctly implementing the intended behavior. While this logic overlaps with the overlay's onClick handler, it serves as a useful fallback during state transitions and ensures zoom is re-enabled if the user clicks the map directly after it becomes inactive.


119-146: Excellent accessibility implementation!

The overlay implementation is well-structured with comprehensive accessibility features:

  • Proper semantic role (button)
  • Keyboard navigation support (Enter/Space keys)
  • Clear aria-label for screen readers
  • Preventive preventDefault() to avoid page scroll on Space key

The conditional rendering logic correctly shows/hides the overlay based on map active state, and the z-index ensures the overlay properly blocks map interaction when inactive.

@mdkaifansari04
Copy link
Contributor Author

@coderabbitai generate docstrings

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 28, 2025

✅ Actions performed

Initiated docstring generation; will generate only if new commits exist.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 28, 2025

Caution

Docstrings generation - FAILED

No docstrings were generated.

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.

@mdkaifansari04 please run make check-test locally and address found issues before we review

…n and improve accessibility, and run make check-test
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/__tests__/unit/components/ChapterMap.test.tsx (1)

164-168: Add coverage for re-disabling scroll zoom on mouseout

Right now we only assert that the Leaflet mouseout listener is registered. If that handler stopped calling scrollWheelZoom.disable, the suite would still pass and the original scroll-trap bug would come back unnoticed. Please capture the registered handler and assert that invoking it actually issues an extra disable—that keeps the regression net tight.

     it('sets up event listeners for map interaction', () => {
       render(<ChapterMap {...defaultProps} />)
       expect(mockMap.on).toHaveBeenCalledWith('click', expect.any(Function))
       expect(mockMap.on).toHaveBeenCalledWith('mouseout', expect.any(Function))
     })
+
+    it('re-disables scroll wheel zoom when the cursor leaves the map', () => {
+      const { getByText } = render(<ChapterMap {...defaultProps} />)
+
+      const mouseoutHandler = (mockMap.on as jest.Mock).mock.calls.find(
+        ([eventName]) => eventName === 'mouseout'
+      )?.[1] as (event: { originalEvent?: { relatedTarget: Element | null } }) => void
+
+      expect(mouseoutHandler).toBeDefined()
+      if (!mouseoutHandler) {
+        throw new Error('Leaflet mouseout handler was not registered')
+      }
+
+      const overlay = getByText('Click to interact with map').closest('button')
+      expect(overlay).not.toBeNull()
+      fireEvent.click(overlay!)
+
+      const disableCallsBefore = mockMap.scrollWheelZoom.disable.mock.calls.length
+
+      mouseoutHandler({ originalEvent: { relatedTarget: null } })
+
+      expect(mockMap.scrollWheelZoom.disable.mock.calls.length).toBe(disableCallsBefore + 1)
+    })
📜 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 475b4dc and ff3b3d1.

📒 Files selected for processing (2)
  • frontend/__tests__/unit/components/ChapterMap.test.tsx (6 hunks)
  • frontend/src/components/ChapterMap.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/components/ChapterMap.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-12T17:36:57.255Z
Learnt from: Rajgupta36
PR: OWASP/Nest#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/ChapterMap.test.tsx

@mdkaifansari04 mdkaifansari04 requested a review from kasya October 29, 2025 20:05
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.

Looks and works great!
One small request ⏬

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: 1

📜 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 ff3b3d1 and f20e4d8.

📒 Files selected for processing (1)
  • frontend/src/components/ChapterMap.tsx (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-12T17:12:25.807Z
Learnt from: Rajgupta36
Repo: OWASP/Nest PR: 1717
File: frontend/__tests__/unit/pages/ProgramDetails.test.tsx:35-0
Timestamp: 2025-07-12T17:12:25.807Z
Learning: In React applications, button elements should always have an explicit type attribute (type="button", type="submit", or type="reset") to prevent unintended form submission behavior, even when the button appears to be outside of a form context. The default type is "submit" which can cause unexpected behavior.

Applied to files:

  • frontend/src/components/ChapterMap.tsx
📚 Learning: 2025-04-30T13:41:20.846Z
Learnt from: codic-yeeshu
Repo: OWASP/Nest PR: 1444
File: frontend/src/components/NavDropDown.tsx:0-0
Timestamp: 2025-04-30T13:41:20.846Z
Learning: When implementing dropdown menus or similar interactive components, always include proper accessibility features: ARIA attributes (aria-expanded, aria-haspopup, aria-controls), keyboard navigation support (Enter, Space, Escape keys), and mechanisms to close dropdowns when clicking outside.

Applied to files:

  • frontend/src/components/ChapterMap.tsx

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)

128-156: Well-implemented overlay with good accessibility—consider improving focus management.

The overlay button properly supports both mouse and keyboard interaction with appropriate ARIA attributes. The implementation successfully solves the accidental zoom issue.

Recommended improvement: Manage keyboard focus after activation.

When the overlay is dismissed, keyboard focus is lost because the button is removed from the DOM. Transfer focus to the map container to maintain keyboard navigation flow:

+  const mapContainerRef = useRef<HTMLDivElement | null>(null)
+
   useEffect(() => {
     if (!mapRef.current) {
       mapRef.current = L.map('chapter-map', {
         worldCopyJump: false,
         maxBounds: [
           [-90, -180],
           [90, 180],
         ],
         maxBoundsViscosity: 1.0,
         scrollWheelZoom: false,
       }).setView([20, 0], 2)
+
+      // Make container focusable for keyboard users
+      const container = mapRef.current.getContainer()
+      if (container) {
+        container.setAttribute('tabindex', '0')
+        container.setAttribute('aria-label', 'Interactive chapter map')
+      }

   return (
     <div className="relative" style={style}>
-      <div id="chapter-map" className="w-full h-full" />
+      <div ref={mapContainerRef} id="chapter-map" className="w-full h-full" />
       {!isMapActive && (
         <button
           type="button"
           tabIndex={0}
           className="absolute inset-0 z-[1000] flex cursor-pointer items-center justify-center rounded-[inherit] bg-black/10"
           onClick={() => {
             mapRef.current?.scrollWheelZoom.enable()
             setIsMapActive(true)
+            // Move focus to map container after overlay is dismissed
+            setTimeout(() => mapContainerRef.current?.focus(), 0)
           }}
           onKeyDown={(e) => {
             if (e.key === 'Enter' || e.key === ' ') {
               e.preventDefault()
               mapRef.current?.scrollWheelZoom.enable()
               setIsMapActive(true)
+              setTimeout(() => mapContainerRef.current?.focus(), 0)
             }
           }}

Optional refinement: Extract activation logic.

The enable-and-activate logic appears in both onClick and onKeyDown. If you prefer, extract it into a helper:

const activateMap = () => {
  mapRef.current?.scrollWheelZoom.enable()
  setIsMapActive(true)
  setTimeout(() => mapContainerRef.current?.focus(), 0)
}

Then use onClick={activateMap} and call activateMap() in the onKeyDown handler after preventDefault(). Given the minimal duplication, this is entirely optional.

Based on learnings

📜 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 e850329 and f8ae74d.

📒 Files selected for processing (1)
  • frontend/src/components/ChapterMap.tsx (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-12T17:12:25.807Z
Learnt from: Rajgupta36
Repo: OWASP/Nest PR: 1717
File: frontend/__tests__/unit/pages/ProgramDetails.test.tsx:35-0
Timestamp: 2025-07-12T17:12:25.807Z
Learning: In React applications, button elements should always have an explicit type attribute (type="button", type="submit", or type="reset") to prevent unintended form submission behavior, even when the button appears to be outside of a form context. The default type is "submit" which can cause unexpected behavior.

Applied to files:

  • frontend/src/components/ChapterMap.tsx
📚 Learning: 2025-04-30T13:41:20.846Z
Learnt from: codic-yeeshu
Repo: OWASP/Nest PR: 1444
File: frontend/src/components/NavDropDown.tsx:0-0
Timestamp: 2025-04-30T13:41:20.846Z
Learning: When implementing dropdown menus or similar interactive components, always include proper accessibility features: ARIA attributes (aria-expanded, aria-haspopup, aria-controls), keyboard navigation support (Enter, Space, Escape keys), and mechanisms to close dropdowns when clicking outside.

Applied to files:

  • frontend/src/components/ChapterMap.tsx
🪛 GitHub Actions: Run CI/CD
frontend/src/components/ChapterMap.tsx

[error] 1-1: Unstaged changes detected. Run 'make check' and use 'git add' to address it.

⏰ 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). (2)
  • GitHub Check: CodeQL (python)
  • GitHub Check: CodeQL (javascript-typescript)
🔇 Additional comments (3)
frontend/src/components/ChapterMap.tsx (3)

3-3: LGTM—State management setup is correct.

The useState import and isMapActive state are properly initialized to support the click-to-activate interaction pattern.

Also applies to: 22-22


33-33: LGTM—Map initialization and event handlers work correctly.

The map initializes with scroll-wheel zoom disabled, and the event handlers properly manage the activation state. The mouseout logic correctly ignores movement within the map container (including controls and popups), which preserves full interactivity after activation.

Also applies to: 41-54


1-1: Original review comment is too vague; request manual verification.

The review doesn't specify which checks failed (formatting, linting, spelling, or code generation). The developer should:

  1. Check the CI pipeline logs to identify the specific issue(s)
  2. Run the corresponding fix command locally:
    • pnpm run format (for formatting issues)
    • pnpm run lint (for linting issues)
    • pnpm run graphql-codegen (for schema generation)
  3. Stage and commit the changes

Running make check blindly may not work locally depending on environment setup (requires Docker for spelling checks).

@sonarqubecloud
Copy link

sonarqubecloud bot commented Nov 1, 2025

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.

Looks good! Thank you @mdkaifansari04 ! 👍🏼

@kasya kasya enabled auto-merge November 1, 2025 01:55
@kasya kasya added this pull request to the merge queue Nov 1, 2025
Merged via the queue into OWASP:main with commit 0083617 Nov 1, 2025
26 checks passed
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.

The map catches the cursor and zooms in and out

2 participants