mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-14 13:22:37 +00:00
fix(app): traverse workspaces beyond nine (#6586)
Relative navigation shared the list capped for digit shortcuts, so it wrapped at the ninth workspace. Keep all sidebar targets and cap only numbered badges. Carry forward #4128 with keyboard-routing and browser regressions. Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com> Co-authored-by: Raisal P Wardana <IEatCodeDaily@users.noreply.github.com>
This commit is contained in:
co-authored by
Raisal P Wardana
parent
938c4e2632
commit
cafa04d3fb
@@ -0,0 +1,74 @@
|
|||||||
|
import type { Page } from "@playwright/test";
|
||||||
|
import { test, expect } from "../support/fixtures";
|
||||||
|
import { gotoAppShell } from "../support/helpers/app";
|
||||||
|
import { seedWorkspace } from "../support/helpers/seed-client";
|
||||||
|
import { getServerId } from "../support/helpers/server-id";
|
||||||
|
import {
|
||||||
|
expectSidebarWorkspaceSelected,
|
||||||
|
switchWorkspaceViaSidebar,
|
||||||
|
} from "../support/helpers/workspace-ui";
|
||||||
|
|
||||||
|
async function readVisibleWorkspaceOrder(page: Page): Promise<string[]> {
|
||||||
|
const rows = page.getByRole("button", { name: /^Workspace \d{2}$/ });
|
||||||
|
await expect(rows).toHaveCount(12);
|
||||||
|
return rows.evaluateAll(
|
||||||
|
(elements, prefix) =>
|
||||||
|
elements.map((element) => element.getAttribute("data-testid")!.slice(prefix.length)),
|
||||||
|
`sidebar-workspace-row-${getServerId()}:`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function navigateWorkspace(
|
||||||
|
page: Page,
|
||||||
|
direction: "next" | "previous",
|
||||||
|
workspaceId: string,
|
||||||
|
): Promise<void> {
|
||||||
|
await page.keyboard.press(direction === "next" ? "Alt+]" : "Alt+[");
|
||||||
|
await expectSidebarWorkspaceSelected({ page, serverId: getServerId(), workspaceId });
|
||||||
|
await expect(page).toHaveURL(new RegExp(`/workspace/${workspaceId}$`));
|
||||||
|
}
|
||||||
|
|
||||||
|
test("previous and next traverse every visible workspace while digit shortcuts stop at nine", async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
test.setTimeout(120_000);
|
||||||
|
const project = await seedWorkspace({
|
||||||
|
repoPrefix: "relative-navigation-",
|
||||||
|
title: "Workspace 01",
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
for (let index = 2; index <= 12; index += 1) {
|
||||||
|
const created = await project.client.createWorkspace({
|
||||||
|
source: { kind: "directory", path: project.repoPath },
|
||||||
|
title: `Workspace ${String(index).padStart(2, "0")}`,
|
||||||
|
});
|
||||||
|
expect(created.error).toBeNull();
|
||||||
|
}
|
||||||
|
await page.setViewportSize({ width: 1440, height: 1100 });
|
||||||
|
await gotoAppShell(page);
|
||||||
|
const order = await readVisibleWorkspaceOrder(page);
|
||||||
|
|
||||||
|
await test.step("next crosses the ninth workspace and previous returns to it", async () => {
|
||||||
|
await switchWorkspaceViaSidebar({ page, serverId: getServerId(), workspaceId: order[8] });
|
||||||
|
await navigateWorkspace(page, "next", order[9]);
|
||||||
|
await navigateWorkspace(page, "previous", order[8]);
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step("wrapping uses the true first and last visible workspace", async () => {
|
||||||
|
await switchWorkspaceViaSidebar({ page, serverId: getServerId(), workspaceId: order[0] });
|
||||||
|
await navigateWorkspace(page, "previous", order[11]);
|
||||||
|
await navigateWorkspace(page, "next", order[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step("the ninth digit shortcut still selects the ninth workspace", async () => {
|
||||||
|
await page.keyboard.press("Alt+9");
|
||||||
|
await expectSidebarWorkspaceSelected({
|
||||||
|
page,
|
||||||
|
serverId: getServerId(),
|
||||||
|
workspaceId: order[8],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
await project.cleanup();
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -5,6 +5,7 @@ import type {
|
|||||||
SidebarWorkspaceEntry,
|
SidebarWorkspaceEntry,
|
||||||
} from "@/hooks/use-sidebar-workspaces-list";
|
} from "@/hooks/use-sidebar-workspaces-list";
|
||||||
import { buildStatusGroups } from "@/hooks/sidebar-status-view-model";
|
import { buildStatusGroups } from "@/hooks/sidebar-status-view-model";
|
||||||
|
import { routeKeyboardShortcut } from "@/keyboard/route-shortcut";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
buildSidebarShortcutModel,
|
buildSidebarShortcutModel,
|
||||||
@@ -65,6 +66,46 @@ function project(projectKey: string, workspaces: SidebarWorkspaceEntry[]): Sideb
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe("buildSidebarShortcutModel", () => {
|
describe("buildSidebarShortcutModel", () => {
|
||||||
|
it.each([
|
||||||
|
[9, 1, 10],
|
||||||
|
[10, -1, 9],
|
||||||
|
[12, 1, 1],
|
||||||
|
[1, -1, 12],
|
||||||
|
] as const)(
|
||||||
|
"navigates from workspace %i by %i to workspace %i across the full visible sidebar",
|
||||||
|
(current, delta, next) => {
|
||||||
|
const workspaces = Array.from({ length: 12 }, (_, index) =>
|
||||||
|
workspace({
|
||||||
|
serverId: "s",
|
||||||
|
workspaceId: `ws-${index + 1}`,
|
||||||
|
workspaceDirectory: `/repo/w${index + 1}`,
|
||||||
|
name: `w${index + 1}`,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const model = buildSidebarShortcutModel({
|
||||||
|
projects: [project("p", workspaces)],
|
||||||
|
collapsedProjectKeys: new Set<string>(),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
routeKeyboardShortcut(
|
||||||
|
{ action: "workspace.navigate.relative", payload: { delta } },
|
||||||
|
{
|
||||||
|
pathname: `/h/s/workspace/ws-${current}`,
|
||||||
|
isMobile: false,
|
||||||
|
sidebarShortcutTargets: model.shortcutTargets,
|
||||||
|
navigationActiveWorkspace: null,
|
||||||
|
commandCenterOpen: false,
|
||||||
|
shortcutsDialogOpen: false,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
).toEqual({ kind: "navigate-workspace", serverId: "s", workspaceId: `ws-${next}` });
|
||||||
|
expect(model.shortcutIndexByWorkspaceKey.size).toBe(9);
|
||||||
|
expect(model.shortcutIndexByWorkspaceKey.get("s:ws-9")).toBe(9);
|
||||||
|
expect(model.shortcutIndexByWorkspaceKey.has("s:ws-10")).toBe(false);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
it("builds shortcut targets in visual order and excludes collapsed projects", () => {
|
it("builds shortcut targets in visual order and excludes collapsed projects", () => {
|
||||||
const projects = [
|
const projects = [
|
||||||
project("p1", [
|
project("p1", [
|
||||||
@@ -111,7 +152,7 @@ describe("buildSidebarShortcutModel", () => {
|
|||||||
expect(model.shortcutIndexByWorkspaceKey.get("s1:ws-repo2-main")).toBeUndefined();
|
expect(model.shortcutIndexByWorkspaceKey.get("s1:ws-repo2-main")).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("limits shortcuts to 9", () => {
|
it("keeps all traversal targets while numbering only the first 9", () => {
|
||||||
const workspaces = Array.from({ length: 20 }, (_, index) =>
|
const workspaces = Array.from({ length: 20 }, (_, index) =>
|
||||||
workspace({
|
workspace({
|
||||||
serverId: "s",
|
serverId: "s",
|
||||||
@@ -127,9 +168,11 @@ describe("buildSidebarShortcutModel", () => {
|
|||||||
collapsedProjectKeys: new Set<string>(),
|
collapsedProjectKeys: new Set<string>(),
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(model.shortcutTargets).toHaveLength(9);
|
expect(model.shortcutTargets).toHaveLength(20);
|
||||||
expect(model.shortcutTargets[0]).toEqual({ serverId: "s", workspaceId: "ws-1" });
|
|
||||||
expect(model.shortcutTargets[8]).toEqual({ serverId: "s", workspaceId: "ws-9" });
|
expect(model.shortcutTargets[8]).toEqual({ serverId: "s", workspaceId: "ws-9" });
|
||||||
|
expect(model.shortcutTargets[9]).toEqual({ serverId: "s", workspaceId: "ws-10" });
|
||||||
|
expect(model.shortcutIndexByWorkspaceKey.get("s:ws-9")).toBe(9);
|
||||||
|
expect(model.shortcutIndexByWorkspaceKey.get("s:ws-10")).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("excludes a collapsed project's workspaces regardless of project kind", () => {
|
it("excludes a collapsed project's workspaces regardless of project kind", () => {
|
||||||
|
|||||||
@@ -70,13 +70,10 @@ export function buildSidebarShortcutSections(input: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const workspace of section.workspaces) {
|
for (const workspace of section.workspaces) {
|
||||||
if (shortcutTargets.length >= maxShortcuts) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const shortcutNumber = shortcutTargets.length + 1;
|
|
||||||
shortcutTargets.push(createShortcutTarget(workspace));
|
shortcutTargets.push(createShortcutTarget(workspace));
|
||||||
shortcutIndexByWorkspaceKey.set(workspace.workspaceKey, shortcutNumber);
|
if (shortcutTargets.length <= maxShortcuts) {
|
||||||
|
shortcutIndexByWorkspaceKey.set(workspace.workspaceKey, shortcutTargets.length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user