FN-8548: keep GitHub import actions on one mobile row
Keep all selected GitHub issue actions touch-safe and visible on a single mobile row. - Group detail action controls separately from the comment composer and make their mobile tracks shrinkable. - Add responsive browser-smoke coverage at 320px, 390px, and 412px plus modal structure tests. - Document the mobile behavior and add a patch changeset. Files changed: .changeset/fn-8548-mobile-github-import-actions.md | 7 + docs/dashboard-guide.md | 8 +- .../dashboard/app/components/GitHubImportModal.css | 42 +++++- .../dashboard/app/components/GitHubImportModal.tsx | 80 ++++++------ .../__tests__/GitHubImportModal.test.tsx | 50 ++++++++ packages/dashboard/app/styles.css | 11 +- .../dashboard/scripts/browser-layout-smoke.mjs | 142 ++++++++++++++++++++- 7 files changed, 290 insertions(+), 50 deletions(-) Fusion-Task-Id: FN-8548 Fusion-Task-Lineage: 8fab6a1d-0ca9-4246-9707-e1fc84ca58e5 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/fn-8548-mobile-github-import-actions.md
Normal file
7
.changeset/fn-8548-mobile-github-import-actions.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Keep GitHub issue import actions on one usable mobile row.
|
||||
category: fix
|
||||
dev: Adds Blink geometry coverage for 320px, 390px, and 412px action bars.
|
||||
@@ -378,8 +378,14 @@ Use GitHub import on mobile:
|
||||
|
||||
1. Open the compact Header actions or bottom **More** sheet and select **Import from GitHub**.
|
||||
Expected outcome: the same import workflow opens in the mobile modal layout.
|
||||
<!--
|
||||
FNXC:GitHubImportDocs 2026-07-23-13:20:
|
||||
The mobile issue detail preserves the complete Close issue, Plan, Chat, and Import as task action set
|
||||
on one touch-safe row. Labels may wrap inside their own actions rather than being hidden or moved to
|
||||
an inaccessible second row.
|
||||
-->
|
||||
2. Choose the repository, issue/PR tab, candidate row, and detail action. For GitHub issues, choose **Import as task** for direct tracked creation or **Plan** to start Planning Mode with the issue context.
|
||||
Expected outcome: direct import creates the board task with the same GitHub provenance/tracking metadata as the desktop/tablet **Import Tasks** view; Plan opens the Planning Mode interview without source-issue tracking.
|
||||
Expected outcome: direct import creates the board task with the same GitHub provenance/tracking metadata as the desktop/tablet **Import Tasks** view; Plan opens the Planning Mode interview without source-issue tracking. When all GitHub issue actions are available, their full labels remain on one touch-safe action row.
|
||||
3. While a candidate detail sheet is open, use the platform Back gesture or control.
|
||||
Expected outcome: the first Back dismisses only the issue, pull request, or GitLab detail and returns to the import candidate list; a second Back dismisses the import form.
|
||||
|
||||
|
||||
@@ -345,8 +345,16 @@ when the preview is short; the top border separates it from scrolling content.
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
/* Keep the composer and its action controls as independently sized flex rows. */
|
||||
.github-import-detail-action-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
|
||||
/* Never let the primary action's label clip when the bar is narrow. */
|
||||
.github-import-detail-actions .btn {
|
||||
.github-import-detail-action-row .btn {
|
||||
flex: 0 0 auto;
|
||||
min-width: max-content;
|
||||
min-height: 36px;
|
||||
@@ -1655,6 +1663,38 @@ counterpart added later cannot silently reintroduce the band.
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:GitHubImport 2026-07-23-12:18:
|
||||
The selected-issue mobile action row must keep Import as task alongside optional Close issue,
|
||||
Plan, and Chat controls. The comment composer remains on its own full-width row, while equal
|
||||
shrinkable action tracks preserve every full label by wrapping it inside its own button and the
|
||||
shared touch-target token preserves every required target without clipping or scrolling.
|
||||
*/
|
||||
@media (max-width: 768px) {
|
||||
.github-import-detail-actions {
|
||||
gap: var(--space-xs);
|
||||
}
|
||||
|
||||
.github-import-detail-action-row {
|
||||
flex: 1 1 100%;
|
||||
min-width: 0;
|
||||
align-items: stretch;
|
||||
flex-wrap: nowrap;
|
||||
gap: var(--space-xs);
|
||||
}
|
||||
|
||||
.github-import-detail-action-row .btn {
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
min-height: var(--touch-target-min-size) !important;
|
||||
padding: var(--space-xs);
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
white-space: normal;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes github-import-spinner-spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
|
||||
@@ -2316,48 +2316,50 @@ export function GitHubImportModal({ isOpen, onClose, onImport, onPlanningMode, o
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
{activeTab === "issues" && selectedIssue && !selectedIssueClosed && (
|
||||
<div className="github-import-detail-action-row" data-testid="github-import-detail-action-row">
|
||||
{activeTab === "issues" && selectedIssue && !selectedIssueClosed && (
|
||||
<button
|
||||
className="btn btn-danger github-import-issue-close"
|
||||
data-testid="github-import-issue-close"
|
||||
onClick={handleCloseIssue}
|
||||
disabled={closingIssue}
|
||||
title={t("git.closeIssueTitle", "Close issue #{{number}}", { number: selectedIssue.number })}
|
||||
>
|
||||
{closingIssue ? <Loader2 size={14} className="spin" /> : t("git.closeIssue", "Close issue")}
|
||||
</button>
|
||||
)}
|
||||
{activeTab === "issues" && selectedIssue && onPlanningMode && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn github-import-action"
|
||||
data-testid="github-import-action-plan"
|
||||
onClick={handlePlanIssue}
|
||||
disabled={importing || isUrlImported(selectedIssue.html_url)}
|
||||
>
|
||||
{t("git.planIssue", "Plan")}
|
||||
</button>
|
||||
)}
|
||||
{onOpenChatWithPrefill && (activeTab === "issues" ? selectedIssue?.html_url?.trim() : selectedPull?.html_url?.trim()) && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn github-import-action"
|
||||
data-testid="github-import-action-chat"
|
||||
onClick={handleChatAboutSelection}
|
||||
>
|
||||
{t("git.chatAboutIssue", "Chat")}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
className="btn btn-danger github-import-issue-close"
|
||||
data-testid="github-import-issue-close"
|
||||
onClick={handleCloseIssue}
|
||||
disabled={closingIssue}
|
||||
title={t("git.closeIssueTitle", "Close issue #{{number}}", { number: selectedIssue.number })}
|
||||
className="btn btn-primary github-import-action"
|
||||
data-testid="github-import-action-top"
|
||||
onClick={handleImport}
|
||||
disabled={
|
||||
(activeTab === "issues" ? selectedIssueNumber === null || isUrlImported(selectedIssue?.html_url) : selectedPullNumber === null || isUrlImported(selectedPull?.html_url)) || importing
|
||||
}
|
||||
>
|
||||
{closingIssue ? <Loader2 size={14} className="spin" /> : t("git.closeIssue", "Close issue")}
|
||||
{importing ? <Loader2 size={14} className="spin" /> : activeTab === "pulls" ? t("git.resolveFeedback", "Resolve feedback") : t("git.importAsTask", "Import as task")}
|
||||
</button>
|
||||
)}
|
||||
{activeTab === "issues" && selectedIssue && onPlanningMode && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn github-import-action"
|
||||
data-testid="github-import-action-plan"
|
||||
onClick={handlePlanIssue}
|
||||
disabled={importing || isUrlImported(selectedIssue.html_url)}
|
||||
>
|
||||
{t("git.planIssue", "Plan")}
|
||||
</button>
|
||||
)}
|
||||
{onOpenChatWithPrefill && (activeTab === "issues" ? selectedIssue?.html_url?.trim() : selectedPull?.html_url?.trim()) && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn github-import-action"
|
||||
data-testid="github-import-action-chat"
|
||||
onClick={handleChatAboutSelection}
|
||||
>
|
||||
{t("git.chatAboutIssue", "Chat")}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
className="btn btn-primary github-import-action"
|
||||
data-testid="github-import-action-top"
|
||||
onClick={handleImport}
|
||||
disabled={
|
||||
(activeTab === "issues" ? selectedIssueNumber === null || isUrlImported(selectedIssue?.html_url) : selectedPullNumber === null || isUrlImported(selectedPull?.html_url)) || importing
|
||||
}
|
||||
>
|
||||
{importing ? <Loader2 size={14} className="spin" /> : activeTab === "pulls" ? t("git.resolveFeedback", "Resolve feedback") : t("git.importAsTask", "Import as task")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FloatingWindow>
|
||||
|
||||
@@ -3160,6 +3160,56 @@ describe("GitHubImportModal — detail actions sit at the bottom (operator repor
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps the four populated issue actions uniquely ordered in the shared mobile bar", async () => {
|
||||
vi.mocked(apiFetchGitHubIssues).mockResolvedValueOnce([
|
||||
{ number: 44, title: "Four actions", body: "Body", html_url: "https://github.com/dustinbyrne/kb/issues/44", labels: [], state: "open" },
|
||||
]);
|
||||
render(
|
||||
<GitHubImportModal
|
||||
isOpen
|
||||
onClose={onClose}
|
||||
onImport={onImport}
|
||||
onPlanningMode={vi.fn()}
|
||||
onOpenChatWithPrefill={vi.fn()}
|
||||
tasks={[]}
|
||||
/>,
|
||||
);
|
||||
fireEvent.click(await screen.findByRole("button", { name: /Select issue #44/i }));
|
||||
|
||||
const bar = await screen.findByTestId("github-import-detail-actions");
|
||||
const actionRow = within(bar).getByTestId("github-import-detail-action-row");
|
||||
const composer = within(bar).getByTestId("github-import-issue-comment-input");
|
||||
const actions = [
|
||||
within(actionRow).getByTestId("github-import-issue-close"),
|
||||
within(actionRow).getByTestId("github-import-action-plan"),
|
||||
within(actionRow).getByTestId("github-import-action-chat"),
|
||||
within(actionRow).getByTestId("github-import-action-top"),
|
||||
];
|
||||
expect(actions.map((action) => action.textContent?.trim())).toEqual(["Close issue", "Plan", "Chat", "Import as task"]);
|
||||
expect(composer.closest(".github-import-detail-actions")).toBe(bar);
|
||||
expect(actionRow.previousElementSibling).toBe(composer.closest("form"));
|
||||
expect(within(actionRow).getAllByRole("button", { name: /^Close issue$/i })).toHaveLength(1);
|
||||
expect(within(actionRow).getAllByRole("button", { name: /^Plan$/i })).toHaveLength(1);
|
||||
expect(within(actionRow).getAllByRole("button", { name: /^Chat$/i })).toHaveLength(1);
|
||||
expect(within(actionRow).getAllByRole("button", { name: /^Import as task$/i })).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("uses shrinkable, non-wrapping mobile action tracks while retaining desktop behavior", () => {
|
||||
const source = readFileSync(resolve(__dirname, "../GitHubImportModal.css"), "utf8");
|
||||
const mobileRule = source.match(/@media\s\(max-width:\s768px\)\s\{[\s\S]*?\.github-import-detail-action-row\s\.btn\s\{[\s\S]*?\n\s{2}\}/)?.[0] ?? "";
|
||||
|
||||
expect(mobileRule).toContain(".github-import-detail-action-row {");
|
||||
expect(mobileRule).toContain("flex-wrap: nowrap;");
|
||||
expect(mobileRule).toContain("flex: 1 1 100%;");
|
||||
expect(mobileRule).toContain("gap: var(--space-xs);");
|
||||
expect(mobileRule).toContain("flex: 1 1 0;");
|
||||
expect(mobileRule).toContain("min-width: 0;");
|
||||
expect(mobileRule).toContain("min-height: var(--touch-target-min-size) !important;");
|
||||
expect(mobileRule).toContain("white-space: normal;");
|
||||
expect(mobileRule).toContain("overflow-wrap: anywhere;");
|
||||
expect(source.match(/\.github-import-detail-actions\s*\{[^}]*flex-wrap: wrap;/)?.[0]).toBeTruthy();
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
FNXC:GitHubImport 2026-07-17-12:00:
|
||||
|
||||
@@ -71,8 +71,8 @@ html {
|
||||
recommended by Apple HIG and WCAG 2.5.8. Apply to interactive
|
||||
elements that are too small on mobile (links, small buttons, icons). */
|
||||
.touch-target {
|
||||
min-width: 44px;
|
||||
min-height: 44px;
|
||||
min-width: var(--touch-target-min-size);
|
||||
min-height: var(--touch-target-min-size);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -146,6 +146,13 @@ html {
|
||||
--space-xl: 24px;
|
||||
--space-2xl: 32px;
|
||||
|
||||
/*
|
||||
FNXC:DashboardStyling 2026-07-23-13:05:
|
||||
Mobile action controls need one shared minimum hit-area token so compact layouts can retain the
|
||||
44px accessibility floor without embedding a raw dimension in component-specific rules.
|
||||
*/
|
||||
--touch-target-min-size: 44px;
|
||||
|
||||
/*
|
||||
FNXC:QuickAddActionRow 2026-07-16-14:00:
|
||||
FN-8147 pins Quick Add action-row control heights to literal values rather than
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#!/usr/bin/env node
|
||||
/* global WebSocket, URL, fetch, console, setTimeout, clearTimeout */
|
||||
|
||||
import { Buffer } from "node:buffer";
|
||||
import { spawn } from "node:child_process";
|
||||
import { createServer } from "node:http";
|
||||
import { superviseSpawn } from "@fusion/core";
|
||||
import { readFile, readdir, rm, stat, mkdtemp } from "node:fs/promises";
|
||||
import { readFile, readdir, rm, stat, mkdtemp, writeFile } from "node:fs/promises";
|
||||
import { existsSync } from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
@@ -15,6 +16,7 @@ const dashboardRoot = path.resolve(import.meta.dirname, "..");
|
||||
const appRoot = path.join(dashboardRoot, "app");
|
||||
const clientDistRoot = path.join(dashboardRoot, "dist", "client");
|
||||
const requireBrowser = process.argv.includes("--require-browser") || process.env.FUSION_BROWSER_SMOKE_REQUIRE === "1";
|
||||
const screenshotPath = process.env.FUSION_BROWSER_SMOKE_SCREENSHOT;
|
||||
|
||||
function log(message) {
|
||||
console.log(`[dashboard-browser-smoke] ${message}`);
|
||||
@@ -24,13 +26,15 @@ function fail(message) {
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:GitHubImport 2026-07-23-13:05:
|
||||
The mobile GitHub action-bar regression measures emitted CSS, so every smoke invocation must
|
||||
rebuild the client before loading it. Reusing a stale dist/client artifact could make geometry
|
||||
assertions pass against styles predating the one-row compact-layout requirement.
|
||||
*/
|
||||
async function loadDashboardCss() {
|
||||
try {
|
||||
return await readEmittedClientCss();
|
||||
} catch {
|
||||
await runCommand("pnpm", ["--filter", "@fusion/dashboard", "build:client"], dashboardRoot);
|
||||
return readEmittedClientCss();
|
||||
}
|
||||
await runCommand("pnpm", ["--filter", "@fusion/dashboard", "build:client"], dashboardRoot);
|
||||
return readEmittedClientCss();
|
||||
}
|
||||
|
||||
async function readEmittedClientCss() {
|
||||
@@ -188,6 +192,29 @@ export function createSmokeHtml() {
|
||||
measure the shared ViewHeader flex geometry. Exercise the unread Inbox's tightest
|
||||
badge + Compose + Mark all read + Refresh row and both Compose + Refresh-only states.
|
||||
*/
|
||||
/*
|
||||
FNXC:GitHubImport 2026-07-23-12:18:
|
||||
FN-8548 mirrors the selected GitHub issue's four-action footer with the emitted dashboard CSS.
|
||||
Blink must measure the production responsive contract at each supported phone width because jsdom
|
||||
cannot detect wrapping, flex-track shrinkage, overflow, or touch-target geometry.
|
||||
*/
|
||||
const githubImportMobileActionFixture = `
|
||||
<section data-smoke="github-import-mobile-actions" aria-label="GitHub issue detail actions">
|
||||
<div class="github-import-detail-actions" data-testid="github-import-detail-actions">
|
||||
<form class="github-import-issue-comment-composer">
|
||||
<textarea class="input github-import-issue-comment-composer__input" aria-label="Add comment"></textarea>
|
||||
<button class="btn btn-primary github-import-issue-comment-composer__submit" type="submit">Add comment</button>
|
||||
</form>
|
||||
<div class="github-import-detail-action-row" data-testid="github-import-detail-action-row">
|
||||
<button class="btn btn-danger github-import-issue-close" type="button">Close issue</button>
|
||||
<button class="btn github-import-action" type="button">Plan</button>
|
||||
<button class="btn github-import-action" type="button">Chat</button>
|
||||
<button class="btn btn-primary github-import-action" type="button">Import as task</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
`;
|
||||
|
||||
const mailboxMobileHeaderFixtures = [
|
||||
["unread-inbox", '<span class="mailbox-unread-badge">9</span><button class="btn btn-sm btn-primary" data-testid="mailbox-header-compose" type="button"><svg width="14" height="14" viewBox="0 0 14 14" aria-hidden="true"><path d="M2 2h10v10H2z"/></svg><span>Compose</span></button><button class="btn btn-sm btn-secondary" data-testid="mailbox-mark-all-read" type="button"><svg width="14" height="14" viewBox="0 0 14 14" aria-hidden="true"><path d="M2 7l3 3 7-7"/></svg><span>Mark all read</span></button><button class="btn-icon" data-testid="mailbox-refresh" type="button" aria-label="Refresh"><svg width="14" height="14" viewBox="0 0 14 14" aria-hidden="true"><path d="M2 7a5 5 0 1 0 2-4"/></svg></button>'],
|
||||
["read-inbox", '<button class="btn btn-sm btn-primary" data-testid="mailbox-header-compose" type="button"><svg width="14" height="14" viewBox="0 0 14 14" aria-hidden="true"><path d="M2 2h10v10H2z"/></svg><span>Compose</span></button><button class="btn-icon" data-testid="mailbox-refresh" type="button" aria-label="Refresh"><svg width="14" height="14" viewBox="0 0 14 14" aria-hidden="true"><path d="M2 7a5 5 0 1 0 2-4"/></svg></button>'],
|
||||
@@ -288,6 +315,8 @@ export function createSmokeHtml() {
|
||||
${mailboxMobileHeaderFixtures}
|
||||
</section>
|
||||
|
||||
${githubImportMobileActionFixture}
|
||||
|
||||
<footer class="executor-status-bar">
|
||||
<div class="executor-status-bar__segment">
|
||||
<span class="executor-status-bar__indicator executor-status-bar__indicator--running"></span>
|
||||
@@ -1267,6 +1296,105 @@ async function runSmokeChecks(page, pageUrl) {
|
||||
JSON.stringify(mailboxMobileHeaderLayout),
|
||||
);
|
||||
|
||||
/*
|
||||
FNXC:GitHubImport 2026-07-23-12:18:
|
||||
Four GitHub issue actions must remain one contained, non-overlapping row at 320px, 390px, and
|
||||
412px. This verifies the full labels and usable 44px minimum targets in a real browser instead
|
||||
of relying on jsdom's zero-layout model or a CSS-source-only assertion.
|
||||
*/
|
||||
for (const width of [320, 390, 412]) {
|
||||
await page.send("Emulation.setDeviceMetricsOverride", {
|
||||
width,
|
||||
height: 844,
|
||||
deviceScaleFactor: 2,
|
||||
mobile: true,
|
||||
});
|
||||
await evaluate(page, "document.fonts ? document.fonts.ready.then(() => true) : true");
|
||||
const githubImportActionsLayout = await evaluate(page, `(() => {
|
||||
const bar = document.querySelector('[data-smoke="github-import-mobile-actions"] .github-import-detail-actions');
|
||||
const actionRow = bar.querySelector('.github-import-detail-action-row');
|
||||
const barRect = bar.getBoundingClientRect();
|
||||
const actionRowRect = actionRow.getBoundingClientRect();
|
||||
const composer = bar.querySelector('.github-import-issue-comment-composer');
|
||||
const composerRect = composer.getBoundingClientRect();
|
||||
const buttons = [...actionRow.querySelectorAll('button')].map((button) => {
|
||||
const rect = button.getBoundingClientRect();
|
||||
const style = getComputedStyle(button);
|
||||
return {
|
||||
label: button.innerText.trim(),
|
||||
left: rect.left,
|
||||
right: rect.right,
|
||||
top: rect.top,
|
||||
bottom: rect.bottom,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
overflowX: button.scrollWidth - button.clientWidth,
|
||||
overflowY: button.scrollHeight - button.clientHeight,
|
||||
display: style.display,
|
||||
visibility: style.visibility,
|
||||
opacity: style.opacity,
|
||||
textOverflow: style.textOverflow,
|
||||
};
|
||||
});
|
||||
return {
|
||||
composer: {
|
||||
bottom: composerRect.bottom,
|
||||
top: composerRect.top,
|
||||
},
|
||||
actionRow: {
|
||||
left: actionRowRect.left,
|
||||
right: actionRowRect.right,
|
||||
top: actionRowRect.top,
|
||||
bottom: actionRowRect.bottom,
|
||||
overflowX: actionRow.scrollWidth - actionRow.clientWidth,
|
||||
overflowY: actionRow.scrollHeight - actionRow.clientHeight,
|
||||
},
|
||||
bar: {
|
||||
left: barRect.left,
|
||||
right: barRect.right,
|
||||
top: barRect.top,
|
||||
bottom: barRect.bottom,
|
||||
overflowX: bar.scrollWidth - bar.clientWidth,
|
||||
overflowY: bar.scrollHeight - bar.clientHeight,
|
||||
},
|
||||
buttons,
|
||||
};
|
||||
})()`);
|
||||
const expectedLabels = ["Close issue", "Plan", "Chat", "Import as task"];
|
||||
assertSmokeResult(
|
||||
`GitHub issue actions remain a visible one-row touch-safe layout at ${width}px`,
|
||||
githubImportActionsLayout.buttons.length === expectedLabels.length
|
||||
&& githubImportActionsLayout.buttons.every((button, index, buttons) =>
|
||||
button.label === expectedLabels[index]
|
||||
&& button.top === buttons[0].top
|
||||
&& button.bottom === buttons[0].bottom
|
||||
&& button.left >= githubImportActionsLayout.actionRow.left - 1
|
||||
&& button.right <= githubImportActionsLayout.actionRow.right + 1
|
||||
&& button.width >= 44
|
||||
&& button.height >= 44
|
||||
&& button.overflowX <= 1
|
||||
&& button.overflowY <= 1
|
||||
&& button.display !== "none"
|
||||
&& button.visibility !== "hidden"
|
||||
&& button.opacity !== "0"
|
||||
&& button.textOverflow !== "ellipsis")
|
||||
&& githubImportActionsLayout.buttons.every((button, index, buttons) =>
|
||||
index === 0 || buttons[index - 1].right <= button.left + 1)
|
||||
&& githubImportActionsLayout.actionRow.overflowX <= 1
|
||||
&& githubImportActionsLayout.actionRow.overflowY <= 1
|
||||
&& githubImportActionsLayout.composer.bottom <= githubImportActionsLayout.actionRow.top + 1
|
||||
&& githubImportActionsLayout.bar.overflowX <= 1
|
||||
&& githubImportActionsLayout.bar.overflowY <= 1,
|
||||
JSON.stringify(githubImportActionsLayout),
|
||||
);
|
||||
|
||||
if (screenshotPath && width === 390) {
|
||||
const screenshot = await page.send("Page.captureScreenshot", { format: "png" });
|
||||
await writeFile(screenshotPath, Buffer.from(screenshot.data, "base64"));
|
||||
log(`saved GitHub import mobile screenshot to ${screenshotPath}`);
|
||||
}
|
||||
}
|
||||
|
||||
await page.send("Emulation.setDeviceMetricsOverride", {
|
||||
width: 412,
|
||||
height: 915,
|
||||
|
||||
Reference in New Issue
Block a user