From 815ec58abdde9c1bb41c0057223b018f5dfff378 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Tue, 9 Jun 2026 00:18:08 -0700 Subject: [PATCH] FN-6043: default GitHub imports to origin remote Prefer the origin remote when preselecting GitHub imports in the dashboard modal. - default the GitHub import modal to the origin remote when multiple remotes are available - keep the remote picker unselected when multiple remotes exist without an origin entry - expand dashboard tests to cover origin defaults for issues, pull requests, and mobile flows Files changed: .../dashboard/app/components/GitHubImportModal.tsx | 16 ++-- .../__tests__/GitHubImportModal.test.tsx | 95 ++++++++++++++++------ 2 files changed, 77 insertions(+), 34 deletions(-) Fusion-Task-Id: FN-6043 Fusion-Task-Lineage: b03b621d-757a-422c-b31c-ca0c34c3c168 --- .../app/components/GitHubImportModal.tsx | 16 +-- .../__tests__/GitHubImportModal.test.tsx | 97 +++++++++++++------ 2 files changed, 78 insertions(+), 35 deletions(-) diff --git a/packages/dashboard/app/components/GitHubImportModal.tsx b/packages/dashboard/app/components/GitHubImportModal.tsx index 9991dccfa5..11528586cc 100644 --- a/packages/dashboard/app/components/GitHubImportModal.tsx +++ b/packages/dashboard/app/components/GitHubImportModal.tsx @@ -143,14 +143,16 @@ export function GitHubImportModal({ isOpen, onClose, onImport, tasks, projectId setRemotes(fetchedRemotes); setLoadingRemotes(false); - if (fetchedRemotes.length === 1) { - // Single remote: auto-select it - const remote = fetchedRemotes[0]; - setOwner(remote.owner); - setRepo(remote.repo); - setSelectedRemoteName(remote.name); + const defaultRemote = fetchedRemotes.length === 1 + ? fetchedRemotes[0] + : fetchedRemotes.find((remote) => remote.name === "origin"); + + if (defaultRemote) { + setOwner(defaultRemote.owner); + setRepo(defaultRemote.repo); + setSelectedRemoteName(defaultRemote.name); } else if (fetchedRemotes.length > 1) { - // Multiple remotes: don't auto-select, user must choose + // Multiple remotes without origin: don't auto-select, user must choose. setOwner(""); setRepo(""); setSelectedRemoteName(""); diff --git a/packages/dashboard/app/components/__tests__/GitHubImportModal.test.tsx b/packages/dashboard/app/components/__tests__/GitHubImportModal.test.tsx index ae07707205..78c9dec90a 100644 --- a/packages/dashboard/app/components/__tests__/GitHubImportModal.test.tsx +++ b/packages/dashboard/app/components/__tests__/GitHubImportModal.test.tsx @@ -61,6 +61,11 @@ const multipleRemotes: GitRemote[] = [ { name: "upstream", owner: "upstream", repo: "kb", url: "https://github.com/upstream/kb.git" }, ]; +const multipleRemotesWithoutOrigin: GitRemote[] = [ + { name: "upstream", owner: "upstream", repo: "kb", url: "https://github.com/upstream/kb.git" }, + { name: "fork", owner: "dustinbyrne", repo: "kb", url: "https://github.com/dustinbyrne/kb.git" }, +]; + const mockPulls = [ { number: 1, title: "Test PR", body: "PR body", html_url: "https://github.com/owner/repo/pull/1", headBranch: "feature", baseBranch: "main" }, { number: 2, title: "Another PR", body: "Another PR body", html_url: "https://github.com/owner/repo/pull/2", headBranch: "bugfix", baseBranch: "main" }, @@ -289,19 +294,7 @@ describe("GitHubImportModal", () => { }); }); - it("disables Load button when no remote is selected", async () => { - vi.mocked(fetchGitRemotes).mockResolvedValueOnce(multipleRemotes); - render(); - - await waitFor(() => { - expect(screen.getByRole("combobox")).toBeTruthy(); - }); - - const loadButton = screen.getByRole("button", { name: /Load issues/i }) as HTMLButtonElement; - expect(loadButton.disabled).toBe(true); - }); - - it("enables Load button and auto-loads after selecting a remote", async () => { + it("defaults to origin and auto-loads when multiple remotes include origin", async () => { vi.mocked(fetchGitRemotes).mockResolvedValueOnce(multipleRemotes); vi.mocked(apiFetchGitHubIssues).mockResolvedValueOnce([ { number: 1, title: "Auto-loaded from origin", body: "", html_url: "https://github.com/dustinbyrne/kb/issues/1", labels: [] }, @@ -310,21 +303,30 @@ describe("GitHubImportModal", () => { render(); await waitFor(() => { - expect(screen.getByRole("combobox")).toBeTruthy(); - }); - - fireEvent.change(screen.getByRole("combobox"), { target: { value: "origin" } }); - - await waitFor(() => { + const select = screen.getByRole("combobox") as HTMLSelectElement; + expect(select.value).toBe("origin"); expect(apiFetchGitHubIssues).toHaveBeenCalledWith("dustinbyrne", "kb", 30, undefined); expect(screen.getByText("Auto-loaded from origin")).toBeTruthy(); }); - // After loading completes, button should be enabled const loadButton = screen.getByRole("button", { name: /Load issues/i }) as HTMLButtonElement; expect(loadButton.disabled).toBe(false); }); + it("keeps placeholder selected and does not auto-load when multiple remotes omit origin", async () => { + vi.mocked(fetchGitRemotes).mockResolvedValueOnce(multipleRemotesWithoutOrigin); + render(); + + await waitFor(() => { + const select = screen.getByRole("combobox") as HTMLSelectElement; + expect(select.value).toBe(""); + }); + + const loadButton = screen.getByRole("button", { name: /Load issues/i }) as HTMLButtonElement; + expect(loadButton.disabled).toBe(true); + expect(apiFetchGitHubIssues).not.toHaveBeenCalled(); + }); + it("switches owner/repo and auto-loads when changing remote selection", async () => { vi.mocked(fetchGitRemotes).mockResolvedValueOnce(multipleRemotes); vi.mocked(apiFetchGitHubIssues) @@ -334,18 +336,13 @@ describe("GitHubImportModal", () => { render(); await waitFor(() => { - expect(screen.getByRole("combobox")).toBeTruthy(); - }); - - const select = screen.getByRole("combobox"); - fireEvent.change(select, { target: { value: "origin" } }); - - await waitFor(() => { + const select = screen.getByRole("combobox") as HTMLSelectElement; + expect(select.value).toBe("origin"); expect(apiFetchGitHubIssues).toHaveBeenCalledWith("dustinbyrne", "kb", 30, undefined); expect(screen.getByText("Issue from origin")).toBeTruthy(); }); - fireEvent.change(select, { target: { value: "upstream" } }); + fireEvent.change(screen.getByRole("combobox"), { target: { value: "upstream" } }); await waitFor(() => { expect(apiFetchGitHubIssues).toHaveBeenLastCalledWith("upstream", "kb", 30, undefined); @@ -574,6 +571,31 @@ describe("GitHubImportModal", () => { window.dispatchEvent(new Event("resize")); }); + it("defaults to origin and auto-loads on mobile", async () => { + Object.defineProperty(window, "innerWidth", { + writable: true, + configurable: true, + value: 640, + }); + window.dispatchEvent(new Event("resize")); + + vi.mocked(fetchGitRemotes).mockResolvedValueOnce(multipleRemotes); + vi.mocked(apiFetchGitHubIssues).mockResolvedValueOnce([ + { number: 1, title: "Mobile origin issue", body: "", html_url: "https://github.com/dustinbyrne/kb/issues/1", labels: [] }, + ]); + + render(); + + await waitFor(() => { + const select = screen.getByRole("combobox") as HTMLSelectElement; + expect(select.value).toBe("origin"); + expect(apiFetchGitHubIssues).toHaveBeenCalledWith("dustinbyrne", "kb", 30, undefined); + expect(screen.getByText("Mobile origin issue")).toBeTruthy(); + }); + + expect(screen.getByTestId("github-import-list-pane").classList.contains("mobile")).toBe(true); + }); + it("shows back button in preview header when on mobile", async () => { // Set mobile viewport Object.defineProperty(window, "innerWidth", { @@ -884,6 +906,25 @@ describe("GitHubImportModal", () => { }); }); + it("uses the default origin remote when switching to Pull Requests", async () => { + vi.mocked(fetchGitRemotes).mockResolvedValueOnce(multipleRemotes); + vi.mocked(apiFetchGitHubPulls).mockResolvedValueOnce(mockPulls); + + render(); + + await waitFor(() => { + const select = screen.getByRole("combobox") as HTMLSelectElement; + expect(select.value).toBe("origin"); + }); + + fireEvent.click(screen.getByRole("tab", { name: /Pull Requests/i })); + + await waitFor(() => { + expect(apiFetchGitHubPulls).toHaveBeenCalledWith("dustinbyrne", "kb", 30); + expect(screen.getByText("Test PR")).toBeTruthy(); + }); + }); + it("displays PR list with branch info", async () => { vi.mocked(fetchGitRemotes).mockResolvedValueOnce(singleRemote); vi.mocked(apiFetchGitHubPulls).mockResolvedValueOnce(mockPulls);