From dfa4a9c849da3cd76ceeac4b95043f3d86e534ba Mon Sep 17 00:00:00 2001 From: Pouzor Date: Sun, 3 May 2026 16:03:30 +0200 Subject: [PATCH] fix(sidebar): unfreeze view after scan triggers history panel forceView prop was overriding local activeView on every render. Once parent set sidebarForceView='history' after starting a scan, it never cleared, so clicks on other nav items were ignored. Treat forceView as a one-shot trigger via the previous-prop pattern; user clicks afterward control view. --- frontend/src/components/panels/Sidebar.tsx | 16 +++++++++++----- .../components/panels/__tests__/Sidebar.test.tsx | 12 ++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/panels/Sidebar.tsx b/frontend/src/components/panels/Sidebar.tsx index 62b94ee..4325938 100644 --- a/frontend/src/components/panels/Sidebar.tsx +++ b/frontend/src/components/panels/Sidebar.tsx @@ -43,13 +43,19 @@ interface SidebarProps { } export function Sidebar({ onAddNode, onAddGroupRect, onScan, onSave, onNodeApproved, forceView, highlightPendingId }: SidebarProps) { - const [_collapsed, setCollapsed] = useState(false) - const [_activeView, setActiveView] = useState('canvas') + const [collapsed, setCollapsed] = useState(false) + const [activeView, setActiveView] = useState(forceView ?? 'canvas') + const [prevForceView, setPrevForceView] = useState(forceView) const logout = useAuthStore((s) => s.logout) - // When forceView is set, override local state without useEffect - const collapsed = forceView ? false : _collapsed - const activeView = forceView ?? _activeView + // forceView acts as a one-shot trigger from parent; user clicks afterwards still control view. + if (forceView !== prevForceView) { + setPrevForceView(forceView) + if (forceView) { + setActiveView(forceView) + setCollapsed(false) + } + } const { nodes, hasUnsavedChanges, hideIp, toggleHideIp } = useCanvasStore() diff --git a/frontend/src/components/panels/__tests__/Sidebar.test.tsx b/frontend/src/components/panels/__tests__/Sidebar.test.tsx index 4853225..5488f1a 100644 --- a/frontend/src/components/panels/__tests__/Sidebar.test.tsx +++ b/frontend/src/components/panels/__tests__/Sidebar.test.tsx @@ -267,6 +267,18 @@ describe('Sidebar', () => { await waitFor(() => expect(screen.getByText('No scans yet')).toBeInTheDocument()) }) + // Regression: forceView used to override local state on every render, freezing + // the sidebar on whichever view the parent forced (e.g. 'history' after a scan). + it('allows switching views after forceView is set by parent', async () => { + const { rerender } = render() + await waitFor(() => expect(screen.getByText('No scans yet')).toBeInTheDocument()) + // Parent keeps forceView as 'history'; user clicks another nav item. + rerender() + fireEvent.click(screen.getByText('Pending Devices')) + await waitFor(() => expect(screen.getByText('No pending devices')).toBeInTheDocument()) + expect(screen.queryByText('No scans yet')).not.toBeInTheDocument() + }) + it('toggles Settings panel on Settings click', async () => { render() fireEvent.click(screen.getByText('Settings'))