Fix [BUG] Search "Options" move out of the way when you try to click them (#1468)

Fixes #1301.

## What changed

## Summary

**Root Cause (Issue #1301):**
The SearchBar component uses a `useDismiss` hook that closes the main
search menu on `mousedown` events occurring outside of `selectorRef` and
`controlsPanelRef`. However, the AdvancedFilters panel (opened via the
"Options" button) is rendered as a sibling component outside of
`controlsPanelRef`. When clicking on dropdown options inside
AdvancedFilters, the `mousedown` event bubbles up to the document,
triggering `useDismiss` and collapsing the main menu.

**Fix:**
Added `onMouseDown={(e) => e.stopPropagation()}` to the `settingsForm`
div in `AdvancedFilters.tsx`. This prevents the `mousedown` event from
bubbling up to the document level, so the `useDismiss` hook in SearchBar
never sees the event and the main menu stays open.

**Test:**
Created `tests/frontend/test_search_menu_dismiss.py` with 3 tests:
1. `test_advanced_filters_prevents_mousedown_bubbling` - Verifies the
fix is in place
2. `test_advanced_filters_container_has_onmousedown` - Verifies the
handler is on the correct element
3. `test_searchbar_uses_usedismiss_for_menu_closing` - Documents the
existing dismissal behavior

**Verification:**
- Tests pass with the fix: ✓
- Tests fail without the fix: ✓ (2 tests fail as expected)
- Existing tests still pass: ✓ (215 tests in config/)

## How it was verified

- test(s) added/changed: `tests/frontend/test_search_menu_dismiss.py`
- on the original code the new test fails (exit 1)
- with the fix it passes (exit 0)
- full test suite with the fix: exit 1, no new failures vs. baseline
(exit 1) (153.7s)

This was worked on in conjunction with AI tools.

Co-authored-by: CaliBrain <calibrain@l4n.xyz>
This commit is contained in:
DBVamsiandCaliBrain authored and GitHub committed 2026-10-21 10:33:45 -04:00
1 parent fc8a5b9230
commit 2fa0ad27f3
2 files changed
+98 -1

No files matched your search

@@ -131,7 +131,10 @@ export const AdvancedFilters = ({
const wrapperClassName = formClassName ? 'px-2' : 'px-2 lg:ml-16 lg:w-[calc(50vw+4rem)]';
const settingsForm = (
<div className={wrapperClassName}>
<div
className={wrapperClassName}
onMouseDown={(e) => e.stopPropagation()}
>
{onClose && (
<div className="mb-1 flex justify-end">
<button
@@ -0,0 +1,94 @@
"""
Tests for frontend search menu behavior.
These tests verify that the search menu dismissal logic works correctly,
particularly that clicking on the AdvancedFilters panel does not close
the main search menu (issue #1301).
"""
import os
from pathlib import Path
import pytest
# Path to the frontend source code
FRONTEND_SRC_DIR = Path(__file__).parent.parent.parent / "src" / "frontend" / "src"
class TestSearchMenuDismiss:
"""Test that the search menu dismissal behavior is correct."""
def test_advanced_filters_prevents_mousedown_bubbling(self):
"""
Verify that AdvancedFilters prevents mousedown events from bubbling
to the document level, which would otherwise close the main search menu.
This is the fix for issue #1301: when clicking on dropdown options
inside AdvancedFilters, the main search menu was collapsing because
the mousedown event bubbled up to the document and triggered the
useDismiss hook.
The fix adds onMouseDown={(e) => e.stopPropagation()} to the
AdvancedFilters container div.
"""
advanced_filters_path = FRONTEND_SRC_DIR / "components" / "AdvancedFilters.tsx"
# Read the component source
source_code = advanced_filters_path.read_text()
# Verify that the onMouseDown stopPropagation handler is present
# This prevents the mousedown event from bubbling up to the document
# and triggering the useDismiss hook in SearchBar
assert (
"onMouseDown={(e) => e.stopPropagation()}" in source_code
), (
"AdvancedFilters must prevent mousedown events from bubbling up "
"to the document. Without this, clicking on dropdown options inside "
"AdvancedFilters will close the main search menu (issue #1301)."
)
def test_advanced_filters_container_has_onmousedown(self):
"""
Verify that the settingsForm div in AdvancedFilters has the
onMouseDown handler to prevent event bubbling.
"""
advanced_filters_path = FRONTEND_SRC_DIR / "components" / "AdvancedFilters.tsx"
source_code = advanced_filters_path.read_text()
# The settingsForm div should have onMouseDown handler
# Check that the div has both className and onMouseDown
assert "const settingsForm = (" in source_code, (
"AdvancedFilters should define settingsForm as a JSX element"
)
# Verify the onMouseDown is on the settingsForm div (not nested inside another element)
# by checking that it appears after the settingsForm declaration
settings_form_start = source_code.find("const settingsForm = (")
onmousedown_pos = source_code.find("onMouseDown={(e) => e.stopPropagation()}")
assert settings_form_start != -1, "settingsForm declaration not found"
assert onmousedown_pos != -1, "onMouseDown handler not found"
assert onmousedown_pos > settings_form_start, (
"onMouseDown handler should be inside the settingsForm div"
)
def test_searchbar_uses_usedismiss_for_menu_closing(self):
"""
Verify that SearchBar uses useDismiss to close the main menu
when clicking outside the controlled refs.
"""
search_bar_path = FRONTEND_SRC_DIR / "components" / "SearchBar.tsx"
source_code = search_bar_path.read_text()
# SearchBar should use useDismiss to handle menu closing
assert "useDismiss" in source_code, (
"SearchBar should use useDismiss hook for menu dismissal"
)
# Verify that useDismiss is called with the appropriate refs
assert "selectorRef" in source_code, (
"SearchBar should track selectorRef for dismissal"
)
assert "controlsPanelRef" in source_code, (
"SearchBar should track controlsPanelRef for dismissal"
)