FN-6380: widen Activity Log tablet layout
Widen the Activity Log modal at tablet widths so header controls stay reachable. - Add component-scoped tablet CSS that widens only the Activity Log modal. - Allow the Activity Log header and action controls to wrap while keeping close pinned right. - Cover the tablet-only layout contract with a CSS regression test. Files changed: .../__tests__/activity-log-tablet-layout.test.ts | 73 ++++++++++++++++++++++ packages/dashboard/app/components/ScriptsModal.css | 49 +++++++++++++++ 2 files changed, 122 insertions(+) Fusion-Task-Id: FN-6380 Fusion-Task-Lineage: ae7cf5a6-c69d-4756-a4fa-1e4346b9c077
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { loadAllAppCss } from "../test/cssFixture";
|
||||
|
||||
/**
|
||||
* Stylesheet regression test for Activity Log tablet layout.
|
||||
*
|
||||
* The desktop .modal-lg width is too narrow for the Activity Log header at
|
||||
* tablet widths, so a component-scoped tablet media block must widen only the
|
||||
* Activity Log modal and wrap the header controls. If these tablet rules are
|
||||
* removed, refresh/close can be clipped between 769px and 1024px.
|
||||
*/
|
||||
describe("activity-log-tablet-layout.css", () => {
|
||||
const cssContent = loadAllAppCss();
|
||||
|
||||
function extractTabletMediaBlocks(content: string): string {
|
||||
const blocks: string[] = [];
|
||||
const regex = /@media[^{}]*\(min-width:\s*769px\)[^{}]*\(max-width:\s*1024px\)[^{}]*\{/g;
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
while ((match = regex.exec(content)) !== null) {
|
||||
const startIdx = match.index + match[0].length;
|
||||
let braceCount = 1;
|
||||
let endIdx = startIdx;
|
||||
|
||||
while (braceCount > 0 && endIdx < content.length) {
|
||||
if (content[endIdx] === "{") braceCount++;
|
||||
if (content[endIdx] === "}") braceCount--;
|
||||
endIdx++;
|
||||
}
|
||||
|
||||
if (braceCount === 0) {
|
||||
blocks.push(content.slice(startIdx, endIdx - 1));
|
||||
}
|
||||
}
|
||||
|
||||
return blocks.join("\n");
|
||||
}
|
||||
|
||||
const tabletCss = extractTabletMediaBlocks(cssContent);
|
||||
|
||||
it("defines tablet Activity Log rules for the broken 769px–1024px range", () => {
|
||||
expect(tabletCss).toContain(".activity-log-modal");
|
||||
expect(tabletCss).toContain(".activity-log-header");
|
||||
});
|
||||
|
||||
it("widens only the Activity Log modal beyond the modal-lg base width", () => {
|
||||
expect(tabletCss).toMatch(/\.activity-log-modal\s*\{[^}]*width:\s*calc\(100vw\s*-\s*var\(--space-2xl\)\)/);
|
||||
expect(tabletCss).toMatch(/\.activity-log-modal\s*\{[^}]*max-width:\s*calc\(100vw\s*-\s*var\(--space-2xl\)\)/);
|
||||
});
|
||||
|
||||
it("does not redefine the global modal-lg width inside the tablet block", () => {
|
||||
expect(tabletCss).not.toMatch(/\.modal-lg\s*\{/);
|
||||
});
|
||||
|
||||
it("allows the Activity Log header to wrap on tablet", () => {
|
||||
expect(tabletCss).toMatch(/\.activity-log-header\s*\{[^}]*flex-wrap:\s*wrap/);
|
||||
});
|
||||
|
||||
it("moves actions to a reachable wrapping row on tablet", () => {
|
||||
expect(tabletCss).toMatch(/\.activity-log-actions\s*\{[^}]*flex:\s*1\s+1\s+100%/);
|
||||
expect(tabletCss).toMatch(/\.activity-log-actions\s*\{[^}]*flex-wrap:\s*wrap/);
|
||||
});
|
||||
|
||||
it("keeps the close button pinned to the top-right row on tablet", () => {
|
||||
expect(tabletCss).toMatch(/\.activity-log-header\s+\.modal-close\s*\{[^}]*order:\s*\d/);
|
||||
expect(tabletCss).toMatch(/\.activity-log-header\s+\.modal-close\s*\{[^}]*margin-left:\s*auto/);
|
||||
});
|
||||
|
||||
it("keeps filters and refresh/clear controls reachable when optional controls render", () => {
|
||||
expect(tabletCss).toMatch(/\.activity-log-filter,\s*\n\s*\.activity-log-filter--project\s*\{[^}]*flex:\s*1\s+1\s+0/);
|
||||
expect(tabletCss).toMatch(/\.activity-log-refresh,\s*\n\s*\.activity-log-clear\s*\{[^}]*flex-shrink:\s*0/);
|
||||
});
|
||||
});
|
||||
@@ -1595,6 +1595,55 @@
|
||||
border-color: var(--ws-error-dark);
|
||||
}
|
||||
|
||||
/* ── Activity Log — Tablet (769px–1024px) ────────────────────────── */
|
||||
|
||||
@media (min-width: 769px) and (max-width: 1024px) {
|
||||
/* Widen only the Activity Log modal; keep the global .modal-lg width unchanged. */
|
||||
.activity-log-modal {
|
||||
width: calc(100vw - var(--space-2xl));
|
||||
max-width: calc(100vw - var(--space-2xl));
|
||||
}
|
||||
|
||||
/* Header: title on left, close on right of top row, actions wrap below. */
|
||||
.activity-log-header {
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
|
||||
.activity-log-title {
|
||||
flex: 1 1 auto;
|
||||
order: 0;
|
||||
}
|
||||
|
||||
.activity-log-actions {
|
||||
flex: 1 1 100%;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-sm);
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.activity-log-header .modal-close {
|
||||
order: 1;
|
||||
margin-left: auto;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.activity-log-filter,
|
||||
.activity-log-filter--project {
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.activity-log-filter-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.activity-log-refresh,
|
||||
.activity-log-clear {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Activity Log — Mobile (≤ 768px) ─────────────────────────────── */
|
||||
|
||||
@media (max-width: 768px) {
|
||||
|
||||
Reference in New Issue
Block a user