FN-8131: wrap Quality header actions on mobile
Keep Quality preset and refresh actions visible beneath the title on compact screens. - Wrap the scoped Quality dashboard header at mobile breakpoints. - Give the title and action cluster full-width rows without desktop regressions. - Add responsive CSS coverage and a patch changeset. Files changed: .changeset/fn-8131-quality-mobile-header.md | 7 ++++ .../__tests__/dashboard-view-mobile-header.test.ts | 37 ++++++++++++++++++++++ .../fusion-plugin-quality/src/dashboard-view.css | 29 +++++++++++++++++ 3 files changed, 73 insertions(+) Fusion-Task-Id: FN-8131 Fusion-Task-Lineage: 920ae59f-327a-45c8-90b1-1b4d7639442f Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/fn-8131-quality-mobile-header.md
Normal file
7
.changeset/fn-8131-quality-mobile-header.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
summary: Keep Quality hub actions visible beneath the title on mobile.
|
||||||
|
category: fix
|
||||||
|
dev: Scope the responsive header-wrap treatment to the bundled Quality plugin.
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
import { dirname, join } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
const srcRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
|
||||||
|
const dashboardViewCss = readFileSync(join(srcRoot, "dashboard-view.css"), "utf8");
|
||||||
|
const mobileMediaQuery = "@media (max-width: 768px), (max-height: 480px)";
|
||||||
|
|
||||||
|
function mediaBlock(css: string, query: string): string {
|
||||||
|
const start = css.indexOf(query);
|
||||||
|
expect(start, `expected ${query} mobile rule`).toBeGreaterThanOrEqual(0);
|
||||||
|
|
||||||
|
const openBrace = css.indexOf("{", start);
|
||||||
|
let depth = 0;
|
||||||
|
for (let index = openBrace; index < css.length; index += 1) {
|
||||||
|
if (css[index] === "{") depth += 1;
|
||||||
|
if (css[index] === "}") depth -= 1;
|
||||||
|
if (depth === 0) return css.slice(openBrace + 1, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`unterminated ${query} rule`);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("QualityDashboardView mobile header CSS", () => {
|
||||||
|
it("gives the title its own row and wraps preset actions below it", () => {
|
||||||
|
const mobileCss = mediaBlock(dashboardViewCss, mobileMediaQuery);
|
||||||
|
|
||||||
|
// FNXC:Quality 2026-07-16-12:45: FN-8131 preserves the mobile invariant for every run-count width and both canonical mobile breakpoint arms.
|
||||||
|
expect(mobileCss).toMatch(/\.quality-view \.view-header\s*\{[\s\S]*?flex-wrap\s*:\s*wrap\s*;/);
|
||||||
|
expect(mobileCss).toMatch(/\.quality-view \.view-header__title\s*\{[\s\S]*?flex\s*:\s*1\s+0\s+100%\s*;/);
|
||||||
|
expect(mobileCss).toMatch(/\.quality-view \.view-header__title\s*\{[\s\S]*?min-width\s*:\s*100%\s*;/);
|
||||||
|
expect(mobileCss).toMatch(/\.quality-view \.view-header__title span\s*\{[\s\S]*?overflow\s*:\s*visible\s*;/);
|
||||||
|
expect(mobileCss).toMatch(/\.quality-view \.view-header__actions\s*\{[\s\S]*?width\s*:\s*100%\s*;/);
|
||||||
|
expect(mobileCss).toMatch(/\.quality-view \.view-header__actions\s*\{[\s\S]*?margin-left\s*:\s*0\s*;/);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -172,3 +172,32 @@ or bare h2/table inline styles.
|
|||||||
.quality-header-actions {
|
.quality-header-actions {
|
||||||
display: contents;
|
display: contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
FNXC:Quality 2026-07-16-12:45:
|
||||||
|
FN-8131: On mobile, Quality's wide preset and refresh action cluster must give the title its own full-width row and wrap below it instead of shrinking the title to a `Q…` ellipsis or clipping buttons. This mirrors InsightsView FN-7830 while preserving the shared desktop header clamp.
|
||||||
|
*/
|
||||||
|
@media (max-width: 768px), (max-height: 480px) {
|
||||||
|
.quality-view .view-header {
|
||||||
|
align-items: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: var(--space-sm) var(--space-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.quality-view .view-header__title {
|
||||||
|
flex: 1 0 100%;
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quality-view .view-header__title span {
|
||||||
|
overflow: visible;
|
||||||
|
text-overflow: clip;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quality-view .view-header__actions {
|
||||||
|
justify-content: flex-start;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user