From 88d82b5401233a2798f65eda28c4ef41fefe5a17 Mon Sep 17 00:00:00 2001 From: yyh Date: Wed, 25 Mar 2026 00:05:40 +0800 Subject: [PATCH] fix --- .../tree/node-delete-confirm-dialog.tsx | 8 +++- .../skill/file-tree/tree/tree-node.spec.tsx | 47 ++++++++++++++++++- .../skill/file-tree/tree/tree-node.tsx | 4 ++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/web/app/components/workflow/skill/file-tree/tree/node-delete-confirm-dialog.tsx b/web/app/components/workflow/skill/file-tree/tree/node-delete-confirm-dialog.tsx index 5ff302139e..7df3506195 100644 --- a/web/app/components/workflow/skill/file-tree/tree/node-delete-confirm-dialog.tsx +++ b/web/app/components/workflow/skill/file-tree/tree/node-delete-confirm-dialog.tsx @@ -29,6 +29,9 @@ const NodeDeleteConfirmDialog = ({ }: NodeDeleteConfirmDialogProps) => { const { t } = useTranslation('workflow') const isFolder = nodeType === 'folder' + const handleDialogClick = React.useCallback((e: React.MouseEvent) => { + e.stopPropagation() + }, []) return ( - +
{isFolder diff --git a/web/app/components/workflow/skill/file-tree/tree/tree-node.spec.tsx b/web/app/components/workflow/skill/file-tree/tree/tree-node.spec.tsx index 6573a4ae14..78d301b258 100644 --- a/web/app/components/workflow/skill/file-tree/tree/tree-node.spec.tsx +++ b/web/app/components/workflow/skill/file-tree/tree/tree-node.spec.tsx @@ -111,9 +111,20 @@ vi.mock('../../hooks/file-tree/operations/use-file-operations', () => ({ })) vi.mock('./node-menu', () => ({ - default: ({ type, menuType, onClose }: { type: string, menuType: string, onClose: () => void }) => ( + default: ({ + type, + menuType, + onClose, + onDeleteClick, + }: { + type: string + menuType: string + onClose: () => void + onDeleteClick?: () => void + }) => (
+
), })) @@ -297,6 +308,23 @@ describe('TreeNode', () => { expect(screen.getByTestId('node-menu-dropdown')).toHaveAttribute('data-type', 'file') }) + + it('should stop dropdown item clicks from bubbling to the parent row host', () => { + const props = buildProps({ id: 'file-1', name: 'readme.md', nodeType: 'file' }) + const rowHostClick = vi.fn() + + render( +
+ +
, + ) + + fireEvent.click(screen.getByRole('button', { name: /workflow\.skillSidebar\.menu\.moreActions/i })) + fireEvent.click(screen.getByRole('button', { name: 'delete-item' })) + + expect(fileOperationMocks.handleDeleteClick).toHaveBeenCalledTimes(1) + expect(rowHostClick).not.toHaveBeenCalled() + }) }) // Effects should synchronize external drag status transitions into workflow store state. @@ -361,5 +389,22 @@ describe('TreeNode', () => { expect(fileOperationMocks.handleDeleteConfirm).toHaveBeenCalledTimes(1) expect(fileOperationMocks.handleDeleteCancel).toHaveBeenCalledTimes(1) }) + + it('should stop cancel clicks in delete confirmation from bubbling to the parent row host', () => { + fileOperationMocks.showDeleteConfirm = true + const props = buildProps({ id: 'file-1', name: 'readme.md', nodeType: 'file' }) + const rowHostClick = vi.fn() + + render( +
+ +
, + ) + + fireEvent.click(screen.getByRole('button', { name: /common\.operation\.cancel/i })) + + expect(fileOperationMocks.handleDeleteCancel).toHaveBeenCalledTimes(1) + expect(rowHostClick).not.toHaveBeenCalled() + }) }) }) diff --git a/web/app/components/workflow/skill/file-tree/tree/tree-node.tsx b/web/app/components/workflow/skill/file-tree/tree/tree-node.tsx index 119b1b3586..57321855a6 100644 --- a/web/app/components/workflow/skill/file-tree/tree/tree-node.tsx +++ b/web/app/components/workflow/skill/file-tree/tree/tree-node.tsx @@ -82,6 +82,9 @@ const TreeNode = ({ node, style, dragHandle, treeChildren }: TreeNodeProps) => { const handleMoreClick = useCallback((e: React.MouseEvent) => { e.stopPropagation() }, []) + const handleDropdownContentClick = useCallback((e: React.MouseEvent) => { + e.stopPropagation() + }, []) const handleMenuClose = useCallback(() => {}, []) const fileOperations = useFileOperations({ @@ -172,6 +175,7 @@ const TreeNode = ({ node, style, dragHandle, treeChildren }: TreeNodeProps) => { placement="bottom-start" sideOffset={4} popupClassName="min-w-[180px]" + popupProps={{ onClick: handleDropdownContentClick }} >