FN-6974: fix tablet planning summary action wrapping
Prevent Planning Mode summary footer buttons from overlapping in tablet-width panes. - Add tablet-specific wrapping rules for Planning Mode summary actions. - Keep the action groups width-constrained so buttons wrap within the details pane. - Add a CSS regression test and patch changeset for the published CLI bundle. Files changed: .changeset/fn-6974-planning-tablet-actions.md | 7 +++ .../dashboard/app/components/PlanningModeModal.css | 29 ++++++++++++ .../__tests__/PlanningModeModal.css.test.ts | 52 ++++++++++++++++++++++ 3 files changed, 88 insertions(+) Fusion-Task-Id: FN-6974 Fusion-Task-Lineage: 25d63165-38a2-47fd-8d81-9eec24b3fb6f
This commit is contained in:
7
.changeset/fn-6974-planning-tablet-actions.md
Normal file
7
.changeset/fn-6974-planning-tablet-actions.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
summary: Prevent Planning Mode summary buttons from overlapping on tablet screens.
|
||||||
|
category: fix
|
||||||
|
dev: Adds a tablet responsive CSS contract for Planning Mode summary action wrapping.
|
||||||
@@ -1251,12 +1251,41 @@ An empty footer must NOT reserve vertical space or paint its divider band. When
|
|||||||
|
|
||||||
.planning-summary-actions {
|
.planning-summary-actions {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.planning-summary-actions-right {
|
.planning-summary-actions-right {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: var(--space-sm);
|
gap: var(--space-sm);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
FNXC:PlanningMode 2026-06-25-09:20:
|
||||||
|
Tablet embedded Planning keeps the desktop two-pane shell, so the summary footer must absorb the narrower detail pane instead of relying on the mobile full-width stack. Allow the three labeled actions to wrap inside the pane while preserving the desktop left/right grouping when space is available.
|
||||||
|
*/
|
||||||
|
@media (min-width: 769px) and (max-width: 1024px) {
|
||||||
|
.planning-summary-actions {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: flex-start;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.planning-summary-actions-right {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: flex-end;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.planning-summary-actions .btn {
|
||||||
|
flex-shrink: 1;
|
||||||
|
max-width: 100%;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Loading State */
|
/* Loading State */
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
import { resolve } from "node:path";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { getMediaBlocks } from "./PlanningModeModal.test-helpers";
|
||||||
|
|
||||||
|
const PLANNING_CSS_PATH = resolve(__dirname, "..", "PlanningModeModal.css");
|
||||||
|
const TABLET_SUMMARY_ACTIONS_QUERY = "@media (min-width: 769px) and (max-width: 1024px)";
|
||||||
|
const MOBILE_ACTIONS_QUERY = "@media (max-width: 768px)";
|
||||||
|
|
||||||
|
function loadPlanningCss(): string {
|
||||||
|
return readFileSync(PLANNING_CSS_PATH, "utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
function findRule(css: string, selector: string): string | undefined {
|
||||||
|
const escapedSelector = selector.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
|
return css.match(new RegExp(`${escapedSelector}\\s*\\{[^}]*\\}`))?.[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function findRules(css: string, selector: string): string[] {
|
||||||
|
const escapedSelector = selector.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
|
return [...css.matchAll(new RegExp(`${escapedSelector}\\s*\\{[^}]*\\}`, "g"))].map((match) => match[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectSomeRule(css: string, selector: string, pattern: RegExp): void {
|
||||||
|
expect(findRules(css, selector).some((rule) => pattern.test(rule))).toBe(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("PlanningModeModal CSS responsive action contract", () => {
|
||||||
|
it("FN-6974 keeps the summary action footer from overflowing on tablet while preserving desktop and mobile affordances", () => {
|
||||||
|
const css = loadPlanningCss();
|
||||||
|
const baseSummaryActionsRule = findRule(css, ".planning-summary-actions");
|
||||||
|
const baseSummaryRightRule = findRule(css, ".planning-summary-actions-right");
|
||||||
|
|
||||||
|
expect(baseSummaryActionsRule).toContain("justify-content: space-between;");
|
||||||
|
expect(baseSummaryRightRule).toContain("display: flex;");
|
||||||
|
|
||||||
|
const tabletCss = getMediaBlocks(css, TABLET_SUMMARY_ACTIONS_QUERY).join("\n");
|
||||||
|
expect(tabletCss).toBeTruthy();
|
||||||
|
expect(findRule(tabletCss, ".planning-summary-actions")).toMatch(/flex-wrap\s*:\s*wrap\s*;/);
|
||||||
|
expect(findRule(tabletCss, ".planning-summary-actions")).toMatch(/min-width\s*:\s*0\s*;/);
|
||||||
|
expect(findRule(tabletCss, ".planning-summary-actions-right")).toMatch(/flex-wrap\s*:\s*wrap\s*;/);
|
||||||
|
expect(findRule(tabletCss, ".planning-summary-actions-right")).toMatch(/min-width\s*:\s*0\s*;/);
|
||||||
|
expect(findRule(tabletCss, ".planning-summary-actions-right")).toMatch(/max-width\s*:\s*100%\s*;/);
|
||||||
|
expect(findRule(tabletCss, ".planning-summary-actions .btn")).toMatch(/max-width\s*:\s*100%\s*;/);
|
||||||
|
expect(findRule(tabletCss, ".planning-summary-actions .btn")).toMatch(/white-space\s*:\s*normal\s*;/);
|
||||||
|
|
||||||
|
const mobileCss = getMediaBlocks(css, MOBILE_ACTIONS_QUERY).join("\n");
|
||||||
|
expectSomeRule(mobileCss, ".planning-actions", /flex-direction\s*:\s*column\s*;/);
|
||||||
|
expectSomeRule(mobileCss, ".planning-summary-actions-right", /flex-direction\s*:\s*column\s*;/);
|
||||||
|
expectSomeRule(mobileCss, ".planning-summary-actions-right", /width\s*:\s*100%\s*;/);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user