fix: truncate long task titles in topbar (#1630)

This commit is contained in:
Carlos Florêncio
2026-07-09 08:14:25 +01:00
committed by GitHub
parent 2b8504a913
commit 9da896bc86
5 changed files with 184 additions and 6 deletions
@@ -113,7 +113,7 @@ function MobileTopBarActions({
onMenuClick,
}: MobileTopBarActionsProps) {
return (
<div className="flex items-center gap-1">
<div className="flex items-center gap-1" data-testid="mobile-topbar-actions">
<MobileRepoPill taskId={taskId ?? null} workspaceId={workspaceId ?? null} />
{isRemoteExecutor && (
<RemoteCloudTooltip
+5 -5
View File
@@ -158,13 +158,13 @@ function TopBarLeft({
}: TopBarLeftProps) {
const showExecutorSettings = shouldShowExecutorEnvironmentControls(remoteExecutorType);
return (
<div className="flex items-center gap-2.5 min-w-0 overflow-hidden">
<Breadcrumb className="min-w-0">
<BreadcrumbList className="flex-nowrap text-sm min-w-0">
<BreadcrumbItem className="min-w-0">
<div className="flex min-w-0 max-w-[min(44rem,45vw)] items-center gap-2.5 overflow-hidden">
<Breadcrumb className="min-w-0 max-w-full">
<BreadcrumbList className="min-w-0 max-w-full flex-nowrap text-sm">
<BreadcrumbItem className="min-w-0 max-w-full">
<Tooltip>
<TooltipTrigger asChild>
<BreadcrumbPage className="font-medium truncate">
<BreadcrumbPage className="block max-w-full truncate font-medium">
{taskTitle ?? "Task details"}
</BreadcrumbPage>
</TooltipTrigger>
+2
View File
@@ -0,0 +1,2 @@
export const LONG_TASK_TITLE =
'Issue #1625: [Bug]: Config-session MCP advertises list_executors but backend returns "Unknown action: mcp.list_executors"';
@@ -0,0 +1,85 @@
import { type Page } from "@playwright/test";
import { test, expect } from "../../fixtures/test-base";
import { LONG_TASK_TITLE } from "../../helpers/topbar-fixtures";
import { SessionPage } from "../../pages/session-page";
type DesktopTopbarMetrics = {
documentClientWidth: number;
documentScrollWidth: number;
stepperLeft: number;
titleClientWidth: number;
titleRight: number;
titleScrollWidth: number;
titleWidth: number;
toolsRight: number;
topbarRight: number;
topbarWidth: number;
};
async function readDesktopTopbarMetrics(page: Page): Promise<DesktopTopbarMetrics | null> {
return page.evaluate(() => {
const topbar = document.querySelector('[data-testid="task-topbar"]') as HTMLElement | null;
const title = topbar?.querySelector('[aria-current="page"]') as HTMLElement | null;
const stepper = topbar?.querySelector('[data-testid="workflow-stepper"]') as HTMLElement | null;
const tools = topbar?.querySelector('[aria-label="Task tools"]') as HTMLElement | null;
if (!topbar || !title || !stepper || !tools) return null;
const topbarRect = topbar.getBoundingClientRect();
const titleRect = title.getBoundingClientRect();
const stepperRect = stepper.getBoundingClientRect();
const toolsRect = tools.getBoundingClientRect();
return {
documentClientWidth: document.documentElement.clientWidth,
documentScrollWidth: document.documentElement.scrollWidth,
stepperLeft: stepperRect.left,
titleClientWidth: title.clientWidth,
titleRight: titleRect.right,
titleScrollWidth: title.scrollWidth,
titleWidth: titleRect.width,
toolsRight: toolsRect.right,
topbarRight: topbarRect.right,
topbarWidth: topbarRect.width,
};
});
}
test.describe("Task topbar long title layout", () => {
test("truncates long task titles without overlapping workflow or tool controls", async ({
testPage,
apiClient,
seedData,
}) => {
test.setTimeout(90_000);
const task = await apiClient.createTaskWithAgent(
seedData.workspaceId,
LONG_TASK_TITLE,
seedData.agentProfileId,
{
description: "/e2e:simple-message",
workflow_id: seedData.workflowId,
workflow_step_id: seedData.startStepId,
repository_ids: [seedData.repositoryId],
},
);
await testPage.goto(`/t/${task.id}`);
const session = new SessionPage(testPage);
await session.waitForLoad();
const title = testPage.locator('[data-testid="task-topbar"] [aria-current="page"]');
await expect(title).toHaveText(LONG_TASK_TITLE, { timeout: 10_000 });
await expect(testPage.getByTestId("layout-preset-trigger")).toBeVisible();
const metrics = await readDesktopTopbarMetrics(testPage);
expect(metrics).not.toBeNull();
if (!metrics) return;
expect(metrics.documentScrollWidth).toBeLessThanOrEqual(metrics.documentClientWidth + 1);
expect(metrics.titleScrollWidth).toBeGreaterThan(metrics.titleClientWidth + 8);
expect(metrics.titleWidth).toBeLessThanOrEqual(metrics.topbarWidth * 0.6);
expect(metrics.titleRight).toBeLessThanOrEqual(metrics.stepperLeft + 1);
expect(metrics.toolsRight).toBeLessThanOrEqual(metrics.topbarRight + 1);
});
});
@@ -0,0 +1,91 @@
import { type Page } from "@playwright/test";
import { test, expect } from "../../fixtures/test-base";
import { LONG_TASK_TITLE } from "../../helpers/topbar-fixtures";
import { SessionPage } from "../../pages/session-page";
type MobileTopbarMetrics = {
actionsLeft: number;
actionsRight: number;
documentClientWidth: number;
documentScrollWidth: number;
headerLeft: number;
headerRight: number;
titleClientWidth: number;
titleRight: number;
titleScrollWidth: number;
};
async function readMobileTopbarMetrics(
page: Page,
taskTitle: string,
): Promise<MobileTopbarMetrics | null> {
return page.evaluate((titleText) => {
const header = Array.from(document.querySelectorAll("header")).find((node) =>
node.textContent?.includes(titleText),
) as HTMLElement | undefined;
const title = Array.from(header?.querySelectorAll("span") ?? []).find(
(node) => node.textContent?.trim() === titleText,
) as HTMLElement | undefined;
const actions = header?.querySelector('[data-testid="mobile-topbar-actions"]') as
| HTMLElement
| null
| undefined;
if (!header || !title || !actions) return null;
const headerRect = header.getBoundingClientRect();
const titleRect = title.getBoundingClientRect();
const actionsRect = actions.getBoundingClientRect();
return {
actionsLeft: actionsRect.left,
actionsRight: actionsRect.right,
documentClientWidth: document.documentElement.clientWidth,
documentScrollWidth: document.documentElement.scrollWidth,
headerLeft: headerRect.left,
headerRight: headerRect.right,
titleClientWidth: title.clientWidth,
titleRight: titleRect.right,
titleScrollWidth: title.scrollWidth,
};
}, taskTitle);
}
test.describe("Mobile task topbar long title layout", () => {
test("truncates long task titles without pushing mobile actions off-screen", async ({
testPage,
apiClient,
seedData,
}) => {
test.setTimeout(90_000);
const task = await apiClient.createTaskWithAgent(
seedData.workspaceId,
LONG_TASK_TITLE,
seedData.agentProfileId,
{
description: "/e2e:simple-message",
workflow_id: seedData.workflowId,
workflow_step_id: seedData.startStepId,
repository_ids: [seedData.repositoryId],
},
);
await testPage.goto(`/t/${task.id}`);
const session = new SessionPage(testPage);
await session.waitForLoad();
const header = testPage.locator("header").filter({ hasText: LONG_TASK_TITLE }).first();
await expect(header).toBeVisible({ timeout: 10_000 });
await expect(testPage.getByTestId("mobile-session-menu")).toBeVisible();
const metrics = await readMobileTopbarMetrics(testPage, LONG_TASK_TITLE);
expect(metrics).not.toBeNull();
if (!metrics) return;
expect(metrics.documentScrollWidth).toBeLessThanOrEqual(metrics.documentClientWidth + 1);
expect(metrics.titleScrollWidth).toBeGreaterThan(metrics.titleClientWidth + 8);
expect(metrics.titleRight).toBeLessThanOrEqual(metrics.actionsLeft + 1);
expect(metrics.headerLeft).toBeGreaterThanOrEqual(0);
expect(metrics.actionsRight).toBeLessThanOrEqual(metrics.headerRight + 1);
});
});