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:
7
.changeset/fn-7972-agents-controls-overlap.md
Normal file
7
.changeset/fn-7972-agents-controls-overlap.md
Normal 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.
|
||||||
@@ -101,6 +101,14 @@ The former scoped overrides (.agents-view .view-header padding/border/background
|
|||||||
gap: var(--space-sm);
|
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 {
|
.agent-controls-trigger {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1401,7 +1401,7 @@ export function AgentsView({ addToast, projectId, onOpenTaskLogs, agentOnboardin
|
|||||||
<Network size={16} />
|
<Network size={16} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="agents-view-primary-actions">
|
<div className={`agents-view-primary-actions${isControlsPanelOpen ? " agents-view-primary-actions--controls-open" : ""}`}>
|
||||||
<button
|
<button
|
||||||
ref={controlsTriggerRef}
|
ref={controlsTriggerRef}
|
||||||
className={`btn-icon agent-controls-trigger${isControlsPanelOpen ? " agent-controls-trigger--active" : ""}`}
|
className={`btn-icon agent-controls-trigger${isControlsPanelOpen ? " agent-controls-trigger--active" : ""}`}
|
||||||
|
|||||||
@@ -647,13 +647,15 @@ describe("AgentsView", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps New Agent directly accessible on desktop while controls live in popup", async () => {
|
it("keeps New Agent directly accessible on desktop while controls live in an elevated popup", async () => {
|
||||||
renderView(<AgentsView addToast={mockAddToast} />);
|
const { container } = renderView(<AgentsView addToast={mockAddToast} />);
|
||||||
|
|
||||||
expect(screen.getByRole("button", { name: "New Agent" })).toBeTruthy();
|
expect(screen.getByRole("button", { name: "New Agent" })).toBeTruthy();
|
||||||
expect(screen.queryByRole("dialog", { name: "Agent controls" })).toBeNull();
|
expect(screen.queryByRole("dialog", { name: "Agent controls" })).toBeNull();
|
||||||
|
expect(container.querySelector(".agents-view-primary-actions--controls-open")).toBeNull();
|
||||||
|
|
||||||
await openControlsPanel();
|
await openControlsPanel();
|
||||||
|
expect(container.querySelector(".agents-view-primary-actions--controls-open")).toBeTruthy();
|
||||||
expect(screen.getByLabelText("Filter agents by state")).toBeTruthy();
|
expect(screen.getByLabelText("Filter agents by state")).toBeTruthy();
|
||||||
expect(screen.getByLabelText("Show system agents")).toBeTruthy();
|
expect(screen.getByLabelText("Show system agents")).toBeTruthy();
|
||||||
expect(screen.getAllByRole("button", { name: "Import" }).length).toBeGreaterThan(0);
|
expect(screen.getAllByRole("button", { name: "Import" }).length).toBeGreaterThan(0);
|
||||||
@@ -661,14 +663,15 @@ describe("AgentsView", () => {
|
|||||||
expect(screen.getByLabelText("Heartbeat speed preset")).toBeTruthy();
|
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");
|
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: "Import" })).toBeNull();
|
||||||
expect(screen.queryByRole("button", { name: "New Agent" })).toBeNull();
|
expect(screen.queryByRole("button", { name: "New Agent" })).toBeNull();
|
||||||
|
|
||||||
await openControlsPanel();
|
await openControlsPanel();
|
||||||
|
expect(container.querySelector(".agents-view-primary-actions--controls-open")).toBeTruthy();
|
||||||
expect(screen.getByRole("button", { name: "Import" })).toBeTruthy();
|
expect(screen.getByRole("button", { name: "Import" })).toBeTruthy();
|
||||||
expect(screen.getByRole("button", { name: "New Agent" })).toBeTruthy();
|
expect(screen.getByRole("button", { name: "New Agent" })).toBeTruthy();
|
||||||
});
|
});
|
||||||
@@ -1629,10 +1632,14 @@ describe("AgentsView", () => {
|
|||||||
expect(css).toContain("pointer-events: none");
|
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 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 {\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(".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(".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 {");
|
||||||
expect(css).toContain(".agent-controls-mobile-actions .btn {");
|
expect(css).toContain(".agent-controls-mobile-actions .btn {");
|
||||||
|
|||||||
Reference in New Issue
Block a user