From 6465ac2a40481cd6a9cf5fee611d7ba8e358db89 Mon Sep 17 00:00:00 2001 From: Jordan Ramos Date: Wed, 10 Jun 2026 11:40:20 -0600 Subject: [PATCH] =?UTF-8?q?Fix=20SearchableSelect=20=E2=80=94=20only=20ope?= =?UTF-8?q?n=20on=20focus,=20close=20properly=20on=20blur/select?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dropdown was opening automatically on render and not closing when clicking elsewhere. Now opens only on focus/click, closes on blur, selection, Enter, Escape, and Tab. Selected value persists in the input after selection. --- frontend/src/components/SearchableSelect.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/SearchableSelect.js b/frontend/src/components/SearchableSelect.js index 718a84f..26f325c 100644 --- a/frontend/src/components/SearchableSelect.js +++ b/frontend/src/components/SearchableSelect.js @@ -41,7 +41,7 @@ const OPTION_HIGHLIGHT = { export default function SearchableSelect({ value, options, onChange, onClose, placeholder, autoFocus }) { const [filter, setFilter] = useState(value || ''); const [highlightIdx, setHighlightIdx] = useState(-1); - const [isOpen, setIsOpen] = useState(true); + const [isOpen, setIsOpen] = useState(false); const inputRef = useRef(null); const listRef = useRef(null); @@ -49,6 +49,7 @@ export default function SearchableSelect({ value, options, onChange, onClose, pl if (autoFocus && inputRef.current) { inputRef.current.focus(); inputRef.current.select(); + setIsOpen(true); } }, [autoFocus]); @@ -79,25 +80,31 @@ export default function SearchableSelect({ value, options, onChange, onClose, pl e.preventDefault(); if (highlightIdx >= 0 && filteredOptions[highlightIdx]) { onChange(filteredOptions[highlightIdx]); + setFilter(filteredOptions[highlightIdx]); } else if (filter.trim()) { onChange(filter.trim()); } + setIsOpen(false); if (onClose) onClose(); } else if (e.key === 'Escape') { + setIsOpen(false); if (onClose) onClose(); } else if (e.key === 'Tab') { - // Accept current value on tab if (highlightIdx >= 0 && filteredOptions[highlightIdx]) { onChange(filteredOptions[highlightIdx]); + setFilter(filteredOptions[highlightIdx]); } else if (filter.trim()) { onChange(filter.trim()); } + setIsOpen(false); if (onClose) onClose(); } }; const handleSelect = (opt) => { onChange(opt); + setFilter(opt); + setIsOpen(false); if (onClose) onClose(); }; @@ -106,16 +113,18 @@ export default function SearchableSelect({ value, options, onChange, onClose, pl { setFilter(e.target.value); setHighlightIdx(-1); setIsOpen(true); }} + onFocus={() => setIsOpen(true)} onKeyDown={handleKeyDown} onBlur={() => { // Delay close so click on option can fire setTimeout(() => { + setIsOpen(false); if (filter.trim() && filter.trim() !== value) { onChange(filter.trim()); }