From a802563d35e292c8fe23e837766fe5bca7fa645e Mon Sep 17 00:00:00 2001 From: Matthew Valancy Date: Sun, 14 Jun 2026 12:17:09 -0700 Subject: [PATCH] Expandable project-explorer graph tree (PR-D) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The graph selector showed only top-level graphs; sub-graphs (the hierarchy) were invisible there — reachable only by drilling into the canvas. Now a parent graph expands in place to reveal its sub-graphs. - GraphSelector.tsx: a graph row with children (graph.children, from the context's existing buildHierarchy tree) gets an expand chevron; expanding it lists the sub-graphs (indented, selectable, with node/edge counts). Per-graph expand state; reuses the existing ChevronRight rotate pattern. The 'system' folder is expanded by default so the seeded "System Overview" is discoverable. Verified: tests/diagnostics/explorer-tree.spec.ts — expand "System Overview" → "Compute Core" sub-graph row appears → selecting it switches currentGraphId to subgraph-compute-shared. Web typecheck clean. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/web/src/components/GraphSelector.tsx | 47 ++++++++++++++++++- tests/diagnostics/explorer-tree.spec.ts | 39 +++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 tests/diagnostics/explorer-tree.spec.ts diff --git a/packages/web/src/components/GraphSelector.tsx b/packages/web/src/components/GraphSelector.tsx index 8bad8734..978f485a 100644 --- a/packages/web/src/components/GraphSelector.tsx +++ b/packages/web/src/components/GraphSelector.tsx @@ -16,7 +16,16 @@ export function GraphSelector({ onCreateGraph, onEditGraph, onDeleteGraph }: Gra const { currentGraph, graphHierarchy, selectGraph } = useGraph(); const { currentTeam, currentUser } = useAuth(); const [isOpen, setIsOpen] = useState(false); - const [expandedFolders, setExpandedFolders] = useState>(new Set(['team', 'personal'])); + const [expandedFolders, setExpandedFolders] = useState>(new Set(['team', 'personal', 'system'])); + // Which parent graphs are expanded to show their sub-graphs (the hierarchy). + const [expandedGraphs, setExpandedGraphs] = useState>(new Set()); + const toggleGraphExpand = (id: string) => { + setExpandedGraphs((prev) => { + const next = new Set(prev); + next.has(id) ? next.delete(id) : next.add(id); + return next; + }); + }; const [buttonPosition, setButtonPosition] = useState<{ top: number; left: number; width: number } | null>(null); const [hoveredTooltip, setHoveredTooltip] = useState<{ message: string; x: number; y: number } | null>(null); const dropdownRef = useRef(null); @@ -310,7 +319,17 @@ export function GraphSelector({ onCreateGraph, onEditGraph, onDeleteGraph }: Gra {isExpanded && (
{graphs.map((graph) => ( -
+
+
+ {graph.children && graph.children.length > 0 && ( + + )}
+ {graph.children && graph.children.length > 0 && expandedGraphs.has(graph.id) && ( +
+ {graph.children.map((child: any) => ( + + ))} +
+ )} +
))}
)} diff --git a/tests/diagnostics/explorer-tree.spec.ts b/tests/diagnostics/explorer-tree.spec.ts new file mode 100644 index 00000000..5760243b --- /dev/null +++ b/tests/diagnostics/explorer-tree.spec.ts @@ -0,0 +1,39 @@ +import { test, expect } from '@playwright/test'; +import { login, TEST_USERS } from '../helpers/auth'; + +/** + * Project-explorer hierarchy: the graph selector expands a parent graph + * ("System Overview") to reveal its sub-graphs, and selecting a sub-graph + * switches the current graph. Needs the hierarchy demo seeded. + */ +test.describe('explorer tree diagnostic @geometry', () => { + test.describe.configure({ timeout: 120_000 }); + + test('expand System Overview → sub-graphs appear → select one switches graph', async ({ page }) => { + await page.setViewportSize({ width: 1440, height: 900 }); + await login(page, TEST_USERS.ADMIN); + await page.waitForTimeout(2500); + + // Open the graph selector. + await page.locator('[data-testid="graph-selector"]').click(); + await page.waitForTimeout(500); + + // Expand the "System Overview" parent (its chevron has a "sub-graphs" title). + const expander = page.locator('button[title$="sub-graphs"]').first(); + await expander.waitFor({ timeout: 10000 }); + await expander.click(); + await page.waitForTimeout(500); + + // A sub-graph row should now be visible. + const child = page.getByRole('button', { name: /Compute Core/ }); + await expect(child.first(), 'sub-graph row appears under the overview').toBeVisible(); + + // Selecting it switches the current graph. + await child.first().click(); + await page.waitForTimeout(3000); + const current = await page.evaluate(() => localStorage.getItem('currentGraphId')); + // eslint-disable-next-line no-console + console.log('[explorer-tree] currentGraphId after select = ' + current); + expect(current, 'selecting a sub-graph switches to it').toBe('subgraph-compute-shared'); + }); +});