diff --git a/src/scripts/domWidget.ts b/src/scripts/domWidget.ts index be7da87f6..a24b2619d 100644 --- a/src/scripts/domWidget.ts +++ b/src/scripts/domWidget.ts @@ -186,6 +186,9 @@ abstract class BaseDOMWidgetImpl options: this.options }) cloned.value = this.value + // Preserve the Y position from the original widget to maintain proper positioning + // when widgets are promoted through subgraph nesting + cloned.y = this.y return cloned } } @@ -217,6 +220,9 @@ export class DOMWidgetImpl options: this.options }) cloned.value = this.value + // Preserve the Y position from the original widget to maintain proper positioning + // when widgets are promoted through subgraph nesting + cloned.y = this.y return cloned } diff --git a/tests-ui/tests/widgets/domWidget.test.ts b/tests-ui/tests/widgets/domWidget.test.ts new file mode 100644 index 000000000..8c5695f33 --- /dev/null +++ b/tests-ui/tests/widgets/domWidget.test.ts @@ -0,0 +1,82 @@ +import { LGraphNode } from '@comfyorg/litegraph' +import { describe, expect, test, vi } from 'vitest' + +import { ComponentWidgetImpl, DOMWidgetImpl } from '@/scripts/domWidget' + +// Mock dependencies +vi.mock('@/stores/domWidgetStore', () => ({ + useDomWidgetStore: () => ({ + unregisterWidget: vi.fn() + }) +})) + +vi.mock('@/utils/formatUtil', () => ({ + generateUUID: () => 'test-uuid' +})) + +describe('DOMWidget Y Position Preservation', () => { + test('BaseDOMWidgetImpl createCopyForNode preserves Y position', () => { + const mockNode = new LGraphNode('test-node') + const originalWidget = new ComponentWidgetImpl({ + node: mockNode, + name: 'test-widget', + component: { template: '
' }, + inputSpec: { name: 'test', type: 'string' }, + options: {} + }) + + // Set a specific Y position + originalWidget.y = 66 + + const newNode = new LGraphNode('new-node') + const clonedWidget = originalWidget.createCopyForNode(newNode) + + // Verify Y position is preserved + expect(clonedWidget.y).toBe(66) + expect(clonedWidget.node).toBe(newNode) + expect(clonedWidget.name).toBe('test-widget') + }) + + test('DOMWidgetImpl createCopyForNode preserves Y position', () => { + const mockNode = new LGraphNode('test-node') + const mockElement = document.createElement('div') + + const originalWidget = new DOMWidgetImpl({ + node: mockNode, + name: 'test-dom-widget', + type: 'test', + element: mockElement, + options: {} + }) + + // Set a specific Y position + originalWidget.y = 42 + + const newNode = new LGraphNode('new-node') + const clonedWidget = originalWidget.createCopyForNode(newNode) + + // Verify Y position is preserved + expect(clonedWidget.y).toBe(42) + expect(clonedWidget.node).toBe(newNode) + expect(clonedWidget.element).toBe(mockElement) + expect(clonedWidget.name).toBe('test-dom-widget') + }) + + test('Y position defaults to 0 when not set', () => { + const mockNode = new LGraphNode('test-node') + const originalWidget = new ComponentWidgetImpl({ + node: mockNode, + name: 'test-widget', + component: { template: '
' }, + inputSpec: { name: 'test', type: 'string' }, + options: {} + }) + + // Don't explicitly set Y (should be 0 by default) + const newNode = new LGraphNode('new-node') + const clonedWidget = originalWidget.createCopyForNode(newNode) + + // Verify Y position is preserved (should be 0) + expect(clonedWidget.y).toBe(0) + }) +})