From 3f86057c839c0570fdf7b1c615f3a25f6016dffc Mon Sep 17 00:00:00 2001 From: Dhavanesh24cs412 Date: Thu, 15 Jan 2026 00:46:55 +0530 Subject: [PATCH] fix: resolve search dropdown unable to reopen in same session (#650) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem The framework and library dropdown menus in the search modal could only be opened once per search session. After the first open-close cycle, subsequent attempts to open the dropdown would fail until the entire search modal was closed and reopened. ## Root Cause The `modal={true}` prop in the Radix Dropdown component creates a modal context that blocks interactions with elements outside the dropdown. This prevented proper state management and focus handling for reopening the dropdown. ## Solution 1. Removed `modal={true}` prop from Dropdown component 2. Removed overly defensive event handlers (onPointerDownOutside, onEscapeKeyDown) 3. Implemented intelligent `onInteractOutside` handler with boundary detection ## Changes - src/components/Dropdown.tsx: Added onInteractOutside with boundary detection - src/components/SearchModal.tsx: Removed modal={true} prop ## Testing ✅ Dropdown opens on first click ✅ Dropdown closes on second click ✅ Dropdown opens again on third click (previously broken - NOW FIXED!) ✅ Multiple open/close cycles work consistently ✅ Keyboard navigation works correctly ✅ Clicking outside dropdown closes it naturally Fixes #650 --- src/components/Dropdown.tsx | 19 +++++++- src/components/SearchModal.tsx | 88 +++++++++++++++++++++++----------- 2 files changed, 78 insertions(+), 29 deletions(-) diff --git a/src/components/Dropdown.tsx b/src/components/Dropdown.tsx index 0a58bbf33..4bb5b57c1 100644 --- a/src/components/Dropdown.tsx +++ b/src/components/Dropdown.tsx @@ -37,10 +37,14 @@ export function Dropdown({ children, open, onOpenChange, - modal = false, + modal = false, // IMPORTANT DEFAULT }: DropdownProps) { return ( - + {children} ) @@ -69,6 +73,16 @@ export function DropdownContent({ e.preventDefault()} + onInteractOutside={(e) => { + // Only close if clicking far outside dropdown + const target = e.target as HTMLElement + if (!target.closest('[role="listbox"]') && + !target.closest('button[class*="dropdown"]')) { + return // Allow the close + } + e.preventDefault() // Block close if clicking dropdown area + }} className={twMerge( 'dropdown-content z-[1000] min-w-48 rounded-lg p-1.5', 'border border-gray-200 dark:border-gray-700', @@ -83,6 +97,7 @@ export function DropdownContent({ ) } + export function DropdownItem({ children, className, diff --git a/src/components/SearchModal.tsx b/src/components/SearchModal.tsx index 03f280d58..9b71f14bc 100644 --- a/src/components/SearchModal.tsx +++ b/src/components/SearchModal.tsx @@ -154,7 +154,6 @@ const searchClient = liteClient( '10c34d6a5c89f6048cf644d601e65172', ) -// Context to share filter state between components const SearchFiltersContext = React.createContext<{ selectedLibrary: string selectedFramework: string @@ -166,16 +165,20 @@ const SearchFiltersContext = React.createContext<{ value: string label: string count: number - isRefined: boolean }> frameworkItems: Array<{ value: string label: string count: number - isRefined: boolean }> + + libraryDropdownOpen: boolean + setLibraryDropdownOpen: (open: boolean) => void + frameworkDropdownOpen: boolean + setFrameworkDropdownOpen: (open: boolean) => void } | null>(null) + function useSearchFilters() { const context = React.useContext(SearchFiltersContext) if (!context) { @@ -190,7 +193,10 @@ function SearchFiltersProvider({ children }: { children: React.ReactNode }) { const userQuery = useCurrentUserQuery() const [selectedLibrary, setSelectedLibrary] = React.useState('') const lastUsedFramework = userQuery.data?.lastUsedFramework - + const [libraryDropdownOpen, setLibraryDropdownOpen] = + React.useState(false) + const [frameworkDropdownOpen, setFrameworkDropdownOpen] = + React.useState(false) // Get initial framework from user preference (DB if logged in, localStorage otherwise) const getInitialFramework = React.useCallback(() => { if (lastUsedFramework) { @@ -290,8 +296,13 @@ function SearchFiltersProvider({ children }: { children: React.ReactNode }) { refineFramework: selectFramework, libraryItems, frameworkItems, + libraryDropdownOpen, + setLibraryDropdownOpen, + frameworkDropdownOpen, + setFrameworkDropdownOpen, }} > + {children} ) @@ -556,16 +567,24 @@ const Hit = ({ } function LibraryRefinement() { - const { - selectedLibrary, - setSelectedLibrary, - libraryItems: items, - } = useSearchFilters() +const { + selectedLibrary, + setSelectedLibrary, + libraryItems: items, + libraryDropdownOpen, + setLibraryDropdownOpen, +} = useSearchFilters() + const currentLibrary = libraries.find((l) => l.id === selectedLibrary) + return ( - + +