From 4b7ea239d4765d41c1b1dc9056062315be54a331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Flor=C3=AAncio?= Date: Mon, 24 Aug 2026 19:24:15 +0000 Subject: [PATCH] fix(web): scope task title previews to Kanban (#2939) --- apps/web/app/tasks/rich-task-list-row.tsx | 9 +- .../components/kanban-card-content.test.tsx | 12 +- apps/web/components/task/task-item.tsx | 20 +-- .../task/task-title-hover-card.test.tsx | 25 +++- .../components/task/task-title-hover-card.tsx | 16 +-- apps/web/e2e/pages/session-page.ts | 30 ++-- .../kanban/task-title-hover-subtasks.spec.ts | 14 +- apps/web/e2e/tests/pr/pr-status-badge.spec.ts | 5 +- .../tests/task/create-task-github-url.spec.ts | 4 +- .../task/task-title-hover-surfaces.spec.ts | 129 ++++-------------- apps/web/hooks/use-lazy-load-sentinel.test.ts | 9 +- docs/plans/rich-task-title-previews/plan.md | 42 +++++- .../task-04-kanban-only-scope.md | 55 ++++++++ .../requirements/rich-task-title-previews.md | 28 +++- 14 files changed, 220 insertions(+), 178 deletions(-) create mode 100644 docs/plans/rich-task-title-previews/task-04-kanban-only-scope.md diff --git a/apps/web/app/tasks/rich-task-list-row.tsx b/apps/web/app/tasks/rich-task-list-row.tsx index c680ca4d83..87a494ddb0 100644 --- a/apps/web/app/tasks/rich-task-list-row.tsx +++ b/apps/web/app/tasks/rich-task-list-row.tsx @@ -6,7 +6,6 @@ import { PRTaskIcon } from "@/components/github/pr-task-icon"; import { RegisteredChangeRequestTaskIcon } from "@/components/integrations/registered-change-request-task-icon"; import { TaskRowMetadata } from "@/components/task/task-row-plugin-slots"; import { MRTaskIcon } from "@/components/gitlab/mr-task-icon"; -import { TaskTitleHoverCard } from "@/components/task/task-title-hover-card"; import { useTaskPendingInput, type PendingInput } from "@/hooks/use-task-pending-input"; import { getTaskStateIcon } from "@/lib/ui/state-icons"; import type { Repository, Task } from "@/lib/types/http"; @@ -45,11 +44,9 @@ function PrimaryTaskLine({ hasPendingPermission: pendingInput.permission, interrupted: task.interrupted, })} - - - {task.title} - - + + {task.title} + {showContributions && ( }, + kanban: { tasks: [] as Array<{ id: string; title: string; parentTaskId?: string }> }, kanbanMulti: { snapshots: {} as Record }> }, taskPRs: { byTaskId: {} as Record }, }; @@ -49,6 +49,7 @@ function SlotPropsProbe({ testId, slotProps }: { testId: string; slotProps?: unk afterEach(() => { cleanup(); + mockAppState.kanban.tasks = []; pluginRegistry.unregisterPlugin(NOTES_PLUGIN_ID); pluginRegistry.unregisterPlugin(SECOND_PLUGIN_ID); }); @@ -171,7 +172,14 @@ describe("KanbanCardBody — title hover card gating", () => { expect(title.getAttribute("data-slot")).not.toBe("hover-card-trigger"); }); - it("mounts the hover card trigger around the title when enableTitleHover is set", () => { + it("does not mount a hover card trigger for a childless task", () => { + render(); + const title = screen.getByTestId(TITLE_TEST_ID); + expect(title.closest('[data-testid="task-title-preview-trigger"]')).toBeNull(); + }); + + it("mounts the hover card trigger when an active direct subtask exists", () => { + mockAppState.kanban.tasks = [{ id: "child-1", title: "Child", parentTaskId: TASK.id }]; render(); const title = screen.getByTestId(TITLE_TEST_ID); expect(title.closest('[data-testid="task-title-preview-trigger"]')).not.toBeNull(); diff --git a/apps/web/components/task/task-item.tsx b/apps/web/components/task/task-item.tsx index 2e9d568fd3..6c55c019c4 100644 --- a/apps/web/components/task/task-item.tsx +++ b/apps/web/components/task/task-item.tsx @@ -27,9 +27,8 @@ import { TaskRowMetadata } from "./task-row-plugin-slots"; import { classifyTask } from "./task-classify"; import { ScrollOnOverflow } from "@kandev/ui/scroll-on-overflow"; import { useTranslation } from "react-i18next"; -import { TaskTitleHoverCard } from "@/components/task/task-title-hover-card"; -import type { WipQueueStatus } from "@/lib/kanban/wip-queue"; import { TaskItemComparisonUnavailable } from "./task-item-comparison-unavailable"; +import type { WipQueueStatus } from "@/lib/kanban/wip-queue"; import { TaskMenuButton } from "./task-item-menu-button"; import { TaskItemLeadingBadges } from "./task-item-leading-badges"; @@ -326,19 +325,8 @@ function DiffStatsRight({ diffStats, menuOpen }: { diffStats: DiffStats; menuOpe ); } -function TaskItemTitle({ taskId, title }: { taskId?: string; title: string }) { - // w-full: ScrollOnOverflow's root is inline-block, so once it sits inside - // the title-preview trigger's