Render status dropdown via React portal to fix transparency

- Menu now renders on document.body via createPortal
- Positioned using getBoundingClientRect from the badge button
- Eliminates all parent stacking context / overflow issues
This commit is contained in:
Adam Moussa 2026-04-16 18:30:45 -04:00
parent 4c54d3499f
commit 24c83c6535
2 changed files with 35 additions and 23 deletions

View file

@ -34,14 +34,10 @@
.woStatusBadge.done, .woStatusBadge.completed { background: #28a745; }
.woStatusBadge.cancelled { background: #dc3545; }
.woStatusDropdown {
position: relative;
display: inline-block;
}
.woStatusClickable {
cursor: pointer;
user-select: none;
border: none;
}
.woStatusClickable:hover {
@ -54,17 +50,14 @@
}
.woStatusMenu {
position: absolute;
top: 100%;
left: 0;
margin-top: 4px;
background: #ffffff;
border: 1px solid #ddd;
position: fixed;
background: #fff;
border: 1px solid #ccc;
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 1000;
min-width: 160px;
overflow: hidden;
box-shadow: 0 6px 20px rgba(0,0,0,0.2);
z-index: 99999;
min-width: 170px;
padding: 4px 0;
}
.woStatusOption {

View file

@ -1,4 +1,5 @@
import React, { useState, useEffect, useRef } from "react";
import ReactDOM from "react-dom";
import { useParams, useNavigate } from "react-router";
import { toast } from "react-toastify";
import { getWorkOrderById, addComment, changeWorkOrderStatus } from "../api";
@ -121,27 +122,44 @@ const STATUS_OPTIONS = ["Open", "In Progress", "On Hold", "Done", "Cancelled"];
function StatusDropdown({ status, onStatusChange }) {
const [open, setOpen] = useState(false);
const ref = useRef(null);
const btnRef = useRef(null);
const menuRef = useRef(null);
const [menuPos, setMenuPos] = useState({ top: 0, left: 0 });
const statusClass = (status || "").toLowerCase().replace(/\s+/g, "-");
useEffect(() => {
const handleClick = (e) => {
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
if (btnRef.current?.contains(e.target)) return;
if (menuRef.current?.contains(e.target)) return;
setOpen(false);
};
document.addEventListener("mousedown", handleClick);
return () => document.removeEventListener("mousedown", handleClick);
}, []);
const handleOpen = () => {
if (btnRef.current) {
const rect = btnRef.current.getBoundingClientRect();
setMenuPos({ top: rect.bottom + 4, left: rect.left });
}
setOpen(!open);
};
return (
<div className="woStatusDropdown" ref={ref}>
<>
<button
ref={btnRef}
className={`woStatusBadge ${statusClass} woStatusClickable`}
onClick={() => setOpen(!open)}
onClick={handleOpen}
>
{status} <span className="woStatusCaret">&#9662;</span>
</button>
{open && (
<div className="woStatusMenu">
{open && ReactDOM.createPortal(
<div
ref={menuRef}
className="woStatusMenu"
style={{ top: menuPos.top, left: menuPos.left }}
>
{STATUS_OPTIONS.map((s) => (
<button
key={s}
@ -152,9 +170,10 @@ function StatusDropdown({ status, onStatusChange }) {
{s}
</button>
))}
</div>
</div>,
document.body
)}
</div>
</>
);
}