Skip to content

Commit 9220596

Browse files
committed
fix: prevent graph from going blank when clicking non-visible nodes
- Added safety check: only apply selection highlighting if selected node is visible - Fixed handlePanelFileClick to auto-enable 'show all' when clicking non-visible dependents - Applied fix to both clustered and non-clustered graph modes - Prevents edge dimming when selected node isn't in visible set
1 parent 2b54b9b commit 9220596

1 file changed

Lines changed: 30 additions & 8 deletions

File tree

  • frontend/src/components/DependencyGraph

frontend/src/components/DependencyGraph/index.tsx

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,11 @@ function DependencyGraphInner({ repoId, apiUrl, apiKey }: DependencyGraphProps)
230230
let flowEdges: Edge[] = []
231231

232232
if (clusterByDir && clusteredData) {
233+
// Safety: only apply selection highlighting if the selected node is actually visible
234+
const effectiveSelectedIdCluster = selectedNodeId &&
235+
(clusteredData.visibleFiles.has(selectedNodeId) || selectedNodeId.startsWith('dir:'))
236+
? selectedNodeId : null
237+
233238
// Add directory nodes
234239
clusteredData.dirNodes.forEach(dir => {
235240
flowNodes.push({
@@ -248,10 +253,10 @@ function DependencyGraphInner({ repoId, apiUrl, apiKey }: DependencyGraphProps)
248253
const metrics = impact.getFileMetrics(node.id)
249254

250255
let state: GraphNodeData['state'] = 'default'
251-
if (selectedNodeId === node.id) state = 'selected'
256+
if (effectiveSelectedIdCluster === node.id) state = 'selected'
252257
else if (selectedImpact?.directDependents.includes(node.id)) state = 'direct'
253258
else if (selectedImpact?.transitiveDependents.includes(node.id)) state = 'transitive'
254-
else if (selectedNodeId && !selectedNodeId.startsWith('dir:')) state = 'dimmed'
259+
else if (effectiveSelectedIdCluster && !effectiveSelectedIdCluster.startsWith('dir:')) state = 'dimmed'
255260

256261
// Hover highlighting in clustered mode
257262
if (hoveredFileId === node.id && state === 'dimmed') state = 'direct'
@@ -284,15 +289,18 @@ function DependencyGraphInner({ repoId, apiUrl, apiKey }: DependencyGraphProps)
284289
})
285290
} else {
286291
// Non-clustered mode (original logic)
292+
// Safety: only apply selection highlighting if the selected node is actually visible
293+
const effectiveSelectedId = selectedNodeId && visibleNodeIds.has(selectedNodeId) ? selectedNodeId : null
294+
287295
flowNodes = rawGraphData.nodes
288296
.filter((node: any) => visibleNodeIds.has(node.id))
289297
.map((node: any) => {
290298
const fileName = node.label || node.id.split('/').pop()
291299
const metrics = impact.getFileMetrics(node.id)
292300

293301
let state: GraphNodeData['state'] = 'default'
294-
if (selectedNodeId) {
295-
if (node.id === selectedNodeId) state = 'selected'
302+
if (effectiveSelectedId) {
303+
if (node.id === effectiveSelectedId) state = 'selected'
296304
else if (selectedImpact?.directDependents.includes(node.id)) state = 'direct'
297305
else if (selectedImpact?.transitiveDependents.includes(node.id)) state = 'transitive'
298306
else state = 'dimmed'
@@ -321,9 +329,9 @@ function DependencyGraphInner({ repoId, apiUrl, apiKey }: DependencyGraphProps)
321329
.filter((edge: any) => visibleNodeIds.has(edge.source) && visibleNodeIds.has(edge.target))
322330
.map((edge: any) => {
323331
let edgeState: 'default' | 'highlighted' | 'dimmed' | 'incoming' | 'outgoing' = 'default'
324-
if (selectedNodeId) {
325-
if (edge.source === selectedNodeId) edgeState = 'outgoing'
326-
else if (edge.target === selectedNodeId) edgeState = 'incoming'
332+
if (effectiveSelectedId) {
333+
if (edge.source === effectiveSelectedId) edgeState = 'outgoing'
334+
else if (edge.target === effectiveSelectedId) edgeState = 'incoming'
327335
else edgeState = 'dimmed'
328336
}
329337

@@ -369,12 +377,26 @@ function DependencyGraphInner({ repoId, apiUrl, apiKey }: DependencyGraphProps)
369377
}, [])
370378

371379
const handlePanelFileClick = useCallback((fileId: string) => {
380+
// Only select if the file is currently visible in the graph
381+
// Otherwise clicking on a non-visible dependent breaks the view
382+
if (!visibleNodeIds.has(fileId)) {
383+
// If not visible, enable "show all" to make it visible first
384+
if (!showAll) {
385+
setShowAll(true)
386+
// Small delay to let the nodes render, then select
387+
setTimeout(() => {
388+
setSelectedNodeId(fileId)
389+
}, 100)
390+
return
391+
}
392+
}
393+
372394
setSelectedNodeId(fileId)
373395
const node = nodes.find(n => n.id === fileId)
374396
if (node) {
375397
fitView({ nodes: [node], padding: 0.5, duration: 300 })
376398
}
377-
}, [nodes, fitView])
399+
}, [nodes, fitView, visibleNodeIds, showAll])
378400

379401
const handleResetView = useCallback(() => {
380402
setSelectedNodeId(null)

0 commit comments

Comments
 (0)