feat(app): polish workspace tabs and changes
Size tabs from rendered labels and switch to horizontal overflow once minimum click targets are reached, keeping the add-tab action adjacent until it must remain fixed. Make Changes an always-expanded document with an optional desktop navigation tree, keep Git actions in their platform-owned headers, remove pinned and inline split shortcuts, and place review tabs beside Changes.
This commit is contained in:
@@ -304,7 +304,7 @@ test("changes context menus duplicate files and folders", async ({ page }) => {
|
||||
.poll(() => readFileIfPresent(path.join(workspace.repoPath, "src/use-mounted-tab-set copy.ts")))
|
||||
.toBe(AFTER);
|
||||
|
||||
await page.getByTestId("changes-toggle-view-mode").click();
|
||||
await page.getByTestId("changes-toggle-tree").click();
|
||||
await changesTree(page).getByTestId("diff-folder-src-toggle").click({ button: "right" });
|
||||
await page.getByTestId("diff-folder-src-duplicate").click();
|
||||
await expect
|
||||
@@ -317,7 +317,7 @@ test("changes context menu recursively collapses descendant folders", async ({ p
|
||||
await useUnwrappedDiffLines(page);
|
||||
await openWorkspaceChanges(page, workspace);
|
||||
|
||||
await page.getByTestId("changes-toggle-view-mode").click();
|
||||
await page.getByTestId("changes-toggle-tree").click();
|
||||
const tree = changesTree(page);
|
||||
await expect(tree.getByTestId("diff-folder-src/zz-folder")).toBeVisible();
|
||||
await expect(tree.getByTestId("diff-folder-src/zz-folder/nested")).toBeVisible();
|
||||
@@ -346,7 +346,7 @@ test("changes context menus expose folder revert and restore a file after confir
|
||||
await useUnwrappedDiffLines(page);
|
||||
await openWorkspaceChanges(page, workspace);
|
||||
|
||||
await page.getByTestId("changes-toggle-view-mode").click();
|
||||
await page.getByTestId("changes-toggle-tree").click();
|
||||
const tree = changesTree(page);
|
||||
await tree.getByTestId("diff-folder-src-toggle").click({ button: "right" });
|
||||
const folderRevert = page.getByTestId("diff-folder-src-revert");
|
||||
@@ -357,7 +357,7 @@ test("changes context menus expose folder revert and restore a file after confir
|
||||
await expect(folderRevert.locator("svg")).toHaveCSS("stroke", revertLabelColor);
|
||||
await page.keyboard.press("Escape");
|
||||
|
||||
await tree.getByTestId("diff-file-0-toggle").click({ button: "right" });
|
||||
await tree.getByTestId("diff-tree-file-0-toggle").click({ button: "right" });
|
||||
const cancelledConfirmation = new Promise<string>((resolve) => {
|
||||
page.once("dialog", async (dialog) => {
|
||||
const message = dialog.message();
|
||||
@@ -365,14 +365,14 @@ test("changes context menus expose folder revert and restore a file after confir
|
||||
resolve(message);
|
||||
});
|
||||
});
|
||||
await page.getByTestId("diff-file-0-revert").click();
|
||||
await page.getByTestId("diff-tree-file-0-revert").click();
|
||||
expect(await cancelledConfirmation).toContain("src/use-mounted-tab-set.ts");
|
||||
await expect(tree.getByTestId("diff-file-0")).toBeVisible();
|
||||
await expect(tree.getByTestId("diff-tree-file-0")).toBeVisible();
|
||||
await expect
|
||||
.poll(() => readFile(path.join(workspace.repoPath, "src/use-mounted-tab-set.ts"), "utf8"))
|
||||
.toBe(AFTER);
|
||||
|
||||
await tree.getByTestId("diff-file-0-toggle").click({ button: "right" });
|
||||
await tree.getByTestId("diff-tree-file-0-toggle").click({ button: "right" });
|
||||
const confirmation = new Promise<string>((resolve) => {
|
||||
page.once("dialog", async (dialog) => {
|
||||
const message = dialog.message();
|
||||
@@ -380,10 +380,10 @@ test("changes context menus expose folder revert and restore a file after confir
|
||||
resolve(message);
|
||||
});
|
||||
});
|
||||
await page.getByTestId("diff-file-0-revert").click();
|
||||
await page.getByTestId("diff-tree-file-0-revert").click();
|
||||
expect(await confirmation).toContain("src/use-mounted-tab-set.ts");
|
||||
|
||||
await expect(tree.getByTestId("diff-file-0")).toHaveCount(0, { timeout: 30_000 });
|
||||
await expect(tree.getByTestId("diff-tree-file-0")).toHaveCount(0, { timeout: 30_000 });
|
||||
await expect
|
||||
.poll(() => readFile(path.join(workspace.repoPath, "src/use-mounted-tab-set.ts"), "utf8"))
|
||||
.toBe(BEFORE);
|
||||
@@ -482,10 +482,11 @@ test("Changes keeps review navigation and controls inside its workspace tab", as
|
||||
await expect(visiblePanel.getByText("use-mounted-tab-set.ts", { exact: true })).toBeVisible();
|
||||
await expect(visiblePanel).toContainText("zz-deleted.ts");
|
||||
await expect(visiblePanel.getByTestId("changes-open-tab")).toHaveCount(0);
|
||||
await expect(visiblePanel.getByTestId("changes-primary-cta")).toHaveCount(0);
|
||||
await expect(page.getByTestId("changes-primary-cta")).toHaveCount(1);
|
||||
await expect(page.getByTestId("changes-primary-cta")).toContainText("Commit");
|
||||
await expect(visiblePanel.getByTestId("diff-file-0-body")).toBeVisible();
|
||||
await visiblePanel.getByTestId("diff-file-0-toggle").click();
|
||||
await expect(visiblePanel.getByTestId("diff-file-0-body")).toHaveCount(0);
|
||||
await visiblePanel.getByTestId("diff-file-0-toggle").click();
|
||||
await expect(visiblePanel.getByTestId("diff-file-0-body")).toBeVisible();
|
||||
const workingDiffLayoutToggle = visiblePanel.getByRole("button", {
|
||||
name: "Switch to side-by-side diff",
|
||||
@@ -501,10 +502,7 @@ test("Changes keeps review navigation and controls inside its workspace tab", as
|
||||
await visiblePanel.getByRole("button", { name: "Diff options" }).click();
|
||||
await expect(page.getByText("Scroll long lines", { exact: true })).toBeVisible();
|
||||
await page.keyboard.press("Escape");
|
||||
await page.getByRole("button", { name: "Expand all files" }).click();
|
||||
await expect(visiblePanel.getByTestId("diff-file-0-body")).toBeVisible();
|
||||
await page.getByRole("button", { name: "Collapse all files" }).click();
|
||||
await expect(visiblePanel.getByTestId(/^diff-file-\d+-body$/)).toHaveCount(0);
|
||||
await expect(page.getByRole("button", { name: /all files/i })).toHaveCount(0);
|
||||
await expect(page.getByTestId(/^workspace-working-diff-close-/)).toHaveCount(1);
|
||||
|
||||
await writeFile(path.join(workspace.repoPath, "src/use-mounted-tab-set.ts"), BEFORE);
|
||||
@@ -520,7 +518,9 @@ test("Changes keeps review navigation and controls inside its workspace tab", as
|
||||
await expect(visiblePanel.getByRole("img", { name: "Deleted" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("changes diff switches between flat and tree file lists", async ({ page }) => {
|
||||
test("desktop Changes toggles a navigation tree beside the expanded diff document", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = await createWorkspaceWithMountedTabDiff();
|
||||
await useUnwrappedDiffLines(page);
|
||||
await openWorkspaceChanges(page, workspace);
|
||||
@@ -541,7 +541,7 @@ test("changes diff switches between flat and tree file lists", async ({ page })
|
||||
await page.keyboard.press("Escape");
|
||||
|
||||
await scrollToLowerUnwrappedDiffRows(page);
|
||||
await page.getByTestId("changes-toggle-view-mode").click();
|
||||
await page.getByTestId("changes-toggle-tree").click();
|
||||
const tree = changesTree(page);
|
||||
const content = changesContent(page);
|
||||
await expect(tree.getByTestId("diff-folder-src")).toBeVisible();
|
||||
@@ -549,9 +549,9 @@ test("changes diff switches between flat and tree file lists", async ({ page })
|
||||
"user-select",
|
||||
"none",
|
||||
);
|
||||
await expect(tree.getByTestId("diff-file-0")).toBeVisible();
|
||||
await expect(tree.getByTestId("diff-tree-file-0")).toBeVisible();
|
||||
await expect(tree.getByTestId("diff-folder-src-toggle").locator("svg")).toHaveCount(1);
|
||||
await expect(tree.getByTestId("diff-file-0-toggle").locator("svg")).toHaveCount(1);
|
||||
await expect(tree.getByTestId("diff-tree-file-0-toggle").locator("svg")).toHaveCount(1);
|
||||
const folderToggleBounds = await tree.getByTestId("diff-folder-src-toggle").boundingBox();
|
||||
const folderChevronBounds = await tree
|
||||
.getByTestId("diff-folder-src-toggle")
|
||||
@@ -568,7 +568,7 @@ test("changes diff switches between flat and tree file lists", async ({ page })
|
||||
.getByText("src", { exact: true })
|
||||
.boundingBox();
|
||||
const fileLabelBounds = await tree
|
||||
.getByTestId("diff-file-0")
|
||||
.getByTestId("diff-tree-file-0")
|
||||
.getByText("use-mounted-tab-set.ts", { exact: true })
|
||||
.boundingBox();
|
||||
expect(folderLabelBounds).not.toBeNull();
|
||||
@@ -578,27 +578,31 @@ test("changes diff switches between flat and tree file lists", async ({ page })
|
||||
const folderToggle = tree.getByTestId("diff-folder-src-toggle");
|
||||
await folderToggle.click();
|
||||
await expect(folderToggle).toHaveAttribute("aria-selected", "true");
|
||||
await expect(tree.getByTestId("diff-file-0")).toHaveCount(0);
|
||||
await expect(tree.getByTestId("diff-tree-file-0")).toHaveCount(0);
|
||||
await folderToggle.click();
|
||||
await expect(folderToggle).toHaveAttribute("aria-selected", "true");
|
||||
await expect(tree.getByTestId("diff-file-0")).toBeVisible();
|
||||
await expect(tree.getByTestId("diff-tree-file-0")).toBeVisible();
|
||||
|
||||
const fileToggle = tree.getByTestId("diff-file-0-toggle");
|
||||
const fileToggle = tree.getByTestId("diff-tree-file-0-toggle");
|
||||
await fileToggle.click({ button: "right" });
|
||||
await expect(fileToggle).toHaveAttribute("aria-selected", "true");
|
||||
await expect(folderToggle).toHaveAttribute("aria-selected", "false");
|
||||
await expect(page.getByTestId("diff-file-0-context-menu")).toBeVisible();
|
||||
await expect(page.getByTestId("diff-tree-file-0-context-menu")).toBeVisible();
|
||||
await page.keyboard.press("Escape");
|
||||
|
||||
await page.getByRole("button", { name: "Collapse all files" }).click();
|
||||
await expect(content.getByTestId(/^diff-file-\d+-body$/)).toHaveCount(0);
|
||||
await page.getByRole("button", { name: "Expand all files" }).click();
|
||||
await fileToggle.click();
|
||||
await expect(content.getByTestId("diff-file-0-body")).toBeVisible();
|
||||
const contentBounds = await content.boundingBox();
|
||||
const focusedFileBounds = await content.getByTestId("diff-file-0").boundingBox();
|
||||
expect(contentBounds).not.toBeNull();
|
||||
expect(focusedFileBounds).not.toBeNull();
|
||||
expect(focusedFileBounds!.y).toBeGreaterThanOrEqual(contentBounds!.y);
|
||||
expect(focusedFileBounds!.y).toBeLessThan(contentBounds!.y + 80);
|
||||
|
||||
await tree.getByTestId("diff-folder-src-toggle").click();
|
||||
await expect(tree.getByTestId("diff-file-0")).toHaveCount(0);
|
||||
await expect(tree.getByTestId("diff-tree-file-0")).toHaveCount(0);
|
||||
|
||||
await page.getByTestId("changes-toggle-view-mode").click();
|
||||
await page.getByTestId("changes-toggle-tree").click();
|
||||
await expectFlatFileList(page);
|
||||
});
|
||||
|
||||
@@ -649,7 +653,7 @@ async function useUnwrappedDiffLines(page: Page): Promise<void> {
|
||||
preferencesKey,
|
||||
JSON.stringify({
|
||||
layout: "unified",
|
||||
viewMode: "flat",
|
||||
desktopTreeVisible: false,
|
||||
wrapLines: false,
|
||||
hideWhitespace: false,
|
||||
}),
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
import { test, expect } from "../support/fixtures";
|
||||
import { gotoWorkspace } from "../support/helpers/launcher";
|
||||
import { seedWorkspace, type SeededWorkspace } from "../support/helpers/seed-client";
|
||||
import { togglePinFromMenu, tabRowPin } from "../support/helpers/pins";
|
||||
import { expectTerminalTabOpen } from "../support/helpers/workspace-tabs";
|
||||
import type { PinnedTabTarget } from "../../src/workspace-pins/target";
|
||||
|
||||
const DRAFT_TARGET: PinnedTabTarget = { kind: "draft" };
|
||||
const TERMINAL_TARGET: PinnedTabTarget = { kind: "terminal" };
|
||||
|
||||
let workspace: SeededWorkspace;
|
||||
|
||||
test.beforeAll(async () => {
|
||||
workspace = await seedWorkspace({ repoPrefix: "workspace-pins-e2e-" });
|
||||
});
|
||||
|
||||
test.afterAll(async () => {
|
||||
await workspace?.cleanup();
|
||||
});
|
||||
|
||||
test.describe("Pinned tab targets", () => {
|
||||
test("pinning a target from the dropdown adds its quick-launch button to the tab row, unpinning removes it", async ({
|
||||
page,
|
||||
}) => {
|
||||
await gotoWorkspace(page, workspace.workspaceId);
|
||||
|
||||
await expect(tabRowPin(page, DRAFT_TARGET)).toHaveCount(0);
|
||||
|
||||
await togglePinFromMenu(page, DRAFT_TARGET);
|
||||
await expect(tabRowPin(page, DRAFT_TARGET)).toBeVisible({ timeout: 10_000 });
|
||||
|
||||
await togglePinFromMenu(page, DRAFT_TARGET);
|
||||
await expect(tabRowPin(page, DRAFT_TARGET)).toHaveCount(0, { timeout: 10_000 });
|
||||
});
|
||||
|
||||
test("clicking the pinned quick-launch button in the tab row opens a terminal tab", async ({
|
||||
page,
|
||||
}) => {
|
||||
test.setTimeout(45_000);
|
||||
await gotoWorkspace(page, workspace.workspaceId);
|
||||
|
||||
await expect(tabRowPin(page, TERMINAL_TARGET)).toBeVisible({ timeout: 10_000 });
|
||||
await tabRowPin(page, TERMINAL_TARGET).click();
|
||||
|
||||
await expectTerminalTabOpen(page);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user