mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-04 23:20:07 +00:00
This pull request refactors the node selection logic in the Vue nodes event handler composable to simplify the function signature and improve single vs. multi-selection behavior. The main change is the removal of the `wasDragging` parameter from the `handleNodeSelect` function, with selection logic now determined by the current selection state. Related test code is updated to match the new function signature. **Node selection logic improvements:** * Refactored the `handleNodeSelect` function in `useNodeEventHandlersIndividual` to remove the `wasDragging` parameter, making the function signature simpler and relying on selection state to handle single vs. multi-selection. * Updated the selection logic to check if multiple nodes are already selected using `isLGraphNode`, and only perform single selection if not. **Code and test updates:** * Updated all calls to `handleNodeSelect` in the composable to remove the `wasDragging` argument, ensuring consistent usage throughout the codebase. [[1]](diffhunk://#diff-8d3820a1ca9c569bce00671fdd6290af81315ae11b8f3d6f29a5a9d30379d084L125-R123) [[2]](diffhunk://#diff-8d3820a1ca9c569bce00671fdd6290af81315ae11b8f3d6f29a5a9d30379d084L146-R144) [[3]](diffhunk://#diff-8d3820a1ca9c569bce00671fdd6290af81315ae11b8f3d6f29a5a9d30379d084L173-R171) * Updated all related test cases to use the new `handleNodeSelect` signature without the third parameter. [[1]](diffhunk://#diff-89bfc2a05201c6ff7116578efa45f96097594eb346f18446c70aa7125ab1811aL105-R105) [[2]](diffhunk://#diff-89bfc2a05201c6ff7116578efa45f96097594eb346f18446c70aa7125ab1811aL125-R125) [[3]](diffhunk://#diff-89bfc2a05201c6ff7116578efa45f96097594eb346f18446c70aa7125ab1811aL144-R144) [[4]](diffhunk://#diff-89bfc2a05201c6ff7116578efa45f96097594eb346f18446c70aa7125ab1811aL162-R162) [[5]](diffhunk://#diff-89bfc2a05201c6ff7116578efa45f96097594eb346f18446c70aa7125ab1811aL174-R174) [[6]](diffhunk://#diff-89bfc2a05201c6ff7116578efa45f96097594eb346f18446c70aa7125ab1811aL187-R187) **Utility import:** * Added an import for `isLGraphNode` from `@/utils/litegraphUtil` to support the updated selection logic.## Summary <!-- One sentence describing what changed and why. --> ## Screenshots (if applicable) https://github.com/user-attachments/assets/71e856d3-afc2-497d-826e-5b485066e7fe --------- Co-authored-by: github-actions <github-actions@github.com>
193 lines
5.3 KiB
TypeScript
193 lines
5.3 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import { computed, shallowRef } from 'vue'
|
|
|
|
import {
|
|
type GraphNodeManager,
|
|
type VueNodeData,
|
|
useGraphNodeManager
|
|
} from '@/composables/graph/useGraphNodeManager'
|
|
import { useVueNodeLifecycle } from '@/composables/graph/useVueNodeLifecycle'
|
|
import type {
|
|
LGraph,
|
|
LGraphCanvas,
|
|
LGraphNode
|
|
} from '@/lib/litegraph/src/litegraph'
|
|
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
|
import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations'
|
|
import { useNodeEventHandlers } from '@/renderer/extensions/vueNodes/composables/useNodeEventHandlers'
|
|
|
|
vi.mock('@/renderer/core/canvas/canvasStore', () => {
|
|
const canvas: Partial<LGraphCanvas> = {
|
|
select: vi.fn(),
|
|
deselect: vi.fn(),
|
|
deselectAll: vi.fn()
|
|
}
|
|
const updateSelectedItems = vi.fn()
|
|
return {
|
|
useCanvasStore: vi.fn(() => ({
|
|
canvas: canvas as LGraphCanvas,
|
|
updateSelectedItems,
|
|
selectedItems: []
|
|
}))
|
|
}
|
|
})
|
|
|
|
vi.mock('@/renderer/core/canvas/useCanvasInteractions', () => ({
|
|
useCanvasInteractions: vi.fn(() => ({
|
|
shouldHandleNodePointerEvents: computed(() => true) // Default to allowing pointer events
|
|
}))
|
|
}))
|
|
|
|
vi.mock('@/renderer/core/layout/operations/layoutMutations', () => {
|
|
const setSource = vi.fn()
|
|
const bringNodeToFront = vi.fn()
|
|
return {
|
|
useLayoutMutations: vi.fn(() => ({
|
|
setSource,
|
|
bringNodeToFront
|
|
}))
|
|
}
|
|
})
|
|
|
|
vi.mock('@/composables/graph/useGraphNodeManager', () => {
|
|
const mockNode = {
|
|
id: 'node-1',
|
|
selected: false,
|
|
flags: { pinned: false }
|
|
}
|
|
const nodeManager = shallowRef({
|
|
getNode: vi.fn(() => mockNode as Partial<LGraphNode> as LGraphNode)
|
|
} as Partial<GraphNodeManager> as GraphNodeManager)
|
|
return {
|
|
useGraphNodeManager: vi.fn(() => nodeManager)
|
|
}
|
|
})
|
|
|
|
vi.mock('@/composables/graph/useVueNodeLifecycle', () => {
|
|
const nodeManager = useGraphNodeManager(undefined as unknown as LGraph)
|
|
return {
|
|
useVueNodeLifecycle: vi.fn(() => ({
|
|
nodeManager
|
|
}))
|
|
}
|
|
})
|
|
|
|
describe('useNodeEventHandlers', () => {
|
|
const { nodeManager: mockNodeManager } = useVueNodeLifecycle()
|
|
|
|
const mockNode = mockNodeManager.value!.getNode('fake_id')
|
|
const mockLayoutMutations = useLayoutMutations()
|
|
|
|
const testNodeData: VueNodeData = {
|
|
id: 'node-1',
|
|
title: 'Test Node',
|
|
type: 'test',
|
|
mode: 0,
|
|
selected: false,
|
|
executing: false
|
|
}
|
|
|
|
beforeEach(async () => {
|
|
vi.restoreAllMocks()
|
|
})
|
|
|
|
describe('handleNodeSelect', () => {
|
|
it('should select single node on regular click', () => {
|
|
const { handleNodeSelect } = useNodeEventHandlers()
|
|
const { canvas, updateSelectedItems } = useCanvasStore()
|
|
|
|
const event = new PointerEvent('pointerdown', {
|
|
bubbles: true,
|
|
ctrlKey: false,
|
|
metaKey: false
|
|
})
|
|
|
|
handleNodeSelect(event, testNodeData)
|
|
|
|
expect(canvas?.deselectAll).toHaveBeenCalledOnce()
|
|
expect(canvas?.select).toHaveBeenCalledWith(mockNode)
|
|
expect(updateSelectedItems).toHaveBeenCalledOnce()
|
|
})
|
|
|
|
it('should toggle selection on ctrl+click', () => {
|
|
const { handleNodeSelect } = useNodeEventHandlers()
|
|
const { canvas } = useCanvasStore()
|
|
|
|
// Test selecting unselected node with ctrl
|
|
mockNode!.selected = false
|
|
|
|
const ctrlClickEvent = new PointerEvent('pointerdown', {
|
|
bubbles: true,
|
|
ctrlKey: true,
|
|
metaKey: false
|
|
})
|
|
|
|
handleNodeSelect(ctrlClickEvent, testNodeData)
|
|
|
|
expect(canvas?.deselectAll).not.toHaveBeenCalled()
|
|
expect(canvas?.select).toHaveBeenCalledWith(mockNode)
|
|
})
|
|
|
|
it('should deselect on ctrl+click of selected node', () => {
|
|
const { handleNodeSelect } = useNodeEventHandlers()
|
|
const { canvas } = useCanvasStore()
|
|
|
|
// Test deselecting selected node with ctrl
|
|
mockNode!.selected = true
|
|
|
|
const ctrlClickEvent = new PointerEvent('pointerdown', {
|
|
bubbles: true,
|
|
ctrlKey: true,
|
|
metaKey: false
|
|
})
|
|
|
|
handleNodeSelect(ctrlClickEvent, testNodeData)
|
|
|
|
expect(canvas?.deselect).toHaveBeenCalledWith(mockNode)
|
|
expect(canvas?.select).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('should handle meta key (Cmd) on Mac', () => {
|
|
const { handleNodeSelect } = useNodeEventHandlers()
|
|
const { canvas } = useCanvasStore()
|
|
|
|
mockNode!.selected = false
|
|
|
|
const metaClickEvent = new PointerEvent('pointerdown', {
|
|
bubbles: true,
|
|
ctrlKey: false,
|
|
metaKey: true
|
|
})
|
|
|
|
handleNodeSelect(metaClickEvent, testNodeData)
|
|
|
|
expect(canvas?.select).toHaveBeenCalledWith(mockNode)
|
|
expect(canvas?.deselectAll).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('should bring node to front when not pinned', () => {
|
|
const { handleNodeSelect } = useNodeEventHandlers()
|
|
|
|
mockNode!.flags.pinned = false
|
|
|
|
const event = new PointerEvent('pointerdown')
|
|
handleNodeSelect(event, testNodeData)
|
|
|
|
expect(mockLayoutMutations.bringNodeToFront).toHaveBeenCalledWith(
|
|
'node-1'
|
|
)
|
|
})
|
|
|
|
it('should not bring pinned node to front', () => {
|
|
const { handleNodeSelect } = useNodeEventHandlers()
|
|
|
|
mockNode!.flags.pinned = true
|
|
|
|
const event = new PointerEvent('pointerdown')
|
|
handleNodeSelect(event, testNodeData)
|
|
|
|
expect(mockLayoutMutations.bringNodeToFront).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
})
|