From cae9923e5a84aaf1dcbc14912314e973546fbf78 Mon Sep 17 00:00:00 2001 From: Joel Date: Tue, 19 May 2026 17:54:52 +0800 Subject: [PATCH] fix: prevent agent tool info popover from jumping on close (#36389) Co-authored-by: yyh --- .../agent-tools/__tests__/index.spec.tsx | 22 ++++-- .../config/agent/agent-tools/index.tsx | 71 ++++++------------- 2 files changed, 38 insertions(+), 55 deletions(-) diff --git a/web/app/components/app/configuration/config/agent/agent-tools/__tests__/index.spec.tsx b/web/app/components/app/configuration/config/agent/agent-tools/__tests__/index.spec.tsx index 628e34bbe0..c804857f6a 100644 --- a/web/app/components/app/configuration/config/agent/agent-tools/__tests__/index.spec.tsx +++ b/web/app/components/app/configuration/config/agent/agent-tools/__tests__/index.spec.tsx @@ -12,7 +12,6 @@ import type { ModelConfig } from '@/models/debug' import type { AgentTool } from '@/types/app' import { act, render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import copy from 'copy-to-clipboard' import * as React from 'react' import { useEffect, @@ -27,6 +26,7 @@ import { } from '@/config' import ConfigContext from '@/context/debug-configuration' import { ModelModeType } from '@/types/app' +import { writeTextToClipboard } from '@/utils/clipboard' import AgentTools from '../index' const formattingDispatcherMock = vi.fn() @@ -99,9 +99,11 @@ vi.mock('../setting-built-in-tool', () => ({ default: (props: SettingBuiltInToolProps) => , })) -vi.mock('copy-to-clipboard') +vi.mock('@/utils/clipboard', () => ({ + writeTextToClipboard: vi.fn().mockResolvedValue(undefined), +})) -const copyMock = copy as Mock +const writeTextToClipboardMock = writeTextToClipboard as Mock const createToolParameter = (overrides?: Partial): ToolParameter => ({ name: 'api_key', @@ -241,7 +243,7 @@ const renderAgentTools = (initialTools?: AgentTool[]) => { const hoverInfoIcon = async (rowIndex = 0) => { const rows = document.querySelectorAll('.group') - const infoTrigger = rows.item(rowIndex)?.querySelector('[data-testid="tool-info-tooltip"]') + const infoTrigger = rows.item(rowIndex)?.querySelector('[aria-label="search"]') if (!infoTrigger) throw new Error('Info trigger not found') await userEvent.hover(infoTrigger as HTMLElement) @@ -343,13 +345,21 @@ describe('AgentTools', () => { expect(screen.getByText('Translate Tool')).toBeInTheDocument() }) - it('should copy tool name from tooltip action', async () => { + it('should copy tool name from infotip action', async () => { renderAgentTools() await hoverInfoIcon() const copyButton = await screen.findByText('tools.copyToolName') await userEvent.click(copyButton) - expect(copyMock).toHaveBeenCalledWith('search') + expect(writeTextToClipboardMock).toHaveBeenCalledWith('search') + }) + + it('should expose the tool name infotip trigger', () => { + renderAgentTools() + + const infoTrigger = screen.getByRole('button', { name: 'search' }) + + expect(infoTrigger).toBeInTheDocument() }) it('should toggle tool enabled state via switch', async () => { diff --git a/web/app/components/app/configuration/config/agent/agent-tools/index.tsx b/web/app/components/app/configuration/config/agent/agent-tools/index.tsx index 8ad9ad1f8f..b45184c095 100644 --- a/web/app/components/app/configuration/config/agent/agent-tools/index.tsx +++ b/web/app/components/app/configuration/config/agent/agent-tools/index.tsx @@ -12,9 +12,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/too import { RiDeleteBinLine, RiEqualizer2Line, - RiInformation2Line, } from '@remixicon/react' -import copy from 'copy-to-clipboard' import { produce } from 'immer' import * as React from 'react' import { useCallback, useMemo, useState } from 'react' @@ -25,6 +23,7 @@ import OperationBtn from '@/app/components/app/configuration/base/operation-btn' import AppIcon from '@/app/components/base/app-icon' import { DefaultToolIcon } from '@/app/components/base/icons/src/public/other' import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' +import { Infotip } from '@/app/components/base/infotip' import Indicator from '@/app/components/header/indicator' import { CollectionType } from '@/app/components/tools/types' import { addDefaultValue, toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema' @@ -34,6 +33,7 @@ import ConfigContext from '@/context/debug-configuration' import { useMittContextSelector } from '@/context/mitt-context' import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools } from '@/service/use-tools' import { canFindTool } from '@/utils' +import { writeTextToClipboard } from '@/utils/clipboard' import { useFormattingChangedDispatcher } from '../../../debug/hooks' import SettingBuiltInTool from './setting-built-in-tool' @@ -156,23 +156,9 @@ const AgentTools: FC = () => { title={(
{t('agent.tools.name', { ns: 'appDebug' })}
- - - - - )} - /> - - {t('agent.tools.description', { ns: 'appDebug' })} - - + + {t('agent.tools.description', { ns: 'appDebug' })} +
)} headerRight={( @@ -228,36 +214,23 @@ const AgentTools: FC = () => { {getProviderShowName(item)} {item.tool_label} {!item.isDeleted && !readonly && ( - - - - - - )} - /> - - -
-
{item.tool_name}
-
{t('toolNameUsageTip', { ns: 'tools' })}
- -
-
-
+ +
+
{item.tool_name}
+
{t('toolNameUsageTip', { ns: 'tools' })}
+ +
+
)}