diff --git a/backend/requirements.txt b/backend/requirements.txt index 792a5bc..3bd01ad 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -9,7 +9,7 @@ pydantic-settings==2.5.2 python-jose[cryptography]==3.5.0 passlib[bcrypt]==1.7.4 bcrypt==4.0.1 -python-multipart==0.0.22 +python-multipart==0.0.26 apscheduler==3.10.4 python-nmap==0.7.1 pyyaml==6.0.2 @@ -21,6 +21,6 @@ zeroconf==0.131.0 # Dev ruff==0.6.9 mypy==1.11.2 -pytest==8.3.3 -pytest-asyncio==0.24.0 +pytest==9.0.3 +pytest-asyncio==1.3.0 pytest-cov==5.0.0 diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index a36b9d0..fdd1b1c 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -343,6 +343,10 @@ export default function App() { setEditEdgeId(edge.id) }, []) + const handleNodeDoubleClick = useCallback((node: Node) => { + handleEditNode(node.id) + }, [handleEditNode]) + const handleEdgeUpdate = useCallback((data: EdgeData) => { if (!editEdgeId) return snapshotHistory() @@ -400,6 +404,7 @@ export default function App() { { setHighlightPendingId(undefined) diff --git a/frontend/src/components/canvas/CanvasContainer.tsx b/frontend/src/components/canvas/CanvasContainer.tsx index 3847dc6..cee03e8 100644 --- a/frontend/src/components/canvas/CanvasContainer.tsx +++ b/frontend/src/components/canvas/CanvasContainer.tsx @@ -25,11 +25,12 @@ import type { NodeData, EdgeData } from '@/types' interface CanvasContainerProps { onConnect?: (connection: Connection) => void onEdgeDoubleClick?: (edge: Edge) => void + onNodeDoubleClick?: (node: Node) => void onNodeDragStart?: () => void onOpenPending?: (deviceId: string) => void } -export function CanvasContainer({ onConnect: onConnectProp, onEdgeDoubleClick, onNodeDragStart, onOpenPending }: CanvasContainerProps) { +export function CanvasContainer({ onConnect: onConnectProp, onEdgeDoubleClick, onNodeDoubleClick, onNodeDragStart, onOpenPending }: CanvasContainerProps) { const [lassoMode, setLassoMode] = useState(true) const { nodes, edges, @@ -68,6 +69,10 @@ export function CanvasContainer({ onConnect: onConnectProp, onEdgeDoubleClick, o onEdgeDoubleClick?.(edge) }, [onEdgeDoubleClick]) + const handleNodeDoubleClick = useCallback((_: React.MouseEvent, node: Node) => { + onNodeDoubleClick?.(node) + }, [onNodeDoubleClick]) + return (
{ }).not.toThrow() }) + // ── Node double-click ───────────────────────────────────────────────────── + + it('calls onNodeDoubleClick prop when a node is double-clicked', () => { + const onNodeDoubleClick = vi.fn() + const node = makeNode('n1') + render() + ;(rfProps.onNodeDoubleClick as (...args: unknown[]) => unknown)({} as MouseEvent, node) + expect(onNodeDoubleClick).toHaveBeenCalledWith(node) + }) + + it('does not throw when onNodeDoubleClick is not provided', () => { + const node = makeNode('n1') + render() + expect(() => { + ;(rfProps.onNodeDoubleClick as (...args: unknown[]) => unknown)({} as MouseEvent, node) + }).not.toThrow() + }) + // ── Connection validation ───────────────────────────────────────────────── it('isValidConnection returns false for self-connections', () => {