feat(FN-1179): improve dashboard core modal mobile behavior

- Update core modal CSS for mobile breakpoints across task detail, new task, settings, and git manager flows
- Align spacing, sizing, and responsive layout rules to keep modal interactions usable on small screens
- Add core-modals-mobile tests to verify mobile-specific CSS behavior and regression coverage
- Document the modal mobile adaptations in the dashboard README for maintainability
This commit is contained in:
gsxdsm
2026-04-08 12:59:39 -07:00
parent bcbe4ad286
commit ac4d8a18bc
3 changed files with 238 additions and 1 deletions

View File

@@ -0,0 +1,98 @@
import fs from "node:fs";
import path from "node:path";
import { describe, expect, it } from "vitest";
const stylesPath = path.resolve(__dirname, "../../styles.css");
function getMainMobileBlock(css: string): string {
const mobileSectionStart = css.indexOf("/* === Mobile Responsive Overrides ===");
const tabletSectionStart = css.indexOf("/* === Tablet Responsive Tier", mobileSectionStart);
expect(mobileSectionStart).toBeGreaterThan(-1);
expect(tabletSectionStart).toBeGreaterThan(mobileSectionStart);
const block = css.slice(mobileSectionStart, tabletSectionStart);
expect(block).toContain("@media (max-width: 768px)");
expect(block).toContain(".modal-overlay");
expect(block).toContain(".detail-tabs");
return block;
}
describe("core modals mobile css coverage", () => {
it("TaskDetailModal: modal-actions uses safe-area inset bottom padding", () => {
const css = fs.readFileSync(stylesPath, "utf-8");
const mobileBlock = getMainMobileBlock(css);
expect(mobileBlock).toContain(".modal-actions {");
expect(mobileBlock).toContain("env(safe-area-inset-bottom, 0px)");
});
it("TaskDetailModal: detail tabs are horizontally scrollable and tabs do not shrink", () => {
const css = fs.readFileSync(stylesPath, "utf-8");
const mobileBlock = getMainMobileBlock(css);
expect(mobileBlock).toContain(".detail-tabs {");
expect(mobileBlock).toContain("overflow-x: auto;");
expect(mobileBlock).toContain(".detail-tab {");
expect(mobileBlock).toContain("flex-shrink: 0;");
});
it("TaskDetailModal: refine modal goes full-screen on mobile", () => {
const css = fs.readFileSync(stylesPath, "utf-8");
const mobileBlock = getMainMobileBlock(css);
expect(mobileBlock).toContain(".detail-refine-modal {");
expect(mobileBlock).toContain("width: 100%;");
expect(mobileBlock).toContain("max-width: 100%;");
});
it("NewTaskModal: modal body unsets desktop max-height for mobile", () => {
const css = fs.readFileSync(stylesPath, "utf-8");
const mobileBlock = getMainMobileBlock(css);
expect(mobileBlock).toContain(".new-task-modal .modal-body {");
expect(mobileBlock).toContain("max-height: unset;");
expect(mobileBlock).toContain("overflow-y: auto;");
});
it("TaskForm: model selection rows stack vertically on mobile", () => {
const css = fs.readFileSync(stylesPath, "utf-8");
const mobileBlock = getMainMobileBlock(css);
expect(mobileBlock).toContain(".model-select-row {");
expect(mobileBlock).toContain("flex-direction: column;");
expect(mobileBlock).toContain(".model-select-label {");
expect(mobileBlock).toContain("width: auto;");
expect(mobileBlock).toContain("text-align: left;");
});
it("SettingsModal: layout stacks and sidebar becomes horizontal scroll row", () => {
const css = fs.readFileSync(stylesPath, "utf-8");
const mobileBlock = getMainMobileBlock(css);
expect(mobileBlock).toContain(".settings-layout {");
expect(mobileBlock).toContain("flex-direction: column;");
expect(mobileBlock).toContain(".settings-sidebar {");
expect(mobileBlock).toContain("flex-direction: row;");
expect(mobileBlock).toContain("overflow-x: auto;");
});
it("GitManagerModal: 768px mobile block includes stacked layout rules", () => {
const css = fs.readFileSync(stylesPath, "utf-8");
const mobileBlock = getMainMobileBlock(css);
expect(mobileBlock).toContain(".gm-layout {");
expect(mobileBlock).toContain("flex-direction: column;");
expect(mobileBlock).toContain(".gm-sidebar {");
expect(mobileBlock).toContain("flex-direction: row;");
});
it("GitManagerModal: nav items keep 44px touch target on mobile", () => {
const css = fs.readFileSync(stylesPath, "utf-8");
const mobileBlock = getMainMobileBlock(css);
expect(mobileBlock).toContain(".gm-nav-item {");
expect(mobileBlock).toContain("min-height: 44px;");
});
});