# Dropdown Portal / Teleport Pattern (ShopProQuote) ## Problem The `#service-search-results` dropdown lives inside `#services-content` (a collapsible accordion section). When the accordion uses `overflow: hidden` or any ancestor creates a stacking context, the absolute-positioned dropdown gets clipped — it appears behind the Quote Summary section or is truncated. CSS `z-index: 99999 !important` does NOT fix this because z-index is scoped within the parent stacking context. ## Fix: Teleport to `#dropdown-root` All ShopProQuote pages have `` at the end of `` (in `index.html`, `dashboard.html`, `repair-orders.html`, `customers.html`, `appointments.html`). Use this as a portal target. ### Implementation (Vanilla JS) In `renderServiceResults()` in both `main.js` and `quote-tab-manager.js`: ```javascript // 1. Move dropdown to portal root (only on first render) const dropdownRoot = document.getElementById('dropdown-root'); if (dropdownRoot && serviceSearchResults.parentElement !== dropdownRoot) { dropdownRoot.appendChild(serviceSearchResults); } // 2. Position relative to the search input using getBoundingClientRect const rect = serviceSearchInput.getBoundingClientRect(); serviceSearchResults.style.position = 'fixed'; serviceSearchResults.style.top = (rect.bottom + 4) + 'px'; serviceSearchResults.style.left = rect.left + 'px'; serviceSearchResults.style.width = rect.width + 'px'; // 3. Add scroll/resize repositioning const repositionDropdown = () => { if (serviceSearchResults.classList.contains('hidden')) return; const rect = serviceSearchInput.getBoundingClientRect(); serviceSearchResults.style.top = (rect.bottom + 4) + 'px'; serviceSearchResults.style.left = rect.left + 'px'; serviceSearchResults.style.width = rect.width + 'px'; }; window.addEventListener('scroll', repositionDropdown, { passive: true }); window.addEventListener('resize', repositionDropdown); ``` ### Why This Works - `appendChild` to `dropdown-root` (direct child of ``) escapes ALL ancestor stacking contexts - `position: fixed` with `getBoundingClientRect()` positions the dropdown relative to the viewport, anchored to the input - Scroll/resize listener keeps it positioned correctly as the user scrolls ### Files to Modify - `main.js`: `renderServiceResults()` function - `quote-tab-manager.js`: `renderServiceResults()` function AND `initializeServiceSearch()` (for the scroll listener) - BOTH files have their own service search implementation — both need the fix ### React Equivalent (spq-v2) In the React rewrite, use a portal: ```tsx import { createPortal } from 'react-dom'; {open && createPortal(
{/* dropdown items */}
, document.getElementById('dropdown-root')! )} ```