diff --git a/frontend/src/components/sidebar/StackRow.tsx b/frontend/src/components/sidebar/StackRow.tsx
index b470539a..0586c6be 100644
--- a/frontend/src/components/sidebar/StackRow.tsx
+++ b/frontend/src/components/sidebar/StackRow.tsx
@@ -1,10 +1,10 @@
import type { ReactNode } from 'react';
import { GitBranch, Loader2, AlertCircle } from 'lucide-react';
import type { CheckStatus } from '@/types/imageUpdates';
-import { Cursor, CursorContainer, CursorFollow, CursorProvider } from '@/components/animate-ui/primitives/animate/cursor';
import { Checkbox } from '@/components/ui/checkbox';
import type { Label } from '@/components/label-types';
import { cn } from '@/lib/utils';
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import { sidebarRowActive, sidebarRowBase, sidebarRowCheckboxSlot } from './sidebar-styles';
import { statusText, statusColor } from './stack-status-utils';
import type { StackRowStatus } from './stack-status-utils';
@@ -34,15 +34,14 @@ interface StackRowProps {
function RowTooltip({ trigger, label }: { trigger: ReactNode; label: string }) {
return (
-
- {trigger}
-
-
-
+
+
+ {trigger}
+ {label}
-
-
-
+
+
+
);
}
diff --git a/frontend/src/components/sidebar/__tests__/StackRow.test.tsx b/frontend/src/components/sidebar/__tests__/StackRow.test.tsx
index 7c98c802..a200979c 100644
--- a/frontend/src/components/sidebar/__tests__/StackRow.test.tsx
+++ b/frontend/src/components/sidebar/__tests__/StackRow.test.tsx
@@ -43,17 +43,15 @@ describe('StackRow', () => {
});
it('wraps the partial pill in a hover tooltip', () => {
- // jsdom does not mount the cursor-follow label, so assert the PT trigger is
- // wrapped in the RowTooltip cursor-container; the visible "3/5 running"
- // tooltip text is verified in the Playwright drive.
const { container } = render();
- expect(screen.getByText('PT').closest('[data-slot="cursor-container"]')).not.toBeNull();
- expect(container.querySelector('[data-slot="cursor-container"]')).not.toBeNull();
+ const trigger = screen.getByText('PT');
+ // Radix TooltipTrigger adds data-state to the wrapped element.
+ expect(trigger.getAttribute('data-state')).toBe('closed');
});
it('does not wrap a non-partial pill in a tooltip', () => {
render();
- expect(screen.getByText('UP').closest('[data-slot="cursor-container"]')).toBeNull();
+ expect(screen.getByText('UP').getAttribute('data-state')).toBeNull();
});
it('renders cyan rail only when active', () => {
@@ -103,12 +101,12 @@ describe('StackRow', () => {
});
// ── Image-update check status indicator ────────────────────────────────
- // status='running' renders the pill as plain text (no tooltip), so the only
- // cursor-container in these rows is the trailing update/check indicator.
it('shows a muted check-failed indicator when the last check failed and there is no update', () => {
const { container } = render();
- expect(container.querySelector('[data-slot="cursor-container"]')).not.toBeNull();
+ // The trailing slot renders a tooltip-wrapped AlertCircle icon (an SVG).
+ const trailingSlot = container.querySelector('[data-state="closed"]');
+ expect(trailingSlot).not.toBeNull();
// It is not the update dot.
expect(container.querySelector('.bg-update')).toBeNull();
});
@@ -120,7 +118,7 @@ describe('StackRow', () => {
it('shows no trailing indicator for a clean ok check with no update', () => {
const { container } = render();
- expect(container.querySelector('[data-slot="cursor-container"]')).toBeNull();
+ expect(container.querySelector('.lucide-alert-circle')).toBeNull();
expect(container.querySelector('.bg-update')).toBeNull();
});
});