From 43f396b4c52a3f62afa23babdb34d22ddb03e4bc Mon Sep 17 00:00:00 2001 From: Chandler Copeland Date: Sat, 21 Mar 2026 15:50:22 -0600 Subject: [PATCH] fix(12-02): body scroll lock and z-index/portal fix for PreviewModal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bug 1: add useEffect that locks document.body.overflow on mount and restores original value on unmount — prevents page scrolling behind modal - Bug 2: render modal via ReactDOM.createPortal to document.body so it escapes the sticky sidebar's stacking context; raise z-index to 9999 so backdrop sits definitively above all FieldPlacer overlay boxes (zIndex: 10/12) and dnd-kit DragOverlay regardless of parent context --- .../[docId]/_components/PreviewModal.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PreviewModal.tsx b/teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PreviewModal.tsx index 4c7276a..2a89b42 100644 --- a/teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PreviewModal.tsx +++ b/teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PreviewModal.tsx @@ -1,5 +1,6 @@ 'use client'; -import { useState } from 'react'; +import { useState, useEffect } from 'react'; +import { createPortal } from 'react-dom'; import { Document, Page, pdfjs } from 'react-pdf'; import 'react-pdf/dist/Page/AnnotationLayer.css'; import 'react-pdf/dist/Page/TextLayer.css'; @@ -19,13 +20,23 @@ export function PreviewModal({ pdfBytes, onClose }: PreviewModalProps) { const [numPages, setNumPages] = useState(0); const [pageNumber, setPageNumber] = useState(1); - return ( + // Bug 1 fix: lock body scroll while modal is open + useEffect(() => { + const original = document.body.style.overflow; + document.body.style.overflow = 'hidden'; + return () => { document.body.style.overflow = original; }; + }, []); + + // Bug 2 fix: render via portal so the modal escapes the sticky sidebar's stacking + // context. The zIndex of 9999 ensures the backdrop sits above all field overlay boxes + // (zIndex: 10/12) and dnd-kit DragOverlay regardless of parent stacking contexts. + const modal = (
); + + return createPortal(modal, document.body); }