mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-06-05 20:54:56 +00:00
## Summary https://linear.app/comfyorg/issue/FE-867/bug-node-expand-menu-doesnt-work-nodes-immediately-collapse-after Recreates #12175 on a fresh `main` base (original branch's CI failed only because its `frontend-dist` artifact had expired — not a code issue). Original work by @christian-byrne / Glary-Bot, cherry-picked here so it can land while he's offline. The Vue right-click "More Options" node menu shows duplicates for collapse/expand functionality: - **Vue source**: `Minimize Node` / `Expand Node` (works) - **LiteGraph source**: `Resize`, `Collapse`, `Expand` (silently no-op in this menu — the converter wrapper invokes `LGraphCanvas.onMenuNodeCollapse` without the `node` arg it expects) Suppress the LiteGraph duplicates in `convertContextMenuToOptions` by matching the built-in **callback identity** (`LGraphCanvas.onMenuResizeNode`, `LGraphCanvas.onMenuNodeCollapse`), not the raw label. Matching by identity avoids accidentally hiding extension-provided items that share those labels. Also align `CORE_MENU_ITEMS` / `MENU_ORDER` on the Vue label `Expand Node` so the toggled Minimize/Expand pair sorts correctly. ## Scope of suppression Only the Vue node menu (via `convertContextMenuToOptions`) is affected. The raw `LGraphCanvas.getNodeMenuOptions` output is untouched, so: - The legacy right-click menu (`Comfy.UseNewMenu` disabled) still has `Collapse` / `Resize`. - `useLoad3d.ts`, which calls `new LiteGraph.ContextMenu(app.canvas.getNodeMenuOptions(node), ...)`, is unaffected. - Extensions that monkey-patch `getNodeMenuOptions` continue to receive the full option list. ## Tests - `contextMenuConverter.test.ts`: covers both that built-in entries are dropped by identity AND that extension-provided items with the same labels survive. - E2E `selectionToolboxMoreActions.spec.ts`: asserts the Vue "More Options" menu shows `Minimize Node` but no `Resize`/`Collapse`/`Expand`. - `pnpm typecheck` clean. Supersedes #12175. --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
97 lines
3.4 KiB
TypeScript
97 lines
3.4 KiB
TypeScript
import { expect } from '@playwright/test'
|
|
|
|
import type { ComfyPage } from '@e2e/fixtures/ComfyPage'
|
|
import { comfyPageFixture as test } from '@e2e/fixtures/ComfyPage'
|
|
|
|
test.describe(
|
|
'Node context menu viewport overflow (#10824)',
|
|
{ tag: '@ui' },
|
|
() => {
|
|
test.beforeEach(async ({ comfyPage }) => {
|
|
// Keep the viewport well below the menu content height so overflow is guaranteed.
|
|
await comfyPage.page.setViewportSize({ width: 1280, height: 300 })
|
|
await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Disabled')
|
|
await comfyPage.settings.setSetting('Comfy.Canvas.SelectionToolbox', true)
|
|
await comfyPage.workflow.loadWorkflow('nodes/single_ksampler')
|
|
})
|
|
|
|
async function openMoreOptions(comfyPage: ComfyPage) {
|
|
const ksamplerNodes =
|
|
await comfyPage.nodeOps.getNodeRefsByTitle('KSampler')
|
|
if (ksamplerNodes.length === 0) {
|
|
throw new Error('No KSampler nodes found')
|
|
}
|
|
|
|
// Drag the KSampler toward the lower-left so the menu has limited space below it.
|
|
const nodePos = await ksamplerNodes[0].getPosition()
|
|
const viewportSize = comfyPage.page.viewportSize()!
|
|
const centerX = viewportSize.width / 3
|
|
const centerY = viewportSize.height * 0.75
|
|
await comfyPage.canvasOps.dragAndDrop(
|
|
{ x: nodePos.x, y: nodePos.y },
|
|
{ x: centerX, y: centerY }
|
|
)
|
|
await comfyPage.nextFrame()
|
|
|
|
await ksamplerNodes[0].click('title')
|
|
|
|
await expect(comfyPage.page.locator('.selection-toolbox')).toBeVisible()
|
|
|
|
const moreOptionsBtn = comfyPage.page.getByTestId('more-options-button')
|
|
await expect(moreOptionsBtn).toBeVisible()
|
|
await moreOptionsBtn.click()
|
|
await comfyPage.nextFrame()
|
|
|
|
const menu = comfyPage.page.locator('.p-contextmenu')
|
|
await expect(menu).toBeVisible()
|
|
|
|
// Wait for constrainMenuHeight (runs via requestAnimationFrame in onMenuShow)
|
|
await comfyPage.nextFrame()
|
|
|
|
return menu
|
|
}
|
|
|
|
test('last menu item "Remove" is reachable via scroll', async ({
|
|
comfyPage
|
|
}) => {
|
|
const menu = await openMoreOptions(comfyPage)
|
|
const rootList = menu.locator(':scope > ul')
|
|
|
|
await expect
|
|
.poll(
|
|
() => rootList.evaluate((el) => el.scrollHeight > el.clientHeight),
|
|
{
|
|
message:
|
|
'Menu should overflow vertically so this test exercises the viewport clamp'
|
|
}
|
|
)
|
|
.toBe(true)
|
|
|
|
// "Remove" is the last item in the More Options menu.
|
|
// It must become reachable by scrolling the bounded menu list.
|
|
const removeItem = menu.getByText('Remove', { exact: true })
|
|
const didScroll = await rootList.evaluate((el) => {
|
|
const previousScrollTop = el.scrollTop
|
|
el.scrollTo({ top: el.scrollHeight })
|
|
return el.scrollTop > previousScrollTop
|
|
})
|
|
expect(didScroll).toBe(true)
|
|
await expect(removeItem).toBeVisible()
|
|
})
|
|
|
|
test('last menu item "Remove" is clickable and removes the node', async ({
|
|
comfyPage
|
|
}) => {
|
|
const menu = await openMoreOptions(comfyPage)
|
|
|
|
const removeItem = menu.getByText('Remove', { exact: true })
|
|
await removeItem.scrollIntoViewIfNeeded()
|
|
await removeItem.click()
|
|
await comfyPage.nextFrame()
|
|
|
|
// The node should be removed from the graph
|
|
await expect.poll(() => comfyPage.nodeOps.getGraphNodesCount()).toBe(0)
|
|
})
|
|
}
|
|
)
|