mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-13 01:06:18 +00:00
fix: App mode - renaming widgets on subgraphs (#10245)
## Summary Fixes renaming of widgets from subgraph nodes in app builder/app mode. ## Changes - **What**: If the widget is from a subgraph node and no parents passed, use the node as the subgraph parent. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10245-fix-App-mode-renaming-widgets-on-subgraphs-3276d73d3650815bb131c840df43cdf2) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -1,8 +1,19 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
|
||||
import type { INodeInputSlot } from '@/lib/litegraph/src/interfaces'
|
||||
import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'
|
||||
import type { InputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||
|
||||
import { getWidgetDefaultValue } from '@/utils/widgetUtil'
|
||||
import { getWidgetDefaultValue, renameWidget } from '@/utils/widgetUtil'
|
||||
|
||||
vi.mock('@/core/graph/subgraph/resolvePromotedWidgetSource', () => ({
|
||||
resolvePromotedWidgetSource: vi.fn()
|
||||
}))
|
||||
|
||||
import { resolvePromotedWidgetSource } from '@/core/graph/subgraph/resolvePromotedWidgetSource'
|
||||
|
||||
const mockedResolve = vi.mocked(resolvePromotedWidgetSource)
|
||||
|
||||
describe('getWidgetDefaultValue', () => {
|
||||
it('returns undefined for undefined spec', () => {
|
||||
@@ -37,3 +48,98 @@ describe('getWidgetDefaultValue', () => {
|
||||
expect(getWidgetDefaultValue(spec)).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
function makeWidget(overrides: Record<string, unknown> = {}): IBaseWidget {
|
||||
return {
|
||||
name: 'myWidget',
|
||||
type: 'number',
|
||||
value: 0,
|
||||
label: undefined,
|
||||
options: {},
|
||||
...overrides
|
||||
} as unknown as IBaseWidget
|
||||
}
|
||||
|
||||
function makeNode({
|
||||
isSubgraph = false,
|
||||
inputs = [] as INodeInputSlot[]
|
||||
}: {
|
||||
isSubgraph?: boolean
|
||||
inputs?: INodeInputSlot[]
|
||||
} = {}): LGraphNode {
|
||||
return {
|
||||
id: 1,
|
||||
inputs,
|
||||
isSubgraphNode: () => isSubgraph
|
||||
} as unknown as LGraphNode
|
||||
}
|
||||
|
||||
describe('renameWidget', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('renames a regular widget and its matching input', () => {
|
||||
const widget = makeWidget({ name: 'seed' })
|
||||
const input = { name: 'seed', widget: { name: 'seed' } } as INodeInputSlot
|
||||
const node = makeNode({ inputs: [input] })
|
||||
|
||||
const result = renameWidget(widget, node, 'My Seed')
|
||||
|
||||
expect(result).toBe(true)
|
||||
expect(widget.label).toBe('My Seed')
|
||||
expect(input.label).toBe('My Seed')
|
||||
})
|
||||
|
||||
it('clears label when given empty string', () => {
|
||||
const widget = makeWidget({ name: 'seed', label: 'Old Label' })
|
||||
const node = makeNode()
|
||||
|
||||
renameWidget(widget, node, '')
|
||||
|
||||
expect(widget.label).toBeUndefined()
|
||||
})
|
||||
|
||||
it('renames promoted widget source when node is a subgraph without explicit parents', () => {
|
||||
const sourceWidget = makeWidget({ name: 'innerSeed' })
|
||||
const interiorInput = {
|
||||
name: 'innerSeed',
|
||||
widget: { name: 'innerSeed' }
|
||||
} as INodeInputSlot
|
||||
const interiorNode = makeNode({ inputs: [interiorInput] })
|
||||
|
||||
mockedResolve.mockReturnValue({
|
||||
widget: sourceWidget,
|
||||
node: interiorNode
|
||||
})
|
||||
|
||||
const promotedWidget = makeWidget({
|
||||
name: 'seed',
|
||||
sourceNodeId: '5',
|
||||
sourceWidgetName: 'innerSeed'
|
||||
})
|
||||
const subgraphNode = makeNode({ isSubgraph: true })
|
||||
|
||||
const result = renameWidget(promotedWidget, subgraphNode, 'Renamed')
|
||||
|
||||
expect(result).toBe(true)
|
||||
expect(sourceWidget.label).toBe('Renamed')
|
||||
expect(interiorInput.label).toBe('Renamed')
|
||||
expect(promotedWidget.label).toBe('Renamed')
|
||||
})
|
||||
|
||||
it('does not resolve promoted widget source for non-subgraph node without parents', () => {
|
||||
const promotedWidget = makeWidget({
|
||||
name: 'seed',
|
||||
sourceNodeId: '5',
|
||||
sourceWidgetName: 'innerSeed'
|
||||
})
|
||||
const node = makeNode({ isSubgraph: false })
|
||||
|
||||
const result = renameWidget(promotedWidget, node, 'Renamed')
|
||||
|
||||
expect(result).toBe(true)
|
||||
expect(mockedResolve).not.toHaveBeenCalled()
|
||||
expect(promotedWidget.label).toBe('Renamed')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -48,7 +48,10 @@ export function renameWidget(
|
||||
newLabel: string,
|
||||
parents?: SubgraphNode[]
|
||||
): boolean {
|
||||
if (isPromotedWidgetView(widget) && parents?.length) {
|
||||
if (
|
||||
isPromotedWidgetView(widget) &&
|
||||
(parents?.length || node.isSubgraphNode())
|
||||
) {
|
||||
const sourceWidget = resolvePromotedWidgetSource(node, widget)
|
||||
if (!sourceWidget) {
|
||||
console.error('Could not resolve source widget for promoted widget')
|
||||
|
||||
Reference in New Issue
Block a user