Compare commits

...

4 Commits

Author SHA1 Message Date
yyh
4e4b65efe1 refactor(web): remove external API card React.FC 2026-08-09 16:59:17 +08:00
yyh
3092b775b8 refactor(web): use css icons for external API card 2026-08-09 16:58:18 +08:00
yyh
1f6f8d1978 test(web): query external API card actions by name 2026-08-09 16:56:52 +08:00
yyh
5d56b168d2 fix(web): name external API card actions 2026-08-09 16:55:29 +08:00
2 changed files with 51 additions and 47 deletions

View File

@@ -88,23 +88,20 @@ describe('ExternalKnowledgeAPICard', () => {
})
it('should render edit and delete buttons', () => {
const { container } = render(<ExternalKnowledgeAPICard {...defaultProps} />)
const buttons = container.querySelectorAll('button')
expect(buttons.length).toBe(2)
render(<ExternalKnowledgeAPICard {...defaultProps} />)
expect(screen.getByRole('button', { name: 'common.operation.edit' })).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'common.operation.delete' })).toBeInTheDocument()
})
it('should hide edit and delete buttons when external knowledge API management is unavailable', () => {
const { container } = render(
<ExternalKnowledgeAPICard {...defaultProps} canManageExternalKnowledgeApi={false} />,
)
render(<ExternalKnowledgeAPICard {...defaultProps} canManageExternalKnowledgeApi={false} />)
expect(container.querySelectorAll('button').length).toBe(0)
})
it('should render API connection icon', () => {
const { container } = render(<ExternalKnowledgeAPICard {...defaultProps} />)
const icon = container.querySelector('svg')
expect(icon)!.toBeInTheDocument()
expect(
screen.queryByRole('button', { name: 'common.operation.edit' }),
).not.toBeInTheDocument()
expect(
screen.queryByRole('button', { name: 'common.operation.delete' }),
).not.toBeInTheDocument()
})
})
@@ -125,9 +122,8 @@ describe('ExternalKnowledgeAPICard', () => {
}
vi.mocked(fetchExternalAPI).mockResolvedValue(mockResponse)
const { container } = render(<ExternalKnowledgeAPICard {...defaultProps} />)
const buttons = container.querySelectorAll('button')
const editButton = buttons[0]
render(<ExternalKnowledgeAPICard {...defaultProps} />)
const editButton = screen.getByRole('button', { name: 'common.operation.edit' })
fireEvent.click(editButton!)
@@ -153,9 +149,8 @@ describe('ExternalKnowledgeAPICard', () => {
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
vi.mocked(fetchExternalAPI).mockRejectedValue(new Error('Fetch failed'))
const { container } = render(<ExternalKnowledgeAPICard {...defaultProps} />)
const buttons = container.querySelectorAll('button')
const editButton = buttons[0]
render(<ExternalKnowledgeAPICard {...defaultProps} />)
const editButton = screen.getByRole('button', { name: 'common.operation.edit' })
fireEvent.click(editButton!)
@@ -185,8 +180,8 @@ describe('ExternalKnowledgeAPICard', () => {
}
vi.mocked(fetchExternalAPI).mockResolvedValue(mockResponse)
const { container } = render(<ExternalKnowledgeAPICard {...defaultProps} />)
const editButton = container.querySelectorAll('button')[0]
render(<ExternalKnowledgeAPICard {...defaultProps} />)
const editButton = screen.getByRole('button', { name: 'common.operation.edit' })
fireEvent.click(editButton!)
@@ -234,8 +229,8 @@ describe('ExternalKnowledgeAPICard', () => {
}
vi.mocked(fetchExternalAPI).mockResolvedValue(mockResponse)
const { container } = render(<ExternalKnowledgeAPICard {...defaultProps} />)
const editButton = container.querySelectorAll('button')[0]
render(<ExternalKnowledgeAPICard {...defaultProps} />)
const editButton = screen.getByRole('button', { name: 'common.operation.edit' })
fireEvent.click(editButton!)
@@ -254,9 +249,8 @@ describe('ExternalKnowledgeAPICard', () => {
it('should check usage and show confirm dialog when delete button is clicked', async () => {
vi.mocked(checkUsageExternalAPI).mockResolvedValue({ is_using: false, count: 0 })
const { container } = render(<ExternalKnowledgeAPICard {...defaultProps} />)
const buttons = container.querySelectorAll('button')
const deleteButton = buttons[1]
render(<ExternalKnowledgeAPICard {...defaultProps} />)
const deleteButton = screen.getByRole('button', { name: 'common.operation.delete' })
fireEvent.click(deleteButton!)
@@ -273,8 +267,8 @@ describe('ExternalKnowledgeAPICard', () => {
it('should show usage count in confirm dialog when API is in use', async () => {
vi.mocked(checkUsageExternalAPI).mockResolvedValue({ is_using: true, count: 3 })
const { container } = render(<ExternalKnowledgeAPICard {...defaultProps} />)
const deleteButton = container.querySelectorAll('button')[1]
render(<ExternalKnowledgeAPICard {...defaultProps} />)
const deleteButton = screen.getByRole('button', { name: 'common.operation.delete' })
fireEvent.click(deleteButton!)
@@ -287,8 +281,8 @@ describe('ExternalKnowledgeAPICard', () => {
vi.mocked(checkUsageExternalAPI).mockResolvedValue({ is_using: false, count: 0 })
vi.mocked(deleteExternalAPI).mockResolvedValue({ result: 'success' })
const { container } = render(<ExternalKnowledgeAPICard {...defaultProps} />)
const deleteButton = container.querySelectorAll('button')[1]
render(<ExternalKnowledgeAPICard {...defaultProps} />)
const deleteButton = screen.getByRole('button', { name: 'common.operation.delete' })
fireEvent.click(deleteButton!)
@@ -310,8 +304,8 @@ describe('ExternalKnowledgeAPICard', () => {
it('should close confirm dialog when cancel is clicked', async () => {
vi.mocked(checkUsageExternalAPI).mockResolvedValue({ is_using: false, count: 0 })
const { container } = render(<ExternalKnowledgeAPICard {...defaultProps} />)
const deleteButton = container.querySelectorAll('button')[1]
render(<ExternalKnowledgeAPICard {...defaultProps} />)
const deleteButton = screen.getByRole('button', { name: 'common.operation.delete' })
fireEvent.click(deleteButton!)
@@ -332,8 +326,8 @@ describe('ExternalKnowledgeAPICard', () => {
vi.mocked(checkUsageExternalAPI).mockResolvedValue({ is_using: false, count: 0 })
vi.mocked(deleteExternalAPI).mockRejectedValue(new Error('Delete failed'))
const { container } = render(<ExternalKnowledgeAPICard {...defaultProps} />)
const deleteButton = container.querySelectorAll('button')[1]
render(<ExternalKnowledgeAPICard {...defaultProps} />)
const deleteButton = screen.getByRole('button', { name: 'common.operation.delete' })
fireEvent.click(deleteButton!)
@@ -358,8 +352,8 @@ describe('ExternalKnowledgeAPICard', () => {
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
vi.mocked(checkUsageExternalAPI).mockRejectedValue(new Error('Check failed'))
const { container } = render(<ExternalKnowledgeAPICard {...defaultProps} />)
const deleteButton = container.querySelectorAll('button')[1]
render(<ExternalKnowledgeAPICard {...defaultProps} />)
const deleteButton = screen.getByRole('button', { name: 'common.operation.delete' })
fireEvent.click(deleteButton!)
@@ -394,8 +388,8 @@ describe('ExternalKnowledgeAPICard', () => {
vi.mocked(checkUsageExternalAPI).mockResolvedValue({ is_using: false, count: 0 })
vi.mocked(deleteExternalAPI).mockResolvedValue({ result: 'error' })
const { container } = render(<ExternalKnowledgeAPICard {...defaultProps} />)
const deleteButton = container.querySelectorAll('button')[1]
render(<ExternalKnowledgeAPICard {...defaultProps} />)
const deleteButton = screen.getByRole('button', { name: 'common.operation.delete' })
fireEvent.click(deleteButton!)

View File

@@ -9,13 +9,10 @@ import {
AlertDialogDescription,
AlertDialogTitle,
} from '@langgenius/dify-ui/alert-dialog'
import { RiDeleteBinLine, RiEditLine } from '@remixicon/react'
import { useQueryClient } from '@tanstack/react-query'
import * as React from 'react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import { ApiConnectionMod } from '@/app/components/base/icons/src/vender/solid/development'
import { useModalContext } from '@/context/modal-context'
import { consoleQuery } from '@/service/client'
import {
@@ -30,10 +27,10 @@ type ExternalKnowledgeAPICardProps = {
canManageExternalKnowledgeApi: boolean
}
const ExternalKnowledgeAPICard: React.FC<ExternalKnowledgeAPICardProps> = ({
const ExternalKnowledgeAPICard = ({
api,
canManageExternalKnowledgeApi,
}) => {
}: ExternalKnowledgeAPICardProps) => {
const { setShowExternalKnowledgeAPIModal } = useModalContext()
const [showConfirm, setShowConfirm] = useState(false)
const [isHovered, setIsHovered] = useState(false)
@@ -125,23 +122,36 @@ const ExternalKnowledgeAPICard: React.FC<ExternalKnowledgeAPICardProps> = ({
>
<div className="flex grow flex-col items-start justify-center gap-1.5 py-1">
<div className="flex items-center gap-1 self-stretch text-text-secondary">
<ApiConnectionMod className="size-4" />
<span
aria-hidden
className="i-custom-vender-solid-development-api-connection-mod size-4"
/>
<div className="system-sm-medium">{api.name}</div>
</div>
<div className="self-stretch system-xs-regular text-text-tertiary">{endpoint}</div>
</div>
{canManageExternalKnowledgeApi && (
<div className="flex items-start gap-1">
<ActionButton onClick={handleEditClick}>
<RiEditLine className="size-4 text-text-tertiary hover:text-text-secondary" />
<ActionButton
aria-label={t(($) => $['operation.edit'], { ns: 'common' })}
onClick={handleEditClick}
>
<span
aria-hidden
className="i-ri-edit-line size-4 text-text-tertiary hover:text-text-secondary"
/>
</ActionButton>
<ActionButton
aria-label={t(($) => $['operation.delete'], { ns: 'common' })}
className="hover:bg-state-destructive-hover"
onClick={handleDeleteClick}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<RiDeleteBinLine className="size-4 text-text-tertiary hover:text-text-destructive" />
<span
aria-hidden
className="i-ri-delete-bin-line size-4 text-text-tertiary hover:text-text-destructive"
/>
</ActionButton>
</div>
)}