From 90fe54ca9ef24d1739a6a45bc16cb532d8a3aed6 Mon Sep 17 00:00:00 2001
From: yyh <92089059+lyzno1@users.noreply.github.com>
Date: Tue, 5 May 2026 07:12:26 +0800
Subject: [PATCH] refactor(web): migrate workflow panel context menu primitive
(#35787)
---
.../__tests__/panel-contextmenu.spec.tsx | 181 +++++++-------
.../__tests__/use-panel-interactions.spec.ts | 22 +-
.../use-selection-interactions.spec.ts | 2 +-
.../workflow/hooks/use-panel-interactions.ts | 6 +-
.../components/workflow/panel-contextmenu.tsx | 230 ++++++++++--------
.../__tests__/index.spec.tsx | 6 +-
.../__tests__/version-history-item.spec.tsx | 12 +-
.../__tests__/action-menu-item.spec.tsx} | 6 +-
.../__tests__/index.spec.tsx | 14 +-
.../__tests__/use-action-menu.spec.tsx} | 12 +-
.../action-menu-item.tsx} | 6 +-
.../{context-menu => action-menu}/index.tsx | 24 +-
.../use-action-menu.ts} | 8 +-
.../panel/version-history-panel/index.tsx | 4 +-
.../version-history-item.tsx | 12 +-
.../store/__tests__/workflow-store.spec.ts | 2 +-
.../workflow/__tests__/panel-slice.spec.ts | 4 +-
.../workflow/store/workflow/panel-slice.ts | 4 +-
18 files changed, 279 insertions(+), 276 deletions(-)
rename web/app/components/workflow/panel/version-history-panel/{context-menu/__tests__/menu-item.spec.tsx => action-menu/__tests__/action-menu-item.spec.tsx} (89%)
rename web/app/components/workflow/panel/version-history-panel/{context-menu => action-menu}/__tests__/index.spec.tsx (75%)
rename web/app/components/workflow/panel/version-history-panel/{context-menu/__tests__/use-context-menu.spec.tsx => action-menu/__tests__/use-action-menu.spec.tsx} (83%)
rename web/app/components/workflow/panel/version-history-panel/{context-menu/menu-item.tsx => action-menu/action-menu-item.tsx} (89%)
rename web/app/components/workflow/panel/version-history-panel/{context-menu => action-menu}/index.tsx (68%)
rename web/app/components/workflow/panel/version-history-panel/{context-menu/use-context-menu.ts => action-menu/use-action-menu.ts} (89%)
diff --git a/web/app/components/workflow/__tests__/panel-contextmenu.spec.tsx b/web/app/components/workflow/__tests__/panel-contextmenu.spec.tsx
index 2c9c457245..6250ba45bd 100644
--- a/web/app/components/workflow/__tests__/panel-contextmenu.spec.tsx
+++ b/web/app/components/workflow/__tests__/panel-contextmenu.spec.tsx
@@ -1,107 +1,63 @@
-import type { ReactNode } from 'react'
-import { fireEvent, render, screen } from '@testing-library/react'
+import { fireEvent, screen, waitFor } from '@testing-library/react'
import PanelContextmenu from '../panel-contextmenu'
+import { BlockEnum } from '../types'
+import { createNode } from './fixtures'
+import { renderWorkflowFlowComponent } from './workflow-test-env'
-const mockUseClickAway = vi.hoisted(() => vi.fn())
const mockUseTranslation = vi.hoisted(() => vi.fn())
-const mockUseStore = vi.hoisted(() => vi.fn())
const mockUseNodesInteractions = vi.hoisted(() => vi.fn())
const mockUsePanelInteractions = vi.hoisted(() => vi.fn())
const mockUseWorkflowStartRun = vi.hoisted(() => vi.fn())
const mockUseWorkflowMoveMode = vi.hoisted(() => vi.fn())
const mockUseOperator = vi.hoisted(() => vi.fn())
const mockUseDSL = vi.hoisted(() => vi.fn())
-
-vi.mock('ahooks', () => ({
- useClickAway: (...args: unknown[]) => mockUseClickAway(...args),
-}))
+const mockUseNodesReadOnly = vi.hoisted(() => vi.fn())
+const mockUseAvailableBlocks = vi.hoisted(() => vi.fn())
+const mockUseNodesMetaData = vi.hoisted(() => vi.fn())
+const mockUseIsChatMode = vi.hoisted(() => vi.fn())
vi.mock('react-i18next', () => ({
useTranslation: () => mockUseTranslation(),
}))
-vi.mock('@/app/components/workflow/store', () => ({
- useStore: (selector: (state: {
- panelMenu?: { left: number, top: number }
- clipboardElements: unknown[]
- pendingComment: null | { pageX: number, pageY: number, elementX: number, elementY: number }
- setCommentPlacing: (placing: boolean) => void
- setCommentQuickAdd: (quickAdd: boolean) => void
- setShowImportDSLModal: (visible: boolean) => void
- }) => unknown) => mockUseStore(selector),
-}))
-
vi.mock('@/app/components/workflow/hooks', () => ({
- useNodesInteractions: () => mockUseNodesInteractions(),
- usePanelInteractions: () => mockUsePanelInteractions(),
- useWorkflowStartRun: () => mockUseWorkflowStartRun(),
- useWorkflowMoveMode: () => mockUseWorkflowMoveMode(),
+ useAvailableBlocks: () => mockUseAvailableBlocks(),
useDSL: () => mockUseDSL(),
+ useIsChatMode: () => mockUseIsChatMode(),
+ useNodesInteractions: () => mockUseNodesInteractions(),
+ useNodesMetaData: () => mockUseNodesMetaData(),
+ useNodesReadOnly: () => mockUseNodesReadOnly(),
+ usePanelInteractions: () => mockUsePanelInteractions(),
+ useWorkflowMoveMode: () => mockUseWorkflowMoveMode(),
+ useWorkflowStartRun: () => mockUseWorkflowStartRun(),
}))
vi.mock('@/app/components/workflow/operator/hooks', () => ({
useOperator: () => mockUseOperator(),
}))
-vi.mock('@/app/components/workflow/operator/add-block', () => ({
- __esModule: true,
- default: ({ renderTrigger }: { renderTrigger: () => ReactNode }) => (
-
{renderTrigger()}
- ),
-}))
-
-vi.mock('@/app/components/base/divider', () => ({
- __esModule: true,
- default: ({ className }: { className?: string }) => ,
-}))
-
-vi.mock('@/app/components/workflow/shortcuts-name', () => ({
- __esModule: true,
- default: ({ keys }: { keys: string[] }) => {keys.join('+')},
-}))
-
describe('PanelContextmenu', () => {
const mockHandleNodesPaste = vi.fn()
const mockHandlePaneContextmenuCancel = vi.fn()
const mockHandleStartWorkflowRun = vi.fn()
+ const mockHandleWorkflowStartRunInChatflow = vi.fn()
const mockHandleAddNote = vi.fn()
const mockExportCheck = vi.fn()
- const mockSetShowImportDSLModal = vi.fn()
- const mockSetCommentPlacing = vi.fn()
- const mockSetCommentQuickAdd = vi.fn()
- let panelMenu: { left: number, top: number } | undefined
- let clipboardElements: unknown[]
- let pendingComment: null | { pageX: number, pageY: number, elementX: number, elementY: number }
- let clickAwayHandler: (() => void) | undefined
+ const defaultNodesMetaDataMap = {
+ [BlockEnum.Answer]: {
+ defaultValue: {
+ title: 'Answer',
+ desc: '',
+ type: BlockEnum.Answer,
+ },
+ },
+ }
beforeEach(() => {
vi.clearAllMocks()
- panelMenu = undefined
- clipboardElements = []
- pendingComment = null
- clickAwayHandler = undefined
-
- mockUseClickAway.mockImplementation((handler: () => void) => {
- clickAwayHandler = handler
- })
mockUseTranslation.mockReturnValue({
t: (key: string) => key,
})
- mockUseStore.mockImplementation((selector: (state: {
- panelMenu?: { left: number, top: number }
- clipboardElements: unknown[]
- pendingComment: null | { pageX: number, pageY: number, elementX: number, elementY: number }
- setCommentPlacing: (placing: boolean) => void
- setCommentQuickAdd: (quickAdd: boolean) => void
- setShowImportDSLModal: (visible: boolean) => void
- }) => unknown) => selector({
- panelMenu,
- clipboardElements,
- pendingComment,
- setCommentPlacing: mockSetCommentPlacing,
- setCommentQuickAdd: mockSetCommentQuickAdd,
- setShowImportDSLModal: mockSetShowImportDSLModal,
- }))
mockUseNodesInteractions.mockReturnValue({
handleNodesPaste: mockHandleNodesPaste,
})
@@ -110,6 +66,7 @@ describe('PanelContextmenu', () => {
})
mockUseWorkflowStartRun.mockReturnValue({
handleStartWorkflowRun: mockHandleStartWorkflowRun,
+ handleWorkflowStartRunInChatflow: mockHandleWorkflowStartRunInChatflow,
})
mockUseWorkflowMoveMode.mockReturnValue({
isCommentModeAvailable: false,
@@ -120,50 +77,86 @@ describe('PanelContextmenu', () => {
mockUseDSL.mockReturnValue({
exportCheck: mockExportCheck,
})
+ mockUseNodesReadOnly.mockReturnValue({
+ nodesReadOnly: false,
+ })
+ mockUseAvailableBlocks.mockReturnValue({
+ availableNextBlocks: [BlockEnum.Answer],
+ })
+ mockUseNodesMetaData.mockReturnValue({
+ nodesMap: defaultNodesMetaDataMap,
+ })
+ mockUseIsChatMode.mockReturnValue(false)
})
it('should stay hidden when the panel menu is absent', () => {
- render()
+ renderWorkflowFlowComponent()
- expect(screen.queryByTestId('add-block')).not.toBeInTheDocument()
+ expect(screen.queryByText('common.addBlock')).not.toBeInTheDocument()
})
- it('should keep paste disabled when the clipboard is empty', () => {
- panelMenu = { left: 24, top: 48 }
-
- render()
+ it('should keep paste disabled when the clipboard is empty', async () => {
+ renderWorkflowFlowComponent(, {
+ initialStoreState: {
+ panelMenu: { clientX: 24, clientY: 48 },
+ },
+ hooksStoreProps: {},
+ })
+ await screen.findByText('common.pasteHere')
fireEvent.click(screen.getByText('common.pasteHere'))
expect(mockHandleNodesPaste).not.toHaveBeenCalled()
expect(mockHandlePaneContextmenuCancel).not.toHaveBeenCalled()
})
- it('should render actions, position the menu, and execute each action', () => {
- panelMenu = { left: 24, top: 48 }
- clipboardElements = [{ id: 'copied-node' }]
- const { container } = render()
-
- expect(screen.getByTestId('add-block')).toHaveTextContent('common.addBlock')
- expect(screen.getByRole('button', { name: /common\.run/i })).toHaveTextContent(/Alt\s*R/)
- expect(screen.getByRole('button', { name: /common\.pasteHere/i })).toHaveTextContent(/Ctrl\s*V/)
- expect(container.firstChild).toHaveStyle({
- left: '24px',
- top: '48px',
+ it('should render actions and execute enabled actions', async () => {
+ const { store } = renderWorkflowFlowComponent(, {
+ initialStoreState: {
+ panelMenu: { clientX: 24, clientY: 48 },
+ clipboardElements: [createNode({ id: 'copied-node' })],
+ },
+ hooksStoreProps: {},
})
+ expect(await screen.findByText('common.addBlock')).toBeInTheDocument()
+ expect(screen.getByText('common.run')).toBeInTheDocument()
+ expect(screen.getByText('common.pasteHere')).toBeInTheDocument()
+
fireEvent.click(screen.getByText('nodes.note.addNote'))
fireEvent.click(screen.getByText('common.run'))
fireEvent.click(screen.getByText('common.pasteHere'))
fireEvent.click(screen.getByText('export'))
fireEvent.click(screen.getByText('importApp'))
- clickAwayHandler?.()
- expect(mockHandleAddNote).toHaveBeenCalledTimes(1)
- expect(mockHandleStartWorkflowRun).toHaveBeenCalledTimes(1)
- expect(mockHandleNodesPaste).toHaveBeenCalledTimes(1)
- expect(mockExportCheck).toHaveBeenCalledTimes(1)
- expect(mockSetShowImportDSLModal).toHaveBeenCalledWith(true)
- expect(mockHandlePaneContextmenuCancel).toHaveBeenCalledTimes(4)
+ await waitFor(() => {
+ expect(mockHandleAddNote).toHaveBeenCalledTimes(1)
+ expect(mockHandleStartWorkflowRun).toHaveBeenCalledTimes(1)
+ expect(mockHandleNodesPaste).toHaveBeenCalledTimes(1)
+ expect(mockExportCheck).toHaveBeenCalledTimes(1)
+ expect(store.getState().showImportDSLModal).toBe(true)
+ })
+ })
+
+ it('should render preview action in chat mode', async () => {
+ mockUseIsChatMode.mockReturnValue(true)
+
+ renderWorkflowFlowComponent(, {
+ initialStoreState: {
+ panelMenu: { clientX: 24, clientY: 48 },
+ },
+ hooksStoreProps: {},
+ })
+
+ expect(await screen.findByText('common.debugAndPreview')).toBeInTheDocument()
+ expect(screen.queryByText('common.run')).not.toBeInTheDocument()
+
+ fireEvent.click(screen.getByText('common.debugAndPreview'))
+
+ await waitFor(() => {
+ expect(mockHandleWorkflowStartRunInChatflow).toHaveBeenCalledTimes(1)
+ expect(mockHandleStartWorkflowRun).not.toHaveBeenCalled()
+ expect(mockHandlePaneContextmenuCancel).toHaveBeenCalled()
+ })
})
})
diff --git a/web/app/components/workflow/hooks/__tests__/use-panel-interactions.spec.ts b/web/app/components/workflow/hooks/__tests__/use-panel-interactions.spec.ts
index 6b35c511c1..8452087b7c 100644
--- a/web/app/components/workflow/hooks/__tests__/use-panel-interactions.spec.ts
+++ b/web/app/components/workflow/hooks/__tests__/use-panel-interactions.spec.ts
@@ -36,7 +36,7 @@ describe('usePanelInteractions', () => {
container.remove()
})
- it('handlePaneContextMenu should set panelMenu with computed coordinates when container exists', () => {
+ it('handlePaneContextMenu should set panelMenu with viewport coordinates', () => {
const { result, store } = renderWorkflowHook(() => usePanelInteractions(), {
initialStoreState: {
nodeMenu: { clientX: 40, clientY: 20, nodeId: 'n1' },
@@ -54,28 +54,14 @@ describe('usePanelInteractions', () => {
expect(preventDefault).toHaveBeenCalled()
expect(store.getState().panelMenu).toEqual({
- top: 200,
- left: 250,
+ clientX: 350,
+ clientY: 250,
})
expect(store.getState().nodeMenu).toBeUndefined()
expect(store.getState().selectionMenu).toBeUndefined()
expect(store.getState().edgeMenu).toBeUndefined()
})
- it('handlePaneContextMenu should throw when container does not exist', () => {
- container.remove()
-
- const { result } = renderWorkflowHook(() => usePanelInteractions())
-
- expect(() => {
- result.current.handlePaneContextMenu({
- preventDefault: vi.fn(),
- clientX: 350,
- clientY: 250,
- } as unknown as React.MouseEvent)
- }).toThrow()
- })
-
it('handlePaneContextMenu should sync clipboard from navigator clipboard', async () => {
const clipboardNode = createNode({ id: 'clipboard-node' })
const clipboardEdge = createEdge({
@@ -106,7 +92,7 @@ describe('usePanelInteractions', () => {
it('handlePaneContextmenuCancel should clear panelMenu', () => {
const { result, store } = renderWorkflowHook(() => usePanelInteractions(), {
- initialStoreState: { panelMenu: { top: 10, left: 20 } },
+ initialStoreState: { panelMenu: { clientX: 20, clientY: 10 } },
})
result.current.handlePaneContextmenuCancel()
diff --git a/web/app/components/workflow/hooks/__tests__/use-selection-interactions.spec.ts b/web/app/components/workflow/hooks/__tests__/use-selection-interactions.spec.ts
index bb35abf743..894c40c4f6 100644
--- a/web/app/components/workflow/hooks/__tests__/use-selection-interactions.spec.ts
+++ b/web/app/components/workflow/hooks/__tests__/use-selection-interactions.spec.ts
@@ -174,7 +174,7 @@ describe('useSelectionInteractions', () => {
it('handleSelectionContextMenu should set menu only when clicking on selection rect', () => {
const { result, store } = renderSelectionInteractions({
nodeMenu: { clientX: 20, clientY: 10, nodeId: 'n1' },
- panelMenu: { top: 30, left: 40 },
+ panelMenu: { clientX: 40, clientY: 30 },
edgeMenu: { clientX: 320, clientY: 180, edgeId: 'e1' },
})
diff --git a/web/app/components/workflow/hooks/use-panel-interactions.ts b/web/app/components/workflow/hooks/use-panel-interactions.ts
index 3b7a2158a1..687a80613d 100644
--- a/web/app/components/workflow/hooks/use-panel-interactions.ts
+++ b/web/app/components/workflow/hooks/use-panel-interactions.ts
@@ -22,15 +22,13 @@ export const usePanelInteractions = () => {
workflowStore.getState().setClipboardData({ nodes, edges })
})
- const container = document.querySelector('#workflow-container')
- const { x, y } = container!.getBoundingClientRect()
workflowStore.setState({
nodeMenu: undefined,
selectionMenu: undefined,
edgeMenu: undefined,
panelMenu: {
- top: e.clientY - y,
- left: e.clientX - x,
+ clientX: e.clientX,
+ clientY: e.clientY,
},
})
}, [workflowStore, appDslVersion])
diff --git a/web/app/components/workflow/panel-contextmenu.tsx b/web/app/components/workflow/panel-contextmenu.tsx
index 5bac2e2364..3c2207adab 100644
--- a/web/app/components/workflow/panel-contextmenu.tsx
+++ b/web/app/components/workflow/panel-contextmenu.tsx
@@ -1,13 +1,20 @@
import { cn } from '@langgenius/dify-ui/cn'
-import { useClickAway } from 'ahooks'
+import {
+ ContextMenu,
+ ContextMenuContent,
+ ContextMenuGroup,
+ ContextMenuItem,
+ ContextMenuSeparator,
+} from '@langgenius/dify-ui/context-menu'
import {
memo,
- useRef,
+ useCallback,
+ useMemo,
} from 'react'
import { useTranslation } from 'react-i18next'
-import Divider from '../base/divider'
import {
useDSL,
+ useIsChatMode,
useNodesInteractions,
usePanelInteractions,
useWorkflowMoveMode,
@@ -20,7 +27,6 @@ import { useStore } from './store'
const PanelContextmenu = () => {
const { t } = useTranslation()
- const ref = useRef(null)
const panelMenu = useStore(s => s.panelMenu)
const clipboardElements = useStore(s => s.clipboardElements)
const setShowImportDSLModal = useStore(s => s.setShowImportDSLModal)
@@ -29,127 +35,147 @@ const PanelContextmenu = () => {
const setCommentQuickAdd = useStore(s => s.setCommentQuickAdd)
const { handleNodesPaste } = useNodesInteractions()
const { handlePaneContextmenuCancel } = usePanelInteractions()
- const { handleStartWorkflowRun } = useWorkflowStartRun()
+ const {
+ handleStartWorkflowRun,
+ handleWorkflowStartRunInChatflow,
+ } = useWorkflowStartRun()
const { handleAddNote } = useOperator()
const { isCommentModeAvailable } = useWorkflowMoveMode()
const { exportCheck } = useDSL()
+ const isChatMode = useIsChatMode()
+ const panelMenuClientX = panelMenu?.clientX
+ const panelMenuClientY = panelMenu?.clientY
- useClickAway(() => {
- handlePaneContextmenuCancel()
- }, ref)
+ const anchor = useMemo(() => {
+ if (panelMenuClientX === undefined || panelMenuClientY === undefined)
+ return null
- const renderTrigger = () => {
+ return {
+ getBoundingClientRect: () => DOMRect.fromRect({
+ width: 0,
+ height: 0,
+ x: panelMenuClientX,
+ y: panelMenuClientY,
+ }),
+ }
+ }, [panelMenuClientX, panelMenuClientY])
+
+ const renderAddBlockTrigger = useCallback(() => {
return (
)
- }
+ }, [t])
- if (!panelMenu)
+ const handleRunAction = useCallback(() => {
+ if (isChatMode)
+ handleWorkflowStartRunInChatflow()
+ else
+ handleStartWorkflowRun()
+
+ handlePaneContextmenuCancel()
+ }, [isChatMode, handleWorkflowStartRunInChatflow, handleStartWorkflowRun, handlePaneContextmenuCancel])
+
+ if (!panelMenu || !anchor)
return null
return (
- !open && handlePaneContextmenuCancel()}
>
-
-
-
- {isCommentModeAvailable && (
-
- )}
-
-
-
-
-
-
-
-
-
-
-
-
+
+ {isChatMode ? t('common.debugAndPreview', { ns: 'workflow' }) : t('common.run', { ns: 'workflow' })}
+ {!isChatMode && }
+
+
+
+
+ {
+ if (clipboardElements.length) {
+ handleNodesPaste()
+ handlePaneContextmenuCancel()
+ }
+ }}
+ >
+ {t('common.pasteHere', { ns: 'workflow' })}
+
+
+
+
+
+ exportCheck?.()}
+ >
+ {t('export', { ns: 'app' })}
+
+ setShowImportDSLModal(true)}
+ >
+ {t('importApp', { ns: 'app' })}
+
+
+
+
)
}
diff --git a/web/app/components/workflow/panel/version-history-panel/__tests__/index.spec.tsx b/web/app/components/workflow/panel/version-history-panel/__tests__/index.spec.tsx
index 3d73d5a61c..3c9f7dba0e 100644
--- a/web/app/components/workflow/panel/version-history-panel/__tests__/index.spec.tsx
+++ b/web/app/components/workflow/panel/version-history-panel/__tests__/index.spec.tsx
@@ -49,7 +49,7 @@ type MockRestoreConfirmModalProps = {
type MockVersionHistoryItemProps = {
item: VersionHistory
onClick: (item: VersionHistory) => void
- handleClickMenuItem: (operation: VersionHistoryContextMenuOptions) => void
+ handleClickActionMenuItem: (operation: VersionHistoryContextMenuOptions) => void
}
vi.mock('@/context/app-context', () => ({
@@ -148,7 +148,7 @@ vi.mock('@/app/components/app/app-publisher/version-info-modal', () => ({
vi.mock('../version-history-item', () => ({
default: (props: MockVersionHistoryItemProps) => {
const MockVersionHistoryItem = () => {
- const { item, onClick, handleClickMenuItem } = props
+ const { item, onClick, handleClickActionMenuItem } = props
useEffect(() => {
if (item.version === WorkflowVersion.Draft)
@@ -159,7 +159,7 @@ vi.mock('../version-history-item', () => ({
{item.version !== WorkflowVersion.Draft && (
-
- {/* Context Menu */}
+ {/* Action Menu */}
{!isDraft && isHovering && (
-
)}
diff --git a/web/app/components/workflow/store/__tests__/workflow-store.spec.ts b/web/app/components/workflow/store/__tests__/workflow-store.spec.ts
index 47819c049f..4b5dc0b302 100644
--- a/web/app/components/workflow/store/__tests__/workflow-store.spec.ts
+++ b/web/app/components/workflow/store/__tests__/workflow-store.spec.ts
@@ -108,7 +108,7 @@ describe('createWorkflowStore', () => {
['showWorkflowVersionHistoryPanel', 'setShowWorkflowVersionHistoryPanel', true],
['showInputsPanel', 'setShowInputsPanel', true],
['showDebugAndPreviewPanel', 'setShowDebugAndPreviewPanel', true],
- ['panelMenu', 'setPanelMenu', { top: 10, left: 20 }],
+ ['panelMenu', 'setPanelMenu', { clientX: 20, clientY: 10 }],
['selectionMenu', 'setSelectionMenu', { clientX: 50, clientY: 60 }],
['edgeMenu', 'setEdgeMenu', { clientX: 320, clientY: 180, edgeId: 'e1' }],
['showVariableInspectPanel', 'setShowVariableInspectPanel', true],
diff --git a/web/app/components/workflow/store/workflow/__tests__/panel-slice.spec.ts b/web/app/components/workflow/store/workflow/__tests__/panel-slice.spec.ts
index 0c7f55850f..1f30a2b7cf 100644
--- a/web/app/components/workflow/store/workflow/__tests__/panel-slice.spec.ts
+++ b/web/app/components/workflow/store/workflow/__tests__/panel-slice.spec.ts
@@ -19,12 +19,12 @@ describe('createPanelSlice', () => {
store.getState().setShowFeaturesPanel(true)
store.getState().setShowDebugAndPreviewPanel(true)
- store.getState().setPanelMenu({ top: 24, left: 48 })
+ store.getState().setPanelMenu({ clientX: 48, clientY: 24 })
store.getState().setEdgeMenu({ clientX: 80, clientY: 120, edgeId: 'edge-1' })
expect(store.getState().showFeaturesPanel).toBe(true)
expect(store.getState().showDebugAndPreviewPanel).toBe(true)
- expect(store.getState().panelMenu).toEqual({ top: 24, left: 48 })
+ expect(store.getState().panelMenu).toEqual({ clientX: 48, clientY: 24 })
expect(store.getState().edgeMenu).toEqual({ clientX: 80, clientY: 120, edgeId: 'edge-1' })
})
})
diff --git a/web/app/components/workflow/store/workflow/panel-slice.ts b/web/app/components/workflow/store/workflow/panel-slice.ts
index e84d33a1eb..2f4264fc78 100644
--- a/web/app/components/workflow/store/workflow/panel-slice.ts
+++ b/web/app/components/workflow/store/workflow/panel-slice.ts
@@ -17,8 +17,8 @@ export type PanelSliceShape = {
showUserCursors: boolean
setShowUserCursors: (showUserCursors: boolean) => void
panelMenu?: {
- top: number
- left: number
+ clientX: number
+ clientY: number
}
setPanelMenu: (panelMenu: PanelSliceShape['panelMenu']) => void
selectionMenu?: {