feat(FN-3132): tokenize sidebar titles and mission manager sizing, add Agen
Merged branch adds the new AgentsOverviewBar component with accompanying test coverage, fixes CSS token issues across MissionManager and sidebar typography (including resolving undefined surface-hover token fallback), and updates agents layout documentation. Also adds test coverage for the insights Fusion-Task-Id: FN-3132
This commit is contained in:
@@ -17,18 +17,18 @@
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: color-mix(in srgb, var(--bg) 60%, transparent);
|
||||
backdrop-filter: blur(4px);
|
||||
backdrop-filter: blur(var(--space-xs));
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
padding-top: 8vh;
|
||||
padding-top: var(--overlay-padding-top);
|
||||
}
|
||||
|
||||
/* ── Shell ── */
|
||||
.mission-manager {
|
||||
width: min(860px, 94vw);
|
||||
max-height: min(88vh, 800px);
|
||||
width: min(calc(var(--space-lg) * 54), calc(100vw - (var(--space-lg) * 2)));
|
||||
max-height: calc(100dvh - var(--overlay-padding-top) - var(--space-lg));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
@@ -86,6 +86,15 @@
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
/* Header title keeps a static desktop label while preserving mobile dynamic text. */
|
||||
.mission-manager__title-text--mobile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mission-manager__title-text--desktop {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.mission-manager__header-icon {
|
||||
color: var(--todo);
|
||||
flex-shrink: 0;
|
||||
@@ -162,17 +171,19 @@
|
||||
.mission-manager__sidebar-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
justify-content: flex-end;
|
||||
gap: var(--space-sm);
|
||||
padding: var(--space-md);
|
||||
border-bottom: var(--btn-border-width) solid var(--border);
|
||||
}
|
||||
|
||||
/* Hide duplicate sidebar title; the shared header already labels this view. */
|
||||
.mission-manager__sidebar-title {
|
||||
display: none;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.75rem;
|
||||
font-size: calc(var(--space-xs) * 3);
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
letter-spacing: var(--btn-border-width);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
@@ -1599,7 +1610,7 @@
|
||||
}
|
||||
|
||||
.mission-assertion__feature-picker-item:hover:not(:disabled) {
|
||||
background: var(--surface-hover);
|
||||
background: var(--surface-hover, var(--card-hover));
|
||||
}
|
||||
|
||||
.mission-assertion__feature-picker-item:disabled {
|
||||
@@ -2186,6 +2197,15 @@
|
||||
padding: var(--space-md);
|
||||
}
|
||||
|
||||
/* Restore dynamic header title text on mobile. */
|
||||
.mission-manager__title-text--mobile {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.mission-manager__title-text--desktop {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mission-manager__body {
|
||||
padding: var(--space-md);
|
||||
}
|
||||
|
||||
@@ -3747,8 +3747,11 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
</button>
|
||||
)}
|
||||
<Target size={18} className="mission-manager__header-icon" />
|
||||
<h2 className="mission-manager__title">
|
||||
{selectedMission ? selectedMission.title : "Missions"}
|
||||
<h2 className="mission-manager__title" data-testid="mission-header-title">
|
||||
<span className="mission-manager__title-text mission-manager__title-text--desktop">Missions</span>
|
||||
<span className="mission-manager__title-text mission-manager__title-text--mobile">
|
||||
{selectedMission ? selectedMission.title : "Missions"}
|
||||
</span>
|
||||
</h2>
|
||||
</div>
|
||||
{!isInline && (
|
||||
|
||||
@@ -57,4 +57,18 @@ describe("MissionManager mobile styles", () => {
|
||||
expect(section).toContain(".mission-manager__body--stacked {");
|
||||
expect(section).toContain("display: block;");
|
||||
});
|
||||
|
||||
it("keeps desktop defaults and mobile overrides for header title spans", () => {
|
||||
const css = loadAllAppCss();
|
||||
const desktopMobileSpanBlock = css.match(/\.mission-manager__title-text--mobile\s*\{[^}]*\}/)?.[0];
|
||||
const desktopDesktopSpanBlock = css.match(/\.mission-manager__title-text--desktop\s*\{[^}]*\}/)?.[0];
|
||||
expect(desktopMobileSpanBlock).toContain("display: none");
|
||||
expect(desktopDesktopSpanBlock).toContain("display: inline");
|
||||
|
||||
const section = getMissionMobileSection(css);
|
||||
const mobileBlock = section.match(/\.mission-manager__title-text--mobile\s*\{[^}]*\}/)?.[0];
|
||||
const desktopBlock = section.match(/\.mission-manager__title-text--desktop\s*\{[^}]*\}/)?.[0];
|
||||
expect(mobileBlock).toContain("display: inline");
|
||||
expect(desktopBlock).toContain("display: none");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -719,7 +719,57 @@ describe("MissionManager", () => {
|
||||
render(<MissionManager isOpen={true} onClose={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("heading", { name: "Missions" })).toBeDefined();
|
||||
expect(screen.getByTestId("mission-header-title")).toBeDefined();
|
||||
expect(screen.getByTestId("mission-header-title").textContent).toContain("Missions");
|
||||
});
|
||||
});
|
||||
|
||||
describe("desktop header behavior", () => {
|
||||
it("shows static Missions title on desktop when no mission is selected", async () => {
|
||||
globalThis.fetch = createFetchMock();
|
||||
render(<MissionManager isOpen={true} onClose={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
await waitFor(() => {
|
||||
const header = screen.getByTestId("mission-header-title");
|
||||
const desktopSpan = header.querySelector(".mission-manager__title-text--desktop");
|
||||
const mobileSpan = header.querySelector(".mission-manager__title-text--mobile");
|
||||
expect(desktopSpan?.textContent).toBe("Missions");
|
||||
expect(mobileSpan?.textContent).toBe("Missions");
|
||||
});
|
||||
});
|
||||
|
||||
it("shows static Missions title on desktop when a mission is selected", async () => {
|
||||
globalThis.fetch = createDetailFetchMock();
|
||||
render(<MissionManager isOpen={true} onClose={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Build Auth System")).toBeDefined();
|
||||
});
|
||||
fireEvent.click(screen.getByText("Build Auth System"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId("mission-tab-structure")).toBeDefined();
|
||||
});
|
||||
|
||||
const header = screen.getByTestId("mission-header-title");
|
||||
const desktopSpan = header.querySelector(".mission-manager__title-text--desktop");
|
||||
const mobileSpan = header.querySelector(".mission-manager__title-text--mobile");
|
||||
expect(desktopSpan?.textContent).toBe("Missions");
|
||||
expect(mobileSpan?.textContent).toBe("Build Auth System");
|
||||
});
|
||||
|
||||
it("sidebar header hides title and shows only action buttons", async () => {
|
||||
globalThis.fetch = createFetchMock();
|
||||
render(<MissionManager isOpen={true} onClose={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
await waitFor(() => {
|
||||
const sidebarTitle = document.querySelector(".mission-manager__sidebar-title");
|
||||
const sidebarActions = document.querySelector(".mission-manager__sidebar-actions");
|
||||
expect(sidebarTitle).toBeInTheDocument();
|
||||
expect(sidebarActions).toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: "Plan with AI" })).toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: "New Mission" })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1603,7 +1653,7 @@ describe("MissionManager", () => {
|
||||
);
|
||||
});
|
||||
|
||||
expect(screen.getByRole("heading", { name: "Missions" })).toBeInTheDocument();
|
||||
expect(screen.getByTestId("mission-header-title")).toBeInTheDocument();
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user