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:
gsxdsm
2026-07-16 13:03:20 -07:00
parent f58b56488c
commit 9b7f282b84
3 changed files with 73 additions and 0 deletions

View 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.

View File

@@ -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*;/);
});
});

View File

@@ -172,3 +172,32 @@ or bare h2/table inline styles.
.quality-header-actions {
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;
}
}