Cleanup: Properties Panel (#7137)
## Summary - Code cleanup - Copy, padding, color, alignment of components - Subgraph Edit mode changes - Partial fix for the Node Info location (need to do context menu still) - Editing node title ### Still to-do - Bi-directionality in values ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7137-WIP-Cleanup-Properties-Panel-2be6d73d3650813e9430f6bcb09dfb4d) by [Unito](https://www.unito.io) --------- Co-authored-by: github-actions <github-actions@github.com>
@@ -27,18 +27,32 @@ dotenv.config()
|
||||
|
||||
type WorkspaceStore = ReturnType<typeof useWorkspaceStore>
|
||||
|
||||
class ComfyPropertiesPanel {
|
||||
readonly root: Locator
|
||||
readonly panelTitle: Locator
|
||||
readonly searchBox: Locator
|
||||
|
||||
constructor(readonly page: Page) {
|
||||
this.root = page.getByTestId('properties-panel')
|
||||
this.panelTitle = this.root.locator('h3')
|
||||
this.searchBox = this.root.getByPlaceholder('Search...')
|
||||
}
|
||||
}
|
||||
|
||||
class ComfyMenu {
|
||||
private _nodeLibraryTab: NodeLibrarySidebarTab | null = null
|
||||
private _workflowsTab: WorkflowsSidebarTab | null = null
|
||||
private _topbar: Topbar | null = null
|
||||
|
||||
public readonly sideToolbar: Locator
|
||||
public readonly propertiesPanel: ComfyPropertiesPanel
|
||||
public readonly themeToggleButton: Locator
|
||||
public readonly saveButton: Locator
|
||||
|
||||
constructor(public readonly page: Page) {
|
||||
this.sideToolbar = page.locator('.side-tool-bar-container')
|
||||
this.themeToggleButton = page.locator('.comfy-vue-theme-toggle')
|
||||
this.propertiesPanel = new ComfyPropertiesPanel(page)
|
||||
this.saveButton = page
|
||||
.locator('button[title="Save the current workflow"]')
|
||||
.nth(0)
|
||||
|
||||
@@ -5,14 +5,18 @@ import type { AutoQueueMode } from '../../src/stores/queueStore'
|
||||
export class ComfyActionbar {
|
||||
public readonly root: Locator
|
||||
public readonly queueButton: ComfyQueueButton
|
||||
public readonly propertiesButton: Locator
|
||||
|
||||
constructor(public readonly page: Page) {
|
||||
this.root = page.locator('.actionbar')
|
||||
this.root = page.locator('.actionbar-container')
|
||||
this.queueButton = new ComfyQueueButton(this)
|
||||
this.propertiesButton = this.root.getByLabel('Toggle properties panel')
|
||||
}
|
||||
|
||||
async isDocked() {
|
||||
const className = await this.root.getAttribute('class')
|
||||
const className = await this.root
|
||||
.locator('.actionbar')
|
||||
.getAttribute('class')
|
||||
return className?.includes('static') ?? false
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 86 KiB |
@@ -52,13 +52,10 @@ test.describe('Node Help', () => {
|
||||
await expect(helpButton).toBeVisible()
|
||||
await helpButton.click()
|
||||
|
||||
// Verify that the node library sidebar is opened
|
||||
await expect(
|
||||
comfyPage.menu.nodeLibraryTab.selectedTabButton
|
||||
).toBeVisible()
|
||||
|
||||
// Verify that the help page is shown for the correct node
|
||||
const helpPage = comfyPage.page.locator('.sidebar-content-container')
|
||||
const helpPage = comfyPage.page.locator(
|
||||
'[data-testid="properties-panel"]'
|
||||
)
|
||||
await expect(helpPage).toContainText('KSampler')
|
||||
await expect(helpPage.locator('.node-help-content')).toBeVisible()
|
||||
})
|
||||
@@ -170,7 +167,9 @@ test.describe('Node Help', () => {
|
||||
await helpButton.click()
|
||||
|
||||
// Verify loading spinner is shown
|
||||
const helpPage = comfyPage.page.locator('.sidebar-content-container')
|
||||
const helpPage = comfyPage.page.locator(
|
||||
'[data-testid="properties-panel"]'
|
||||
)
|
||||
await expect(helpPage.locator('.p-progressspinner')).toBeVisible()
|
||||
|
||||
// Wait for content to load
|
||||
@@ -200,7 +199,9 @@ test.describe('Node Help', () => {
|
||||
await helpButton.click()
|
||||
|
||||
// Verify fallback content is shown (description, inputs, outputs)
|
||||
const helpPage = comfyPage.page.locator('.sidebar-content-container')
|
||||
const helpPage = comfyPage.page.locator(
|
||||
'[data-testid="properties-panel"]'
|
||||
)
|
||||
await expect(helpPage).toContainText('Description')
|
||||
await expect(helpPage).toContainText('Inputs')
|
||||
await expect(helpPage).toContainText('Outputs')
|
||||
@@ -233,7 +234,9 @@ test.describe('Node Help', () => {
|
||||
)
|
||||
await helpButton.click()
|
||||
|
||||
const helpPage = comfyPage.page.locator('.sidebar-content-container')
|
||||
const helpPage = comfyPage.page.locator(
|
||||
'[data-testid="properties-panel"]'
|
||||
)
|
||||
await expect(helpPage).toContainText('KSampler Documentation')
|
||||
|
||||
// Check that relative image paths are prefixed correctly
|
||||
@@ -281,7 +284,9 @@ test.describe('Node Help', () => {
|
||||
)
|
||||
await helpButton.click()
|
||||
|
||||
const helpPage = comfyPage.page.locator('.sidebar-content-container')
|
||||
const helpPage = comfyPage.page.locator(
|
||||
'[data-testid="properties-panel"]'
|
||||
)
|
||||
|
||||
// Check relative video paths are prefixed
|
||||
const relativeVideo = helpPage.locator('video[src*="demo.mp4"]')
|
||||
@@ -354,7 +359,9 @@ This is documentation for a custom node.
|
||||
if (await helpButton.isVisible()) {
|
||||
await helpButton.click()
|
||||
|
||||
const helpPage = comfyPage.page.locator('.sidebar-content-container')
|
||||
const helpPage = comfyPage.page.locator(
|
||||
'[data-testid="properties-panel"]'
|
||||
)
|
||||
await expect(helpPage).toContainText('Custom Node Documentation')
|
||||
|
||||
// Check image path for custom nodes
|
||||
@@ -394,7 +401,9 @@ This is documentation for a custom node.
|
||||
)
|
||||
await helpButton.click()
|
||||
|
||||
const helpPage = comfyPage.page.locator('.sidebar-content-container')
|
||||
const helpPage = comfyPage.page.locator(
|
||||
'[data-testid="properties-panel"]'
|
||||
)
|
||||
|
||||
// Dangerous elements should be removed
|
||||
await expect(helpPage.locator('script')).toHaveCount(0)
|
||||
@@ -461,7 +470,9 @@ This is English documentation.
|
||||
)
|
||||
await helpButton.click()
|
||||
|
||||
const helpPage = comfyPage.page.locator('.sidebar-content-container')
|
||||
const helpPage = comfyPage.page.locator(
|
||||
'[data-testid="properties-panel"]'
|
||||
)
|
||||
await expect(helpPage).toContainText('KSamplerノード')
|
||||
await expect(helpPage).toContainText('これは日本語のドキュメントです')
|
||||
|
||||
@@ -484,7 +495,9 @@ This is English documentation.
|
||||
)
|
||||
await helpButton.click()
|
||||
|
||||
const helpPage = comfyPage.page.locator('.sidebar-content-container')
|
||||
const helpPage = comfyPage.page.locator(
|
||||
'[data-testid="properties-panel"]'
|
||||
)
|
||||
|
||||
// Should show fallback content (node description)
|
||||
await expect(helpPage).toBeVisible()
|
||||
@@ -528,7 +541,9 @@ This is English documentation.
|
||||
)
|
||||
await helpButton.click()
|
||||
|
||||
const helpPage = comfyPage.page.locator('.sidebar-content-container')
|
||||
const helpPage = comfyPage.page.locator(
|
||||
'[data-testid="properties-panel"]'
|
||||
)
|
||||
await expect(helpPage).toContainText('KSampler Help')
|
||||
await expect(helpPage).toContainText('This is KSampler documentation')
|
||||
|
||||
|
||||
35
browser_tests/tests/propertiesPanel/propertiesPanel.spec.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../../fixtures/ComfyPage'
|
||||
|
||||
test.describe('Properties panel', () => {
|
||||
test('opens and updates title based on selection', async ({ comfyPage }) => {
|
||||
await comfyPage.actionbar.propertiesButton.click()
|
||||
|
||||
const { propertiesPanel } = comfyPage.menu
|
||||
|
||||
await expect(propertiesPanel.panelTitle).toContainText(
|
||||
'No node(s) selected'
|
||||
)
|
||||
|
||||
await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)'])
|
||||
|
||||
await expect(propertiesPanel.panelTitle).toContainText('3 nodes selected')
|
||||
await expect(propertiesPanel.root.getByText('KSampler')).toHaveCount(1)
|
||||
await expect(
|
||||
propertiesPanel.root.getByText('CLIP Text Encode (Prompt)')
|
||||
).toHaveCount(2)
|
||||
|
||||
await propertiesPanel.searchBox.fill('seed')
|
||||
await expect(propertiesPanel.root.getByText('KSampler')).toHaveCount(1)
|
||||
await expect(
|
||||
propertiesPanel.root.getByText('CLIP Text Encode (Prompt)')
|
||||
).toHaveCount(0)
|
||||
|
||||
await propertiesPanel.searchBox.fill('')
|
||||
await expect(propertiesPanel.root.getByText('KSampler')).toHaveCount(1)
|
||||
await expect(
|
||||
propertiesPanel.root.getByText('CLIP Text Encode (Prompt)')
|
||||
).toHaveCount(2)
|
||||
})
|
||||
})
|
||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 151 KiB After Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |