From 862e776a96525dadea02f773c86d56b1cd60f413 Mon Sep 17 00:00:00 2001 From: Puneet Verma Date: Wed, 29 Jul 2026 15:50:01 +0530 Subject: [PATCH] fix(desktop): render projects header and create menu at zero projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ProjectsView returned before the page header and toolbar were built, and the create menu lives in that toolbar. A relay with no repo announcements therefore had no way to create its first project from the UI — the button that creates a project only appeared once a project already existed. Drop the early return and render the empty state in the content slot instead, the way EmptyFilteredState already does, so the header, filter toolbar, and + menu stay mounted at every project count. Adds an e2e spec that hides every seeded mock project to reach a zero-project relay, then drives the create menu through to the create project dialog. Both cases fail without the fix. Fixes #3504 Signed-off-by: Puneet Verma --- desktop/playwright.config.ts | 1 + .../src/features/projects/ui/ProjectsView.tsx | 11 +-- .../tests/e2e/projects-empty-state.spec.ts | 71 +++++++++++++++++++ 3 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 desktop/tests/e2e/projects-empty-state.spec.ts diff --git a/desktop/playwright.config.ts b/desktop/playwright.config.ts index c1ea0e061b..a6de9b227d 100644 --- a/desktop/playwright.config.ts +++ b/desktop/playwright.config.ts @@ -107,6 +107,7 @@ export default defineConfig({ "**/project-commit-detail.spec.ts", "**/project-inbox.spec.ts", "**/project-issue-comments.spec.ts", + "**/projects-empty-state.spec.ts", "**/project-pr-review.spec.ts", "**/persona-model-combobox-screenshots.spec.ts", "**/drafts-screenshots.spec.ts", diff --git a/desktop/src/features/projects/ui/ProjectsView.tsx b/desktop/src/features/projects/ui/ProjectsView.tsx index 78ba09934d..35029c89b5 100644 --- a/desktop/src/features/projects/ui/ProjectsView.tsx +++ b/desktop/src/features/projects/ui/ProjectsView.tsx @@ -592,9 +592,10 @@ export function ProjectsView() { ); } - if (projects.length === 0) { - return ; - } + // Zero projects still renders the full chrome — the create menu lives in the + // toolbar, so returning early here would leave no way to create the first + // project from the UI. + const hasProjects = projects.length > 0; const projectItems = visibleProjects.length === 0 ? ( @@ -850,7 +851,9 @@ export function ProjectsView() {
- {filter === "all" ? ( + {!hasProjects ? ( + + ) : filter === "all" ? ( { + window.localStorage.setItem( + featureOverridesStorageKey, + JSON.stringify({ projects: true }), + ); + window.localStorage.setItem( + "buzz.projects.hidden-cards.v1", + JSON.stringify(coordinates), + ); + }, + { + coordinates: ALL_MOCK_PROJECT_COORDINATES, + featureOverridesStorageKey: FEATURE_OVERRIDES_STORAGE_KEY, + }, + ); + await installMockBridge(page); + await page.goto("/", { waitUntil: "domcontentloaded" }); + await page.getByTestId("open-projects-view").click(); +} + +test("projects header and create menu stay mounted with zero projects", async ({ + page, +}) => { + await openEmptyProjectsView(page); + + await expect( + page.getByRole("heading", { level: 1, name: "Projects" }), + ).toBeVisible(); + await expect(page.getByText("No projects yet")).toBeVisible(); + await expect( + page.getByRole("button", { name: "Repositories", exact: true }), + ).toBeVisible(); + + // The regression: the create menu was unreachable until a project already + // existed, so the first project could never be created from the UI. + await page.getByTestId("projects-create-menu").hover(); + await page.getByRole("menuitem", { name: "Project" }).click(); + await expect(page.getByTestId("create-project-dialog")).toBeVisible(); + await expect(page.getByTestId("create-project-name")).toBeVisible(); +}); + +test("empty state replaces only the content area on every filter", async ({ + page, +}) => { + await openEmptyProjectsView(page); + + for (const filter of ["Repositories", "Pull Requests", "Issues"]) { + await page.getByRole("button", { name: filter, exact: true }).click(); + await expect(page.getByText("No projects yet")).toBeVisible(); + await expect(page.getByTestId("projects-create-menu")).toBeVisible(); + } +});