FN-7320: hide branch group card during expanded Activity chat

Keep maximized task Activity chat free of branch-group chrome and hidden focus targets.

- Skip mounting BranchGroupCard while the Activity chat is expanded.
- Cover branch-group and no-branch-group expanded chat states in TaskDetailModal tests.
- Add a patch changeset for the published Fusion package.

Files changed:
 .../fn-7320-hide-branch-group-expanded-chat.md     |  7 +++
 .../dashboard/app/components/TaskDetailModal.tsx   |  7 ++-
 .../TaskDetailModal.attachments-and-tabs.test.tsx  | 69 ++++++++++++++++++++--
 3 files changed, 77 insertions(+), 6 deletions(-)

Fusion-Task-Id: FN-7320

Fusion-Task-Lineage: 4fd7f328-5874-4924-bb17-6b29320a32ad

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-06-30 17:54:10 -07:00
parent db7b46f60f
commit 5e0c567199
3 changed files with 77 additions and 6 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Hide branch group chrome while the task Activity chat is maximized.
category: fix
dev: TaskDetailModal skips mounting BranchGroupCard during expanded Activity chat so branch controls are not focusable.

View File

@@ -2611,6 +2611,11 @@ export function TaskDetailContent({
const effectiveAutoMerge = resolveEffectiveAutoMerge({ autoMerge: task.autoMerge }, { autoMerge: autoMergeEnabled }); const effectiveAutoMerge = resolveEffectiveAutoMerge({ autoMerge: task.autoMerge }, { autoMerge: autoMergeEnabled });
const isManualPrFlow = mergeStrategy === "pull-request" && !effectiveAutoMerge; const isManualPrFlow = mergeStrategy === "pull-request" && !effectiveAutoMerge;
const isChatExpanded = chatExpanded && activeTab === "chat" && !isEditing; const isChatExpanded = chatExpanded && activeTab === "chat" && !isEditing;
/*
FNXC:TaskDetailChat 2026-06-30-23:30:
Maximized Activity chat should reserve the detail surface for the header context and chat only. Do not mount branch-group chrome in this mode so its expand/promote controls are not hidden-but-focusable, while normal and embedded task details keep the BranchGroupCard behavior.
*/
const shouldShowBranchGroupCard = Boolean(task.branchContext?.groupId && !isChatExpanded);
const taskActionMenuModel = useMemo(() => buildTaskActionMenuModel({ const taskActionMenuModel = useMemo(() => buildTaskActionMenuModel({
task, task,
@@ -3118,7 +3123,7 @@ export function TaskDetailContent({
)} )}
</div> </div>
</div> </div>
{task.branchContext?.groupId && ( {shouldShowBranchGroupCard && task.branchContext?.groupId && (
/* FNXC:BranchGroupDetails 2026-06-30-00:00: Task-detail branch groups must return to their compact collapsed default when users switch tasks, including between members of the same shared branch group. Key by task and group so a manual expansion never leaks into the next task detail view. */ /* FNXC:BranchGroupDetails 2026-06-30-00:00: Task-detail branch groups must return to their compact collapsed default when users switch tasks, including between members of the same shared branch group. Key by task and group so a manual expansion never leaks into the next task detail view. */
<BranchGroupCard key={`${task.id}:${task.branchContext.groupId}`} groupId={task.branchContext.groupId} projectId={projectId} /> <BranchGroupCard key={`${task.id}:${task.branchContext.groupId}`} groupId={task.branchContext.groupId} projectId={projectId} />
)} )}

View File

@@ -17,6 +17,14 @@ import {
} from "./TaskDetailModal.test-helpers"; } from "./TaskDetailModal.test-helpers";
import { TaskDetailModal, TaskDetailContent } from "../TaskDetailModal"; import { TaskDetailModal, TaskDetailContent } from "../TaskDetailModal";
vi.mock("../BranchGroupCard", () => ({
BranchGroupCard: ({ groupId }: { groupId: string }) => (
<section className="card branch-group-card" data-testid="mock-branch-group-card" aria-label={`Mock branch group ${groupId}`}>
<button type="button">Mock branch group toggle {groupId}</button>
</section>
),
}));
/* /*
FNXC:TaskDetailTabs 2026-06-17-08:20: FNXC:TaskDetailTabs 2026-06-17-08:20:
FN-7306 labels the stable internal `chat` tab as Activity and keeps it as the default TaskDetailModal tab. Definition-tab regression coverage must prove both the no-`initialTab` Activity landing state and the explicit `initialTab="definition"` Definition surface for prompt, GitHub tracking, and dependency sections. FN-7306 labels the stable internal `chat` tab as Activity and keeps it as the default TaskDetailModal tab. Definition-tab regression coverage must prove both the no-`initialTab` Activity landing state and the explicit `initialTab="definition"` Definition surface for prompt, GitHub tracking, and dependency sections.
@@ -790,19 +798,20 @@ describe("TaskDetailModal", () => {
); );
// For an in-progress task (no workflow steps, no merge commit), the // For an in-progress task (no workflow steps, no merge commit), the
// top-level tabs are: Activity, Plan, Changes, Review, Comments, // top-level tabs are: Activity, Chat, Plan, Changes, Review, Comments,
// Artifacts, Model, Workflow, Stats, Routing. // Artifacts, Model, Workflow, Stats, Routing.
const tabTexts = ["Activity", "Plan", "Changes", "Review", "Comments", "Artifacts", "Model", "Workflow", "Stats", "Routing"]; const tabTexts = ["Activity", "Chat", "Plan", "Changes", "Review", "Comments", "Artifacts", "Model", "Workflow", "Stats", "Routing"];
const tabs = screen.getAllByRole("button").filter((b) => const tabs = screen.getAllByRole("button").filter((b) =>
tabTexts.includes(b.textContent || "") tabTexts.includes(b.textContent || "")
); );
expect(tabs.map((tab) => tab.textContent)).toEqual(tabTexts); expect(tabs.map((tab) => tab.textContent)).toEqual(tabTexts);
expect(tabs[0].textContent).toBe("Activity"); expect(tabs[0].textContent).toBe("Activity");
expect(tabs[1].textContent).toBe("Plan"); expect(tabs[1].textContent).toBe("Chat");
expect(tabs[2].textContent).toBe("Changes"); expect(tabs[2].textContent).toBe("Plan");
expect(tabs[3].textContent).toBe("Changes");
expect(screen.queryByRole("button", { name: "Logs" })).toBeNull(); expect(screen.queryByRole("button", { name: "Logs" })).toBeNull();
expect(container.querySelectorAll(".detail-tab").length).toBe(10); expect(container.querySelectorAll(".detail-tab").length).toBe(11);
// Workflow tab should always appear even when no workflow steps are configured // Workflow tab should always appear even when no workflow steps are configured
expect(screen.getByText("Workflow")).toBeInTheDocument(); expect(screen.getByText("Workflow")).toBeInTheDocument();
// Commits tab should NOT appear for non-done tasks // Commits tab should NOT appear for non-done tasks
@@ -899,6 +908,56 @@ describe("TaskDetailModal", () => {
expect(screen.getByTestId("task-chat-expand-toggle")).toHaveAttribute("aria-pressed", "false"); expect(screen.getByTestId("task-chat-expand-toggle")).toHaveAttribute("aria-pressed", "false");
}); });
it("FN-7320 removes branch group chrome only while Activity chat is expanded", () => {
const branchContext = { groupId: "BG-7320", source: "planning", assignmentMode: "shared" } as const;
const { container } = render(
<TaskDetailModal
task={makeTask({ prompt: "# Hello\n\nContent", branchContext })}
onClose={noop}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
onMergeTask={noopMerge}
onOpenDetail={noopOpenDetail}
addToast={noop}
/>,
);
const content = container.querySelector(".task-detail-content");
expect(content).not.toHaveClass("task-detail-content--chat-expanded");
expect(screen.getByTestId("mock-branch-group-card")).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Mock branch group toggle BG-7320" })).toBeInTheDocument();
fireEvent.click(screen.getByTestId("task-chat-expand-toggle"));
expect(content).toHaveClass("task-detail-content--chat-expanded");
expect(screen.queryByTestId("mock-branch-group-card")).toBeNull();
expect(screen.queryByRole("button", { name: "Mock branch group toggle BG-7320" })).toBeNull();
fireEvent.click(screen.getByTestId("task-chat-expand-toggle"));
expect(content).not.toHaveClass("task-detail-content--chat-expanded");
expect(screen.getByTestId("mock-branch-group-card")).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Mock branch group toggle BG-7320" })).toBeInTheDocument();
});
it("FN-7320 expands Activity chat for tasks without branch groups without rendering branch shells", () => {
const { container } = render(
<TaskDetailModal
task={makeTask({ prompt: "# Hello\n\nContent", branchContext: undefined })}
onClose={noop}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
onMergeTask={noopMerge}
onOpenDetail={noopOpenDetail}
addToast={noop}
/>,
);
expect(screen.queryByTestId("mock-branch-group-card")).toBeNull();
fireEvent.click(screen.getByTestId("task-chat-expand-toggle"));
expect(container.querySelector(".task-detail-content")).toHaveClass("task-detail-content--chat-expanded");
expect(screen.queryByTestId("mock-branch-group-card")).toBeNull();
expect(screen.queryByRole("button", { name: /Mock branch group toggle/ })).toBeNull();
});
it("FN-6517 keeps the title row visible when embedded chat expands", () => { it("FN-6517 keeps the title row visible when embedded chat expands", () => {
const { container } = render( const { container } = render(
<TaskDetailContent <TaskDetailContent