Compare commits

..

3 Commits

Author SHA1 Message Date
yyh
81f0d69c83 refactor(web): use css icon for version filter 2026-08-09 19:35:12 +08:00
autofix-ci[bot]
8a61561159 [autofix.ci] apply automated fixes 2026-08-09 11:08:11 +00:00
yyh
88895b6dfa fix(web): use buttons for version filters 2026-08-09 19:03:40 +08:00
6 changed files with 37 additions and 59 deletions

View File

@@ -5269,14 +5269,6 @@
"count": 4
}
},
"web/app/components/workflow/panel/version-history-panel/filter/filter-item.tsx": {
"jsx_a11y/click-events-have-key-events": {
"count": 1
},
"jsx_a11y/no-static-element-interactions": {
"count": 1
}
},
"web/app/components/workflow/panel/version-history-panel/index.tsx": {
"jsx_a11y/click-events-have-key-events": {
"count": 2

View File

@@ -105,8 +105,10 @@ describe('ExternalAPIPanel', () => {
})
it('should render close button', () => {
render(<ExternalAPIPanel {...defaultProps} />)
expect(screen.getByRole('button', { name: 'common.operation.close' })).toBeInTheDocument()
const { container } = render(<ExternalAPIPanel {...defaultProps} />)
const closeButton =
container.querySelector('[class*="action-button"]') || screen.getAllByRole('button')[0]
expect(closeButton)!.toBeInTheDocument()
})
})
@@ -169,7 +171,11 @@ describe('ExternalAPIPanel', () => {
it('should call onClose when close button is clicked', () => {
const onClose = vi.fn()
render(<ExternalAPIPanel canManageExternalKnowledgeApi={true} onClose={onClose} />)
fireEvent.click(screen.getByRole('button', { name: 'common.operation.close' }))
// Find the close button (ActionButton with close icon)
const buttons = screen.getAllByRole('button')
const closeButton =
buttons.find((btn) => btn.querySelector('svg[class*="ri-close"]')) || buttons[0]
fireEvent.click(closeButton!)
expect(onClose).toHaveBeenCalledTimes(1)
})

View File

@@ -1,6 +1,8 @@
import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiAddLine, RiBookOpenLine, RiCloseLine } from '@remixicon/react'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import Loading from '@/app/components/base/loading'
@@ -14,7 +16,10 @@ type ExternalAPIPanelProps = {
onClose: () => void
}
const ExternalAPIPanel = ({ canManageExternalKnowledgeApi, onClose }: ExternalAPIPanelProps) => {
const ExternalAPIPanel: React.FC<ExternalAPIPanelProps> = ({
canManageExternalKnowledgeApi,
onClose,
}) => {
const { t } = useTranslation()
const docLink = useDocLink()
const { setShowExternalKnowledgeAPIModal } = useModalContext()
@@ -59,18 +64,15 @@ const ExternalAPIPanel = ({ canManageExternalKnowledgeApi, onClose }: ExternalAP
href={docLink('/use-dify/knowledge/external-knowledge-api')}
target="_blank"
>
<span aria-hidden className="i-ri-book-open-line size-3 text-text-accent" />
<RiBookOpenLine className="size-3 text-text-accent" />
<div className="grow body-xs-regular text-text-accent">
{t(($) => $.externalAPIPanelDocumentation, { ns: 'dataset' })}
</div>
</a>
</div>
<div className="flex items-center">
<ActionButton
aria-label={t(($) => $['operation.close'], { ns: 'common' })}
onClick={() => onClose()}
>
<span aria-hidden className="i-ri-close-line size-4 text-text-tertiary" />
<ActionButton onClick={() => onClose()}>
<RiCloseLine className="size-4 text-text-tertiary" />
</ActionButton>
</div>
</div>
@@ -81,10 +83,7 @@ const ExternalAPIPanel = ({ canManageExternalKnowledgeApi, onClose }: ExternalAP
className="flex items-center justify-center px-3 py-2"
onClick={handleOpenExternalAPIModal}
>
<span
aria-hidden
className="i-ri-add-line size-4 text-components-button-primary-text"
/>
<RiAddLine className="size-4 text-components-button-primary-text" />
<div className="system-sm-medium text-components-button-primary-text">
{t(($) => $.createExternalAPI, { ns: 'dataset' })}
</div>

View File

@@ -7,7 +7,7 @@ describe('FilterItem', () => {
it('renders the label, fires selection, and shows the check mark when selected', async () => {
const user = userEvent.setup()
const onClick = vi.fn()
const { container } = render(
render(
<FilterItem
item={{
key: WorkflowVersionFilterOptions.onlyYours,
@@ -18,9 +18,12 @@ describe('FilterItem', () => {
/>,
)
await user.click(screen.getByText('Only yours'))
const option = screen.getByRole('button', { name: 'Only yours' })
expect(option).toHaveAttribute('aria-pressed', 'true')
option.focus()
await user.keyboard(' ')
expect(onClick).toHaveBeenCalledWith(WorkflowVersionFilterOptions.onlyYours)
expect(container.querySelector('svg')).not.toBeNull()
})
})

View File

@@ -1,7 +1,6 @@
import { fireEvent, render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { WorkflowVersionFilterOptions } from '../../../../types'
import FilterItem from '../filter-item'
import FilterSwitch from '../filter-switch'
import Filter from '../index'
@@ -29,32 +28,6 @@ describe('VersionHistory Filter Components', () => {
})
})
// Filter items should show the current selection and forward the option key.
describe('FilterItem', () => {
it('should call onClick with the selected filter key', async () => {
const user = userEvent.setup()
const onClick = vi.fn()
const { container } = render(
<FilterItem
item={{
key: WorkflowVersionFilterOptions.onlyYours,
name: 'Only Yours',
}}
isSelected
onClick={onClick}
/>,
)
expect(screen.getByText('Only Yours')).toBeInTheDocument()
expect(container.querySelector('svg')).toBeInTheDocument()
await user.click(screen.getByText('Only Yours'))
expect(onClick).toHaveBeenCalledWith(WorkflowVersionFilterOptions.onlyYours)
})
})
// The composed filter popover should open, list options, and delegate actions.
describe('Filter', () => {
it('should open the menu and forward option and switch actions', async () => {
@@ -79,7 +52,9 @@ describe('VersionHistory Filter Components', () => {
expect(screen.getByText('workflow.versionHistory.filter.all')).toBeInTheDocument()
expect(screen.getByText('workflow.versionHistory.filter.onlyYours')).toBeInTheDocument()
await user.click(screen.getByText('workflow.versionHistory.filter.onlyYours'))
await user.click(
screen.getByRole('button', { name: 'workflow.versionHistory.filter.onlyYours' }),
)
expect(onClickFilterItem).toHaveBeenCalledWith(WorkflowVersionFilterOptions.onlyYours)
fireEvent.click(screen.getByRole('switch'))

View File

@@ -1,6 +1,5 @@
import type { FC } from 'react'
import type { WorkflowVersionFilterOptions } from '../../../types'
import { RiCheckLine } from '@remixicon/react'
import * as React from 'react'
type FilterItemProps = {
@@ -14,15 +13,19 @@ type FilterItemProps = {
const FilterItem: FC<FilterItemProps> = ({ item, isSelected = false, onClick }) => {
return (
<div
className="flex cursor-pointer items-center justify-between gap-x-1 rounded-lg px-2 py-1.5 hover:bg-state-base-hover"
<button
type="button"
aria-pressed={isSelected}
className="flex w-full cursor-pointer appearance-none items-center justify-between gap-x-1 rounded-lg px-2 py-1.5 text-left outline-hidden hover:bg-state-base-hover focus-visible:ring-2 focus-visible:ring-state-accent-solid"
onClick={() => {
onClick(item.key)
}}
>
<div className="flex-1 system-md-regular text-text-primary">{item.name}</div>
{isSelected && <RiCheckLine className="size-4 shrink-0 text-text-accent" />}
</div>
<span className="flex-1 system-md-regular text-text-primary">{item.name}</span>
{isSelected && (
<span aria-hidden className="i-ri-check-line size-4 shrink-0 text-text-accent" />
)}
</button>
)
}