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);