From e9089ee8a18bc4b1c693af39244743d5eebb3ec3 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Sat, 15 Aug 2026 17:53:29 -0700 Subject: [PATCH] FN-9110: contain GitHub pull request imports on mobile Keep GitHub pull-request rows and previews readable within narrow import layouts. - Let mobile pull-request rows grow with wrapped titles, badges, and branch metadata. - Break long branch names within list rows and detail previews. - Cover modal and embedded presentations with layout and interaction regressions. - Add a patch changeset for the mobile layout fix. Files changed: .changeset/fn-9110-github-import-pulls-mobile.md | 7 +++ .../github-import-pulls-mobile-layout.test.ts | 54 ++++++++++++++++++++++ .../dashboard/app/components/GitHubImportModal.css | 27 ++++++++++- .../__tests__/GitHubImportModal.test.tsx | 32 +++++++++++++ 4 files changed, 119 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-9110 Fusion-Task-Lineage: 572b5fb3-076e-448a-9f6f-185b836b4161 Co-authored-by: Fusion (runfusion.ai) --- .../fn-9110-github-import-pulls-mobile.md | 7 +++ .../github-import-pulls-mobile-layout.test.ts | 54 +++++++++++++++++++ .../app/components/GitHubImportModal.css | 27 +++++++++- .../__tests__/GitHubImportModal.test.tsx | 32 +++++++++++ 4 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 .changeset/fn-9110-github-import-pulls-mobile.md create mode 100644 packages/dashboard/app/__tests__/github-import-pulls-mobile-layout.test.ts diff --git a/.changeset/fn-9110-github-import-pulls-mobile.md b/.changeset/fn-9110-github-import-pulls-mobile.md new file mode 100644 index 0000000000..b2af721d28 --- /dev/null +++ b/.changeset/fn-9110-github-import-pulls-mobile.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Keep GitHub pull-request imports readable on mobile screens. +category: fix +dev: Pull rows and preview branch names now wrap safely in mobile import layouts. diff --git a/packages/dashboard/app/__tests__/github-import-pulls-mobile-layout.test.ts b/packages/dashboard/app/__tests__/github-import-pulls-mobile-layout.test.ts new file mode 100644 index 0000000000..de96721aed --- /dev/null +++ b/packages/dashboard/app/__tests__/github-import-pulls-mobile-layout.test.ts @@ -0,0 +1,54 @@ +import { describe, expect, it } from "vitest"; +import { readFileSync } from "fs"; +import { resolve } from "path"; + +const githubImportCss = readFileSync( + resolve(__dirname, "../components/GitHubImportModal.css"), + "utf8", +); + +function extractBlock(source: string, marker: string): string { + const start = source.indexOf(marker); + expect(start, `missing ${marker}`).toBeGreaterThanOrEqual(0); + const openingBrace = source.indexOf("{", start); + let depth = 0; + + for (let index = openingBrace; index < source.length; index += 1) { + if (source[index] === "{") depth += 1; + if (source[index] === "}") depth -= 1; + if (depth === 0) return source.slice(openingBrace + 1, index); + } + + throw new Error(`unterminated ${marker}`); +} + +function extractRule(source: string, selector: string): string { + return extractBlock(source, `${selector} {`); +} + +describe("GitHub import pull-request mobile layout (FN-9110)", () => { + it("lets Pull Requests rows grow and wraps unbroken branch names at the phone breakpoint", () => { + const phoneRules = extractBlock(githubImportCss, "@media (max-width: 640px)"); + const rowRule = extractRule(phoneRules, ".issue-item"); + const branchRule = extractRule(phoneRules, ".pull-branch-info"); + + expect(rowRule).toContain("flex-wrap: wrap;"); + expect(rowRule).not.toMatch(/min-height\s*:/); + expect(branchRule).toContain("min-width: 0;"); + expect(branchRule).toContain("overflow-wrap: anywhere;"); + }); + + it("contains long PR preview branch text and keeps the detail body scrollable on narrow sheets", () => { + const detailActionSection = githubImportCss.slice( + githubImportCss.indexOf("The selected-issue mobile action row"), + ); + const narrowRules = extractBlock(detailActionSection, "@media (max-width: 768px)"); + const previewBranchRule = extractRule(narrowRules, ".preview-branch"); + const detailPanelRule = extractRule(githubImportCss, ".github-import-detail-panel"); + + expect(previewBranchRule).toContain("min-width: 0;"); + expect(previewBranchRule).toContain("overflow-wrap: anywhere;"); + expect(detailPanelRule).toContain("min-height: 0;"); + expect(detailPanelRule).toContain("overflow: hidden;"); + }); +}); diff --git a/packages/dashboard/app/components/GitHubImportModal.css b/packages/dashboard/app/components/GitHubImportModal.css index 10d75457f7..37839d912a 100644 --- a/packages/dashboard/app/components/GitHubImportModal.css +++ b/packages/dashboard/app/components/GitHubImportModal.css @@ -1638,9 +1638,24 @@ counterpart added later cannot silently reintroduce the band. } + /* + FNXC:GitHubImport 2026-08-16-00:33: + Pull-request rows can contain a wrapped title, a branch line, and an Imported badge. They must use + their intrinsic height on phones: the former fixed minimum height constrained wrapped content and + made consecutive rows paint over one another. + */ .issue-item { flex-wrap: wrap; - min-height: 36px; + } + + /* + FNXC:GitHubImport 2026-08-16-00:33: + Pull head/base names can be unbroken generated identifiers. Let the branch line shrink and break + them inside the shared list row so both dialog and embedded phone presentations stay contained. + */ + .pull-branch-info { + min-width: 0; + overflow-wrap: anywhere; } .imported-badge { @@ -1678,6 +1693,16 @@ shrinkable action tracks preserve every full label by wrapping it inside its own shared touch-target token preserves every required target without clipping or scrolling. */ @media (max-width: 768px) { + /* + FNXC:GitHubImport 2026-08-16-00:33: + The selected pull-request sheet shares this preview branch block with desktop. On narrow and + landscape sheets, break unbroken branch names so its scrollable detail content cannot overflow. + */ + .preview-branch { + min-width: 0; + overflow-wrap: anywhere; + } + .github-import-detail-actions { gap: var(--space-xs); } diff --git a/packages/dashboard/app/components/__tests__/GitHubImportModal.test.tsx b/packages/dashboard/app/components/__tests__/GitHubImportModal.test.tsx index 48fb7eb50d..ff2fda8551 100644 --- a/packages/dashboard/app/components/__tests__/GitHubImportModal.test.tsx +++ b/packages/dashboard/app/components/__tests__/GitHubImportModal.test.tsx @@ -2701,6 +2701,38 @@ describe("GitHubImportModal", () => { + it.each(["modal", "embedded"] as const)("retains long pull rows, imported state, and the detail sheet in the %s presentation", async (presentation) => { + const longBranch = "branch-".repeat(24); + const longPull = { + number: 31, + title: "A deliberately long pull-request title that remains available to the mobile list row", + body: "PR body", + html_url: "https://github.com/owner/repo/pull/31", + headBranch: longBranch, + baseBranch: longBranch, + }; + const importedTask: Task = { + ...mockPRTask, + description: "Review and address any issues in this pull request.\n\nPR: https://github.com/owner/repo/pull/31", + }; + vi.mocked(fetchGitRemotes).mockResolvedValueOnce(singleRemote); + vi.mocked(apiFetchGitHubPulls).mockResolvedValueOnce([longPull, mockPulls[0]]); + + render(); + fireEvent.click(screen.getByRole("tab", { name: /Pull Requests/i })); + + const longRow = await screen.findByRole("button", { name: /Select pull request #31/i }); + expect(longRow).toHaveTextContent(longPull.title); + expect(longRow).toHaveTextContent(`${longBranch} → ${longBranch}`); + expect(longRow).toBeDisabled(); + expect(within(longRow).getByText("Imported")).toBeTruthy(); + + fireEvent.click(screen.getByRole("button", { name: /Select pull request #1/i })); + const detail = await screen.findByTestId("floating-window-github-import-detail"); + expect(detail.querySelector(".github-import-detail-panel")).toBeTruthy(); + expect(within(detail).getByTestId("github-import-detail-actions")).toBeTruthy(); + }); + it("calls apiImportGitHubPull when Import is clicked on PRs tab", async () => { vi.mocked(fetchGitRemotes).mockResolvedValueOnce(singleRemote); vi.mocked(apiFetchGitHubPulls).mockResolvedValueOnce(mockPulls);