Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/app/(dashboard)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ export default function Layout({
<ModuleAvailabilityProvider>
<div className="flex h-dvh min-h-0 w-full overflow-hidden">
<AppSidebar />
<main className="relative flex h-dvh min-h-0 flex-1 flex-col overflow-hidden bg-background md:ml-[52px]">
{/* isolate keeps in-page stacking (logs tab) from covering sidebar flyouts */}
<main className="relative isolate flex h-dvh min-h-0 flex-1 flex-col overflow-hidden bg-background md:ml-[52px]">
<ModuleGuard>{children}</ModuleGuard>
</main>
<CommandPalette />
Expand Down
37 changes: 37 additions & 0 deletions src/components/logs-viewer/LogsDrawer.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { cleanup, render, screen, waitFor } from '@testing-library/react';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { LogsDrawer } from './LogsDrawer';

vi.mock('next/navigation', () => ({
usePathname: () => '/settings/general',
}));

vi.mock('@/lib/observability/lokiAvailability.actions', () => ({
getLokiAvailability: vi.fn(async () => ({ state: 'unreachable' })),
}));

vi.mock('@/lib/loki/requests', () => ({
getLogsLevels: vi.fn(async () => []),
getLogsQueryRange: vi.fn(async () => []),
getModules: vi.fn(async () => []),
}));

afterEach(() => {
cleanup();
});

describe('LogsDrawer', () => {
it('keeps the closed-tab trigger below overlay z-index', async () => {
render(
<main className="relative isolate">
<LogsDrawer />
</main>
);

const trigger = await waitFor(() =>
screen.getByRole('button', { name: /logs/i })
);
expect(trigger.className).toMatch(/\bz-20\b/);
expect(trigger.className).not.toMatch(/\bz-40\b/);
});
});
2 changes: 1 addition & 1 deletion src/components/logs-viewer/LogsDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export function LogsDrawer() {
<DrawerTrigger asChild>
<Button
variant="outline"
className="absolute bottom-0 left-4 z-40 h-8 gap-1.5 rounded-t-md rounded-b-none border-b-0 px-3"
className="absolute bottom-0 left-4 z-20 h-8 gap-1.5 rounded-t-md rounded-b-none border-b-0 px-3"
>
<Logs className="size-4" />
Logs
Expand Down
25 changes: 25 additions & 0 deletions src/components/ui/hover-card.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { cleanup, render, screen } from '@testing-library/react';
import { afterEach, describe, expect, it } from 'vitest';
import { HoverCard, HoverCardContent, HoverCardTrigger } from './hover-card';

afterEach(() => {
cleanup();
});

describe('HoverCard', () => {
it('portals content so sidebar flyouts are not trapped in the rail stacking context', () => {
const { container } = render(
<div className="fixed z-10" data-testid="sidebar">
<HoverCard open>
<HoverCardTrigger>Settings</HoverCardTrigger>
<HoverCardContent>General</HoverCardContent>
</HoverCard>
</div>
);

const flyout = screen.getByText('General');
expect(flyout).toBeInTheDocument();
expect(container).not.toContainElement(flyout);
expect(document.body).toContainElement(flyout);
});
});
22 changes: 12 additions & 10 deletions src/components/ui/hover-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,18 @@ const HoverCardContent = React.forwardRef<
React.ElementRef<typeof HoverCardPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => (
<HoverCardPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
'z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className
)}
{...props}
/>
<HoverCardPrimitive.Portal>
<HoverCardPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
'z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className
)}
{...props}
/>
</HoverCardPrimitive.Portal>
));
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName;

Expand Down
32 changes: 32 additions & 0 deletions src/components/ui/tooltip.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { cleanup, render, screen } from '@testing-library/react';
import { afterEach, describe, expect, it } from 'vitest';
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from './tooltip';

afterEach(() => {
cleanup();
});

describe('Tooltip', () => {
it('portals content so account labels are not trapped in the rail stacking context', () => {
const { container } = render(
<TooltipProvider delayDuration={0}>
<div className="fixed z-10" data-testid="sidebar">
<Tooltip open>
<TooltipTrigger>J</TooltipTrigger>
<TooltipContent>jordan</TooltipContent>
</Tooltip>
</div>
</TooltipProvider>
);

const label = screen.getByRole('tooltip', { name: 'jordan' });
expect(label).toBeInTheDocument();
expect(container).not.toContainElement(label);
expect(document.body).toContainElement(label);
});
});
20 changes: 11 additions & 9 deletions src/components/ui/tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,17 @@ const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<TooltipPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
'z-50 shadow-md rounded-md overflow-hidden bg-popover px-2.5 py-1 text-sm font-normal text-popover-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className
)}
{...props}
/>
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
'z-50 shadow-md rounded-md overflow-hidden bg-popover px-2.5 py-1 text-sm font-normal text-popover-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className
)}
{...props}
/>
</TooltipPrimitive.Portal>
));
TooltipContent.displayName = TooltipPrimitive.Content.displayName;

Expand Down
Loading