Fix dropdown clipping: remove overflow constraints on parent containers
Revert fixed-positioning approach (caused z-index and placement issues). Instead fix the root cause: parent containers with overflow that clipped absolutely-positioned dropdowns. - IAMPage: Remove overflow-x-auto on table wrapper (columns already hide via responsive classes, no horizontal scroll needed) - AlertBanner: Remove max-h-48 overflow-y-auto on alerts list (alerts are naturally bounded, constraint clipped SnoozeDropdown) - Revert UserActionsMenu and SnoozeDropdown to simple absolute positioning Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a327890b57
commit
0f6e40a5ba
@ -160,7 +160,7 @@ export default function IAMPage() {
|
|||||||
{searchQuery ? 'No users match your search.' : 'No users found.'}
|
{searchQuery ? 'No users match your search.' : 'No users found.'}
|
||||||
</p>
|
</p>
|
||||||
) : (
|
) : (
|
||||||
<div className="overflow-x-auto">
|
<div>
|
||||||
<table className="w-full text-sm">
|
<table className="w-full text-sm">
|
||||||
<thead>
|
<thead>
|
||||||
<tr className="border-b border-border bg-card-elevated/50">
|
<tr className="border-b border-border bg-card-elevated/50">
|
||||||
|
|||||||
@ -46,8 +46,6 @@ export default function UserActionsMenu({ user, currentUsername }: UserActionsMe
|
|||||||
const [roleSubmenuOpen, setRoleSubmenuOpen] = useState(false);
|
const [roleSubmenuOpen, setRoleSubmenuOpen] = useState(false);
|
||||||
const [tempPassword, setTempPassword] = useState<string | null>(null);
|
const [tempPassword, setTempPassword] = useState<string | null>(null);
|
||||||
const menuRef = useRef<HTMLDivElement>(null);
|
const menuRef = useRef<HTMLDivElement>(null);
|
||||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
|
||||||
const [menuPos, setMenuPos] = useState<{ top: number; right: number } | null>(null);
|
|
||||||
|
|
||||||
const updateRole = useUpdateRole();
|
const updateRole = useUpdateRole();
|
||||||
const resetPassword = useResetPassword();
|
const resetPassword = useResetPassword();
|
||||||
@ -125,17 +123,10 @@ export default function UserActionsMenu({ user, currentUsername }: UserActionsMe
|
|||||||
return (
|
return (
|
||||||
<div ref={menuRef} className="relative">
|
<div ref={menuRef} className="relative">
|
||||||
<Button
|
<Button
|
||||||
ref={buttonRef}
|
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
className="h-7 w-7"
|
className="h-7 w-7"
|
||||||
onClick={() => {
|
onClick={() => setOpen((v) => !v)}
|
||||||
if (!open && buttonRef.current) {
|
|
||||||
const rect = buttonRef.current.getBoundingClientRect();
|
|
||||||
setMenuPos({ top: rect.bottom + 4, right: window.innerWidth - rect.right });
|
|
||||||
}
|
|
||||||
setOpen((v) => !v);
|
|
||||||
}}
|
|
||||||
aria-label="User actions"
|
aria-label="User actions"
|
||||||
>
|
>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
@ -146,10 +137,7 @@ export default function UserActionsMenu({ user, currentUsername }: UserActionsMe
|
|||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{open && (
|
{open && (
|
||||||
<div
|
<div className="absolute right-0 top-8 z-50 min-w-[200px] rounded-lg border bg-card shadow-lg py-1">
|
||||||
className="fixed z-50 min-w-[200px] rounded-lg border bg-card shadow-lg py-1"
|
|
||||||
style={menuPos ? { top: menuPos.top, right: menuPos.right } : undefined}
|
|
||||||
>
|
|
||||||
{/* Edit Role */}
|
{/* Edit Role */}
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<button
|
<button
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export default function AlertBanner({ alerts, onDismiss, onSnooze }: AlertBanner
|
|||||||
{alerts.length}
|
{alerts.length}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="divide-y divide-border max-h-48 overflow-y-auto">
|
<div className="divide-y divide-border">
|
||||||
{alerts.map((alert) => (
|
{alerts.map((alert) => (
|
||||||
<div
|
<div
|
||||||
key={alert.id}
|
key={alert.id}
|
||||||
|
|||||||
@ -17,8 +17,6 @@ const DEFAULT_OPTIONS: { value: number; label: string }[] = [
|
|||||||
export default function SnoozeDropdown({ onSnooze, label, direction = 'up', options = DEFAULT_OPTIONS }: SnoozeDropdownProps) {
|
export default function SnoozeDropdown({ onSnooze, label, direction = 'up', options = DEFAULT_OPTIONS }: SnoozeDropdownProps) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
|
||||||
const [menuPos, setMenuPos] = useState<{ top?: number; bottom?: number; right: number } | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open) return;
|
if (!open) return;
|
||||||
@ -41,18 +39,7 @@ export default function SnoozeDropdown({ onSnooze, label, direction = 'up', opti
|
|||||||
return (
|
return (
|
||||||
<div className="relative" ref={ref}>
|
<div className="relative" ref={ref}>
|
||||||
<button
|
<button
|
||||||
ref={buttonRef}
|
onClick={() => setOpen(!open)}
|
||||||
onClick={() => {
|
|
||||||
if (!open && buttonRef.current) {
|
|
||||||
const rect = buttonRef.current.getBoundingClientRect();
|
|
||||||
if (direction === 'up') {
|
|
||||||
setMenuPos({ bottom: window.innerHeight - rect.top + 4, right: window.innerWidth - rect.right });
|
|
||||||
} else {
|
|
||||||
setMenuPos({ top: rect.bottom + 4, right: window.innerWidth - rect.right });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setOpen(!open);
|
|
||||||
}}
|
|
||||||
aria-label={`Snooze "${label}"`}
|
aria-label={`Snooze "${label}"`}
|
||||||
aria-expanded={open}
|
aria-expanded={open}
|
||||||
aria-haspopup="menu"
|
aria-haspopup="menu"
|
||||||
@ -62,11 +49,9 @@ export default function SnoozeDropdown({ onSnooze, label, direction = 'up', opti
|
|||||||
<span className="text-[11px] font-medium">Snooze</span>
|
<span className="text-[11px] font-medium">Snooze</span>
|
||||||
</button>
|
</button>
|
||||||
{open && (
|
{open && (
|
||||||
<div
|
<div role="menu" className={`absolute right-0 w-32 rounded-md border bg-popover shadow-lg z-50 py-1 animate-fade-in ${
|
||||||
role="menu"
|
direction === 'up' ? 'bottom-full mb-1' : 'top-full mt-1'
|
||||||
className="fixed w-32 rounded-md border bg-popover shadow-lg z-50 py-1 animate-fade-in"
|
}`}>
|
||||||
style={menuPos ? { ...menuPos } : undefined}
|
|
||||||
>
|
|
||||||
{options.map((opt) => (
|
{options.map((opt) => (
|
||||||
<button
|
<button
|
||||||
key={opt.value}
|
key={opt.value}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user