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) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/fn-9110-github-import-pulls-mobile.md
Normal file
7
.changeset/fn-9110-github-import-pulls-mobile.md
Normal file
@@ -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.
|
||||
@@ -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;");
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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(<GitHubImportModal isOpen onClose={onClose} onImport={onImport} tasks={[importedTask]} presentation={presentation} />);
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user