FN-7972: fix agents controls panel overlapping content

Elevate the Agents controls popover stacking so it layers above agent cards and token usage on desktop and mobile.

- Add `.agents-view-primary-actions--controls-open` with z-index 50 when the controls panel is open
- Toggle the elevated class from AgentsView when the panel opens
- Extend AgentsView tests for open-state class and stacking CSS invariant
- Add patch changeset for the operator-facing fix

Files changed:
 .changeset/fn-7972-agents-controls-overlap.md           |  7 +++++++
 packages/dashboard/app/components/AgentsView.css        |  8 ++++++++
 packages/dashboard/app/components/AgentsView.tsx        |  2 +-
 .../app/components/__tests__/AgentsView.test.tsx        | 17 ++++++++++++-----
 4 files changed, 28 insertions(+), 6 deletions(-)

Fusion-Task-Id: FN-7972

Fusion-Task-Lineage: e6cca457-1cd1-4577-83a7-d7de7e484580

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-15 14:10:28 -07:00
parent 0863c0fb58
commit aa0d8635fa
4 changed files with 28 additions and 6 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Fix Agents controls panel overlapping surrounding content on narrow viewports.
category: fix
dev: Elevated the .agent-controls-panel stacking context in AgentsView.css so the open controls popover sits above agent cards and token usage.

View File

@@ -101,6 +101,14 @@ The former scoped overrides (.agents-view .view-header padding/border/background
gap: var(--space-sm);
}
/*
FNXC:AgentsControlsOverlay 2026-07-15-00:00:
FN-7972 requires the open controls panel to layer above agent cards and Token Usage on desktop and mobile. Elevate only its active anchor so unrelated dashboard overlays keep their existing stacking order.
*/
.agents-view-primary-actions--controls-open {
z-index: 50;
}
.agent-controls-trigger {
white-space: nowrap;
}

View File

@@ -1401,7 +1401,7 @@ export function AgentsView({ addToast, projectId, onOpenTaskLogs, agentOnboardin
<Network size={16} />
</button>
</div>
<div className="agents-view-primary-actions">
<div className={`agents-view-primary-actions${isControlsPanelOpen ? " agents-view-primary-actions--controls-open" : ""}`}>
<button
ref={controlsTriggerRef}
className={`btn-icon agent-controls-trigger${isControlsPanelOpen ? " agent-controls-trigger--active" : ""}`}

View File

@@ -647,13 +647,15 @@ describe("AgentsView", () => {
});
});
it("keeps New Agent directly accessible on desktop while controls live in popup", async () => {
renderView(<AgentsView addToast={mockAddToast} />);
it("keeps New Agent directly accessible on desktop while controls live in an elevated popup", async () => {
const { container } = renderView(<AgentsView addToast={mockAddToast} />);
expect(screen.getByRole("button", { name: "New Agent" })).toBeTruthy();
expect(screen.queryByRole("dialog", { name: "Agent controls" })).toBeNull();
expect(container.querySelector(".agents-view-primary-actions--controls-open")).toBeNull();
await openControlsPanel();
expect(container.querySelector(".agents-view-primary-actions--controls-open")).toBeTruthy();
expect(screen.getByLabelText("Filter agents by state")).toBeTruthy();
expect(screen.getByLabelText("Show system agents")).toBeTruthy();
expect(screen.getAllByRole("button", { name: "Import" }).length).toBeGreaterThan(0);
@@ -661,14 +663,15 @@ describe("AgentsView", () => {
expect(screen.getByLabelText("Heartbeat speed preset")).toBeTruthy();
});
it("moves import and new-agent actions into the controls popup on mobile", async () => {
it("moves import and new-agent actions into an elevated controls popup on mobile", async () => {
mockViewportMode.mockReturnValue("mobile");
renderView(<AgentsView addToast={mockAddToast} />);
const { container } = renderView(<AgentsView addToast={mockAddToast} />);
expect(screen.queryByRole("button", { name: "Import" })).toBeNull();
expect(screen.queryByRole("button", { name: "New Agent" })).toBeNull();
await openControlsPanel();
expect(container.querySelector(".agents-view-primary-actions--controls-open")).toBeTruthy();
expect(screen.getByRole("button", { name: "Import" })).toBeTruthy();
expect(screen.getByRole("button", { name: "New Agent" })).toBeTruthy();
});
@@ -1629,10 +1632,14 @@ describe("AgentsView", () => {
expect(css).toContain("pointer-events: none");
});
it("keeps a compact mobile Agents label visible, anchors the controls popup to the action row, and expands view toggles to 36px touch targets", () => {
it("keeps a compact mobile Agents label visible, elevates the open controls overlay above Agents content, and expands view toggles to 36px touch targets", () => {
const css = loadAllAppCss();
const mobileCss = css.slice(css.indexOf("@media (max-width: 768px)"));
expect(css).toContain(".agents-view-primary-actions {\n position: relative;");
expect(css).toContain(".agents-view-primary-actions--controls-open {\n z-index: 50;\n}");
expect(css).toContain(".agent-controls-panel {\n position: absolute;\n top: calc(100% + var(--space-sm));\n right: 0;");
expect(css).toContain("background: var(--card);");
expect(mobileCss).not.toMatch(/\.agents-view-primary-actions--controls-open\s*\{[^}]*z-index:\s*(?:auto|0)/);
expect(css).toContain(".agents-view-title h2 {\n display: block;\n font-size: var(--space-lg);");
expect(css).toContain(".agent-controls-mobile-actions {");
expect(css).toContain(".agent-controls-mobile-actions .btn {");