mirror of
https://github.com/langgenius/dify.git
synced 2026-05-26 22:00:53 -04:00
fix: prevent agent tool info popover from jumping on close (#36389)
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
This commit is contained in:
@@ -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) => <SettingBuiltInToolMock {...props} />,
|
||||
}))
|
||||
|
||||
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>): 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 () => {
|
||||
|
||||
@@ -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={(
|
||||
<div className="flex items-center">
|
||||
<div className="mr-1">{t('agent.tools.name', { ns: 'appDebug' })}</div>
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
openOnHover
|
||||
aria-label={t('agent.tools.description', { ns: 'appDebug' })}
|
||||
render={(
|
||||
<button
|
||||
type="button"
|
||||
className="flex h-4 w-4 shrink-0 items-center justify-center rounded-sm p-px outline-hidden hover:bg-state-base-hover focus-visible:ring-1 focus-visible:ring-components-input-border-hover"
|
||||
>
|
||||
<span aria-hidden className="i-ri-question-line h-3.5 w-3.5 text-text-quaternary hover:text-text-tertiary" />
|
||||
</button>
|
||||
)}
|
||||
/>
|
||||
<PopoverContent popupClassName="w-[180px] px-3 py-2 system-xs-regular text-text-tertiary">
|
||||
{t('agent.tools.description', { ns: 'appDebug' })}
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<Infotip aria-label={t('agent.tools.description', { ns: 'appDebug' })}>
|
||||
{t('agent.tools.description', { ns: 'appDebug' })}
|
||||
</Infotip>
|
||||
</div>
|
||||
)}
|
||||
headerRight={(
|
||||
@@ -228,36 +214,23 @@ const AgentTools: FC = () => {
|
||||
<span className="pr-1.5 system-xs-medium text-text-secondary">{getProviderShowName(item)}</span>
|
||||
<span className="text-text-tertiary">{item.tool_label}</span>
|
||||
{!item.isDeleted && !readonly && (
|
||||
<Popover>
|
||||
<span className="h-4 w-4">
|
||||
<PopoverTrigger
|
||||
openOnHover
|
||||
aria-label={item.tool_name}
|
||||
render={(
|
||||
<button
|
||||
type="button"
|
||||
className="ml-0.5 hidden h-4 w-4 items-center justify-center rounded-sm outline-hidden group-hover:inline-flex hover:bg-state-base-hover focus-visible:inline-flex focus-visible:ring-1 focus-visible:ring-components-input-border-hover"
|
||||
data-testid="tool-info-tooltip"
|
||||
>
|
||||
<RiInformation2Line className="h-4 w-4 text-text-tertiary" />
|
||||
</button>
|
||||
)}
|
||||
/>
|
||||
</span>
|
||||
<PopoverContent popupClassName="w-[180px] px-3 py-2 system-xs-regular">
|
||||
<div className="w-[180px]">
|
||||
<div className="mb-1.5 text-text-secondary">{item.tool_name}</div>
|
||||
<div className="mb-1.5 text-text-tertiary">{t('toolNameUsageTip', { ns: 'tools' })}</div>
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer rounded-sm border-none bg-transparent p-0 text-left text-text-accent outline-hidden hover:underline focus-visible:ring-1 focus-visible:ring-components-input-border-hover"
|
||||
onClick={() => copy(item.tool_name)}
|
||||
>
|
||||
{t('copyToolName', { ns: 'tools' })}
|
||||
</button>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<Infotip
|
||||
aria-label={item.tool_name}
|
||||
className="ml-0.5 opacity-0 group-hover:opacity-100 focus-visible:opacity-100"
|
||||
popupClassName="w-[220px]"
|
||||
>
|
||||
<div>
|
||||
<div className="mb-1.5 text-text-secondary">{item.tool_name}</div>
|
||||
<div className="mb-1.5 text-text-tertiary">{t('toolNameUsageTip', { ns: 'tools' })}</div>
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer rounded-sm border-none bg-transparent p-0 text-left text-text-accent outline-hidden hover:underline focus-visible:ring-1 focus-visible:ring-components-input-border-hover"
|
||||
onClick={() => void writeTextToClipboard(item.tool_name)}
|
||||
>
|
||||
{t('copyToolName', { ns: 'tools' })}
|
||||
</button>
|
||||
</div>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user