diff --git a/packages/dashboard/app/components/InsightsView.css b/packages/dashboard/app/components/InsightsView.css index ffefb864d6..9fa6b711d2 100644 --- a/packages/dashboard/app/components/InsightsView.css +++ b/packages/dashboard/app/components/InsightsView.css @@ -613,3 +613,70 @@ max-width: 200px; } } + +/* +FNXC:InsightsView 2026-06-20-00:34: +Insights tablets at 769px–1024px were inheriting the desktop fixed category sidebar, which squeezed the detail pane inside the .project-content flex chain. Stack the category rail above the detail pane and keep both panes shrinkable so long insight content cannot force horizontal cut-off. +*/ +@media (min-width: 769px) and (max-width: 1024px) { + .insights-view { + flex: 1; + inline-size: 100%; + min-inline-size: 0; + min-block-size: 0; + overflow: hidden; + } + + .insights-view-header { + min-inline-size: 0; + } + + .insights-view-title { + flex: 1 1 auto; + min-inline-size: 0; + } + + .insights-view-actions { + flex-shrink: 0; + } + + .insights-body { + flex-direction: column; + inline-size: 100%; + min-width: 0; + min-inline-size: 0; + min-block-size: 0; + overflow: hidden; + } + + .insights-sidebar { + width: 100%; + min-width: 0; + min-inline-size: 0; + border-right: none; + border-bottom: var(--btn-border-width) solid var(--border); + overflow-x: auto; + overflow-y: hidden; + } + + .insights-category-list { + flex-direction: row; + flex-wrap: nowrap; + } + + .insights-category-list > li { + flex-shrink: 0; + } + + .insights-category-item { + white-space: nowrap; + } + + .insights-detail { + flex: 1 1 0; + min-width: 0; + min-inline-size: 0; + min-block-size: 0; + overflow-y: auto; + } +} diff --git a/packages/dashboard/app/components/ResearchView.css b/packages/dashboard/app/components/ResearchView.css index 83f4f0414b..ef719ebde6 100644 --- a/packages/dashboard/app/components/ResearchView.css +++ b/packages/dashboard/app/components/ResearchView.css @@ -293,3 +293,50 @@ min-height: calc(var(--space-lg) * 2 + var(--space-sm)); } } + +/* +FNXC:ResearchView 2026-06-20-00:33: +Research tablets at 769px–1024px were inheriting the desktop two-column grid, leaving the reader cramped inside the .project-content flex chain. Reflow to one full-width column while preserving internal history and reader scroll containers so the document itself does not overflow horizontally. +*/ +@media (min-width: 769px) and (max-width: 1024px) { + .research-view { + flex: 1; + inline-size: 100%; + min-inline-size: 0; + min-block-size: 0; + overflow: hidden; + } + + .research-view__layout { + display: flex; + flex-direction: column; + grid-template-columns: minmax(0, 1fr); + inline-size: 100%; + min-width: 0; + min-inline-size: 0; + min-block-size: 0; + overflow: hidden; + } + + .research-view__sidebar, + .research-view__reader, + .research-view__sidebar-content, + .research-view__reader-content { + min-width: 0; + min-inline-size: 0; + min-block-size: 0; + } + + .research-view__sidebar { + flex: 0 1 auto; + } + + .research-view__reader { + flex: 1 1 0; + overflow: auto; + } + + .research-view__history { + overflow: auto; + } +} diff --git a/packages/dashboard/app/components/__tests__/InsightsView.test.tsx b/packages/dashboard/app/components/__tests__/InsightsView.test.tsx index d405bf94f5..1c6b3dbfaa 100644 --- a/packages/dashboard/app/components/__tests__/InsightsView.test.tsx +++ b/packages/dashboard/app/components/__tests__/InsightsView.test.tsx @@ -4,6 +4,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { render, screen, fireEvent, waitFor, act } from "@testing-library/react"; +import { loadAllAppCss, loadAllAppCssBaseOnly } from "../../test/cssFixture"; import { InsightsView } from "../InsightsView"; // Mock the useInsights hook @@ -1351,4 +1352,23 @@ describe("InsightsView", () => { expect(dismiss2Button).toBeDisabled(); // All actions disabled when any is in-flight }); }); + + describe("responsive CSS contracts", () => { + it("FN-6764: adds a tablet full-width reflow without regressing desktop or mobile tiers", () => { + const baseCss = loadAllAppCssBaseOnly(); + const css = loadAllAppCss(); + + expect(baseCss).toMatch(/\.insights-body\s*\{[^}]*display:\s*flex;[^}]*overflow:\s*hidden;[^}]*\}/); + expect(baseCss).toMatch(/\.insights-sidebar\s*\{[^}]*width:\s*calc\(var\(--space-2xl\)\s*\*\s*7\s*\+\s*var\(--space-lg\)\);[^}]*flex-shrink:\s*0;[^}]*overflow-y:\s*auto;[^}]*\}/); + expect(baseCss).toMatch(/\.insights-detail\s*\{[^}]*flex:\s*1;[^}]*min-width:\s*0;[^}]*overflow-y:\s*auto;[^}]*\}/); + + expect(css).toMatch(/@media[^{]*\(max-width:\s*768px\)[^{]*\{[\s\S]*?\.insights-body\s*\{[^}]*flex-direction:\s*column;[^}]*\}/); + expect(css).toMatch(/@media[^{]*\(max-width:\s*768px\)[^{]*\{[\s\S]*?\.insights-sidebar\s*\{[^}]*width:\s*100%;[^}]*border-right:\s*none;[^}]*overflow-x:\s*auto;[^}]*overflow-y:\s*hidden;[^}]*\}/); + + expect(css).toMatch(/@media[^{]*\(min-width:\s*769px\)\s*and\s*\(max-width:\s*1024px\)[^{]*\{[\s\S]*?\.insights-view\s*\{[^}]*inline-size:\s*100%;[^}]*min-inline-size:\s*0;[^}]*overflow:\s*hidden;[^}]*\}/); + expect(css).toMatch(/@media[^{]*\(min-width:\s*769px\)\s*and\s*\(max-width:\s*1024px\)[^{]*\{[\s\S]*?\.insights-body\s*\{[^}]*flex-direction:\s*column;[^}]*inline-size:\s*100%;[^}]*min-width:\s*0;[^}]*min-inline-size:\s*0;[^}]*overflow:\s*hidden;[^}]*\}/); + expect(css).toMatch(/@media[^{]*\(min-width:\s*769px\)\s*and\s*\(max-width:\s*1024px\)[^{]*\{[\s\S]*?\.insights-sidebar\s*\{[^}]*width:\s*100%;[^}]*min-width:\s*0;[^}]*min-inline-size:\s*0;[^}]*border-right:\s*none;[^}]*border-bottom:\s*var\(--btn-border-width\)\s+solid\s+var\(--border\);[^}]*overflow-x:\s*auto;[^}]*overflow-y:\s*hidden;[^}]*\}/); + expect(css).toMatch(/@media[^{]*\(min-width:\s*769px\)\s*and\s*\(max-width:\s*1024px\)[^{]*\{[\s\S]*?\.insights-detail\s*\{[^}]*flex:\s*1\s+1\s+0;[^}]*min-width:\s*0;[^}]*min-inline-size:\s*0;[^}]*overflow-y:\s*auto;[^}]*\}/); + }); + }); }); diff --git a/packages/dashboard/app/components/__tests__/ResearchView.test.tsx b/packages/dashboard/app/components/__tests__/ResearchView.test.tsx index 583ff550d0..f78194c2d0 100644 --- a/packages/dashboard/app/components/__tests__/ResearchView.test.tsx +++ b/packages/dashboard/app/components/__tests__/ResearchView.test.tsx @@ -561,4 +561,17 @@ describe("ResearchView", () => { expect(css).toMatch(/@media[^{]*\(max-width:\s*768px\)[^{]*\{[\s\S]*?\.research-view__layout\s*\{[^}]*display:\s*flex;[^}]*flex-direction:\s*column;[^}]*overflow:\s*visible;[^}]*\}/); expect(css).toMatch(/@media[^{]*\(max-width:\s*768px\)[^{]*\{[\s\S]*?\.research-view__history\s*\{[^}]*overflow:\s*visible;[^}]*\}/); }); + + it("FN-6764: adds a tablet full-width reflow without regressing desktop or mobile tiers", () => { + const baseCss = loadAllAppCssBaseOnly(); + const css = loadAllAppCss(); + + expect(baseCss).toMatch(/\.research-view__layout\s*\{[^}]*display:\s*grid;[^}]*grid-template-columns:\s*minmax\(0,\s*1fr\)\s*minmax\(0,\s*2fr\);[^}]*overflow:\s*hidden;[^}]*\}/); + expect(css).toMatch(/@media[^{]*\(max-width:\s*768px\)[^{]*\{[\s\S]*?\.research-view__layout\s*\{[^}]*display:\s*flex;[^}]*flex-direction:\s*column;[^}]*grid-template-columns:\s*minmax\(0,\s*1fr\);[^}]*overflow:\s*visible;[^}]*\}/); + + expect(css).toMatch(/@media[^{]*\(min-width:\s*769px\)\s*and\s*\(max-width:\s*1024px\)[^{]*\{[\s\S]*?\.research-view\s*\{[^}]*inline-size:\s*100%;[^}]*min-inline-size:\s*0;[^}]*overflow:\s*hidden;[^}]*\}/); + expect(css).toMatch(/@media[^{]*\(min-width:\s*769px\)\s*and\s*\(max-width:\s*1024px\)[^{]*\{[\s\S]*?\.research-view__layout\s*\{[^}]*display:\s*flex;[^}]*flex-direction:\s*column;[^}]*grid-template-columns:\s*minmax\(0,\s*1fr\);[^}]*min-width:\s*0;[^}]*min-inline-size:\s*0;[^}]*overflow:\s*hidden;[^}]*\}/); + expect(css).toMatch(/@media[^{]*\(min-width:\s*769px\)\s*and\s*\(max-width:\s*1024px\)[^{]*\{[\s\S]*?\.research-view__reader\s*\{[^}]*flex:\s*1\s+1\s+0;[^}]*overflow:\s*auto;[^}]*\}/); + expect(css).toMatch(/@media[^{]*\(min-width:\s*769px\)\s*and\s*\(max-width:\s*1024px\)[^{]*\{[\s\S]*?\.research-view__history\s*\{[^}]*overflow:\s*auto;[^}]*\}/); + }); });