FN-8189: fix mobile task-detail scrollbar gutter
Hide the mobile task-detail scrollbar to keep content insets visually symmetric. - Preserve touch scrolling while removing the mobile non-overlay scrollbar track - Add responsive regression coverage for scrollbar and inset behavior - Add a patch changeset for the mobile layout fix Files changed: .../fn-8189-task-detail-mobile-scrollbar-gutter.md | 7 +++ .../dashboard/app/components/TaskDetailModal.css | 12 +++++ ...etailModal.responsive-and-dependencies.test.tsx | 55 ++++++++++++++++++++++ 3 files changed, 74 insertions(+) Fusion-Task-Id: FN-8189 Fusion-Task-Lineage: 3a9300f7-9732-417b-95b0-b65a79efaaf6 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
summary: Fix lopsided right gutter in the task detail view on mobile.
|
||||||
|
category: fix
|
||||||
|
dev: Hides the mobile `.detail-body` scrollbar while preserving touch scrolling and the desktop scrollbar.
|
||||||
@@ -3427,10 +3427,22 @@ Live and Feed Activity expansion overlays the content instead of reserving a too
|
|||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
FNXC:TaskDetailModalResponsive 2026-07-16-00:00:
|
||||||
|
FN-8189 hides the mobile `.detail-body` scrollbar so a non-overlay vertical
|
||||||
|
track cannot reserve right-edge layout space and recreate the lopsided gutter
|
||||||
|
fixed by FN-8166. Keep touch scrolling automatic; desktop retains its thin,
|
||||||
|
visible scrollbar outside this breakpoint.
|
||||||
|
*/
|
||||||
.detail-body {
|
.detail-body {
|
||||||
padding: calc(var(--space-md) + var(--space-xs) / 2);
|
padding: calc(var(--space-md) + var(--space-xs) / 2);
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-body::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-title {
|
.detail-title {
|
||||||
|
|||||||
@@ -732,6 +732,61 @@ describe("TaskDetailModal", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("FN-8189 hides only the mobile detail-body scrollbar to preserve symmetric task-detail insets", () => {
|
||||||
|
const css = readDashboardStylesSource();
|
||||||
|
const baseDetailBodyBlock = getExactCssRuleBlock(css, ".detail-body");
|
||||||
|
const baseScrollbarBlock = getExactCssRuleBlock(css, ".detail-body::-webkit-scrollbar");
|
||||||
|
const mobileBlock = getCssAtRuleBlockContainingExactRule(css, "@media (max-width: 768px)", ".detail-body");
|
||||||
|
const mobileDetailBodyBlock = getExactCssRuleBlock(mobileBlock, ".detail-body");
|
||||||
|
const mobileScrollbarBlock = getExactCssRuleBlock(mobileBlock, ".detail-body::-webkit-scrollbar");
|
||||||
|
const mobileActivityBlock = getExactCssRuleBlock(mobileBlock, ".detail-activity");
|
||||||
|
const mobileInterventionsBlock = getExactCssRuleBlock(mobileBlock, ".detail-activity--interventions");
|
||||||
|
const mobilePrBlock = getExactCssRuleBlock(
|
||||||
|
getCssAtRuleBlockContainingExactRule(css, "@media (max-width: 768px)", ".detail-pr-tab"),
|
||||||
|
".detail-pr-tab",
|
||||||
|
);
|
||||||
|
const embeddedBodyBlock = getExactCssRuleBlock(
|
||||||
|
css,
|
||||||
|
".task-detail-content--embedded .modal-header,\n.task-detail-content--embedded .detail-body,\n.task-detail-content--embedded .detail-tabs,\n.task-detail-content--embedded .modal-actions",
|
||||||
|
);
|
||||||
|
const allMobileCss = getCssAtRuleBlocks(css, "@media (max-width: 768px)").join("\n");
|
||||||
|
|
||||||
|
expect(baseDetailBodyBlock).toContain("scrollbar-width: thin;");
|
||||||
|
expect(baseScrollbarBlock).toContain("width: 6px;");
|
||||||
|
expect(mobileDetailBodyBlock).toContain("padding: calc(var(--space-md) + var(--space-xs) / 2);");
|
||||||
|
expect(mobileDetailBodyBlock).toContain("overflow-x: hidden;");
|
||||||
|
expect(mobileDetailBodyBlock).toContain("overflow-y: auto;");
|
||||||
|
expect(mobileDetailBodyBlock).toContain("scrollbar-width: none;");
|
||||||
|
expect(mobileScrollbarBlock).toContain("display: none;");
|
||||||
|
|
||||||
|
expect(mobileActivityBlock).toContain("padding-inline-end: 0;");
|
||||||
|
expect(mobileBlock).toContain("padding-inline-end: calc(var(--space-2xl) + var(--space-sm));");
|
||||||
|
expect(mobileInterventionsBlock).toContain("padding-inline-end: 0;");
|
||||||
|
expect(mobilePrBlock.trim()).toBe("gap: var(--space-md);");
|
||||||
|
expect(allMobileCss).not.toMatch(/\.task-changes-tab\s*\{[^}]*\bpadding(?:-[\w-]+)?\s*:/);
|
||||||
|
expect(embeddedBodyBlock).toContain("width: 100%;");
|
||||||
|
expect(embeddedBodyBlock).toContain("min-width: 0;");
|
||||||
|
expect(embeddedBodyBlock).toContain("max-width: 100%;");
|
||||||
|
|
||||||
|
for (const selector of [
|
||||||
|
".detail-section",
|
||||||
|
".detail-section--plan-prompt",
|
||||||
|
".detail-section--original-prompt",
|
||||||
|
".detail-body--chat",
|
||||||
|
".detail-section--chat",
|
||||||
|
".detail-body--agent-log",
|
||||||
|
".detail-body--planner-chat",
|
||||||
|
]) {
|
||||||
|
const escapedSelector = selector.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
|
const exactSelector = `${escapedSelector}(?![-\\w])`;
|
||||||
|
const declarations = [...neutralizeCssCommentBraces(allMobileCss).matchAll(new RegExp(`${exactSelector}[^{}]*\\{([^{}]*)\\}`, "g"))]
|
||||||
|
.map((match) => match[1])
|
||||||
|
.join("\n");
|
||||||
|
expect(declarations, `${selector} mobile right inset`).not.toMatch(/\bpadding-(?:inline-end|right)\s*:/);
|
||||||
|
expect(declarations, `${selector} mobile asymmetric padding`).not.toMatch(/\bpadding\s*:/);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it("renders responsive structural classes (modal-lg, overlay, spacer, tabs, detail-body)", () => {
|
it("renders responsive structural classes (modal-lg, overlay, spacer, tabs, detail-body)", () => {
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<TaskDetailModal
|
<TaskDetailModal
|
||||||
|
|||||||
Reference in New Issue
Block a user