perf: virtualize FormDropdownMenu and fix VirtualGrid jitter

- Integrate VirtualGrid into FormDropdownMenu for virtualized rendering
- Fix jitter: overflow-anchor:none, scrollbar-gutter:stable, cols=maxColumns when finite
- Remove transition-[width] from grid items, replace transition-all with explicit properties
- Replace LazyImage with native img (redundant with virtualization)
- Change max-h-[640px] to fixed h-[640px] for proper virtualization
- Add unit tests for VirtualGrid and FormDropdownMenu
- Add E2E test for image dropdown virtualization

Amp-Thread-ID: https://ampcode.com/threads/T-019c5a71-66c8-76e9-95ed-671a1b4538da
This commit is contained in:
bymyself
2026-02-13 20:58:04 -08:00
parent 337e0486ea
commit 092e3934f6
6 changed files with 421 additions and 46 deletions

View File

@@ -0,0 +1,49 @@
import {
comfyExpect as expect,
comfyPageFixture as test
} from '../../../fixtures/ComfyPage'
test.describe('Image Dropdown Virtualization', { tag: '@widget' }, () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.VueNodes.Enabled', true)
await comfyPage.vueNodes.waitForNodes()
})
test('should virtualize items when dropdown has many entries', async ({
comfyPage
}) => {
await comfyPage.loadWorkflow('widgets/load_image_widget')
await comfyPage.vueNodes.waitForNodes()
const totalItems = await comfyPage.page.evaluate(() => {
const node = window['graph']._nodes_by_id['10']
const widget = node.widgets.find(
(w: { name: string }) => w.name === 'image'
)
const count = 60
const values = Array.from(
{ length: count },
(_, i) => `test_image_${i}.png`
)
widget.options.values = values
widget.value = values[0]
return count
})
const loadImageNode = comfyPage.vueNodes.getNodeByTitle('Load Image')
const dropdownButton = loadImageNode.locator(
'button:has(span:has-text("test_image_0.png"))'
)
await dropdownButton.waitFor({ state: 'visible' })
await dropdownButton.click()
const virtualGridItems = comfyPage.page.locator(
'[data-virtual-grid-item]'
)
await expect(virtualGridItems.first()).toBeVisible()
const renderedCount = await virtualGridItems.count()
expect(renderedCount).toBeLessThan(totalItems)
expect(renderedCount).toBeGreaterThan(0)
})
})