Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions eslint-suppressions.json
Original file line number Diff line number Diff line change
Expand Up @@ -4519,9 +4519,6 @@
}
},
"web/app/components/workflow/nodes/tool/components/tool-form/item.tsx": {
"no-restricted-imports": {
"count": 1
},
"ts/no-explicit-any": {
"count": 1
}
Expand Down
9 changes: 9 additions & 0 deletions web/app/components/apps/__tests__/app-card.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,15 @@ describe('AppCard', () => {
expect(screen.getByTestId('dropdown-menu')).toHaveAttribute('data-modal', 'false')
})

it('should reveal operations trigger when card receives keyboard focus', () => {
render(<AppCard app={mockApp} />)
const operationsTriggerWrapper = screen.getByTestId('dropdown-menu-trigger').closest('.absolute')

expect(operationsTriggerWrapper).toHaveClass('group-focus-within:pointer-events-auto')
expect(operationsTriggerWrapper).toHaveClass('group-focus-within:opacity-100')
expect(screen.getByTestId('dropdown-menu-trigger')).toHaveClass('focus-visible:ring-1')
})

it('should show edit option when dropdown menu is opened', async () => {
render(<AppCard app={mockApp} />)

Expand Down
6 changes: 3 additions & 3 deletions web/app/components/apps/app-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ const AppCard = ({ app, onlineUsers = [], onRefresh, onOpenTagManagement = () =>
e.preventDefault()
getRedirection(isCurrentWorkspaceEditor, app, push)
}}
className="group relative col-span-1 inline-flex h-[160px] cursor-pointer flex-col rounded-xl border border-solid border-components-card-border bg-components-card-bg shadow-sm transition-all duration-200 ease-in-out hover:shadow-lg"
className="group relative col-span-1 inline-flex h-[160px] cursor-pointer flex-col rounded-xl border border-solid border-components-card-border bg-components-card-bg shadow-sm transition-shadow duration-200 ease-in-out hover:shadow-lg"
>
<div className="flex h-[66px] shrink-0 grow-0 items-center gap-3 px-[14px] pt-[14px] pb-3">
<div className="relative shrink-0">
Expand Down Expand Up @@ -524,7 +524,7 @@ const AppCard = ({ app, onlineUsers = [], onRefresh, onOpenTagManagement = () =>
'absolute top-1/2 right-[6px] flex -translate-y-1/2 items-center transition-opacity',
isOperationsMenuOpen
? 'pointer-events-auto opacity-100'
: 'pointer-events-none opacity-0 group-hover:pointer-events-auto group-hover:opacity-100',
: 'pointer-events-none opacity-0 group-focus-within:pointer-events-auto group-focus-within:opacity-100 group-hover:pointer-events-auto group-hover:opacity-100',
)}
>
<div className="mx-1 h-[14px] w-px shrink-0 bg-divider-regular" />
Expand All @@ -533,7 +533,7 @@ const AppCard = ({ app, onlineUsers = [], onRefresh, onOpenTagManagement = () =>
aria-label={t('operation.more', { ns: 'common' })}
className={cn(
isOperationsMenuOpen ? 'bg-state-base-hover shadow-none' : 'bg-transparent',
'flex h-8 w-8 items-center justify-center rounded-md border-none p-2 hover:bg-state-base-hover',
'flex h-8 w-8 items-center justify-center rounded-md border-none p-2 hover:bg-state-base-hover focus-visible:bg-state-base-hover focus-visible:ring-1 focus-visible:ring-components-input-border-active focus-visible:ring-inset',
)}
onClick={(e) => {
e.stopPropagation()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ describe('tool/tool-form/item', () => {
} as unknown as SchemaRoot,
})

const { container } = render(
render(
<ToolFormItem
readOnly={false}
nodeId="tool-node"
Expand All @@ -182,7 +182,8 @@ describe('tool/tool-form/item', () => {
/>,
)

fireEvent.mouseEnter(container.querySelector('svg')?.parentElement as HTMLElement)
const infotipTrigger = screen.getByRole('button', { name: 'Select from tools' })
fireEvent.click(infotipTrigger)
expect(screen.getByText('Select from tools'))!.toBeInTheDocument()

fireEvent.click(screen.getByRole('button', { name: 'JSON Schema' }))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
RiBracesLine,
} from '@remixicon/react'
import { useBoolean } from 'ahooks'
import Tooltip from '@/app/components/base/tooltip'
import { Infotip } from '@/app/components/base/infotip'
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { SchemaModal } from '@/app/components/plugins/plugin-detail-panel/tool-selector/components'
Expand Down Expand Up @@ -100,15 +100,13 @@ const ToolFormItem: FC<Props> = ({
<div className="ml-1 system-xs-regular text-text-destructive-secondary">*</div>
)}
{!showDescription && tooltip && (
<Tooltip
popupContent={(
<div className="w-[200px]">
{tooltip[language] || tooltip.en_US}
</div>
)}
triggerClassName="ml-1 w-4 h-4"
asChild={false}
/>
<Infotip
aria-label={tooltip[language] || tooltip.en_US}
className="ml-1"
popupClassName="w-[200px]"
>
{tooltip[language] || tooltip.en_US}
</Infotip>
)}
{showSchemaButton && (
<>
Expand Down
27 changes: 16 additions & 11 deletions web/features/tag-management/__tests__/dataset-card-tags.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,15 @@ import { fireEvent, render, screen } from '@testing-library/react'
import { describe, expect, it, vi } from 'vitest'
import { DatasetCardTags } from '../components/dataset-card-tags'

// Mock TagSelector as it's a complex component from base
vi.mock('@/features/tag-management/components/tag-selector', () => ({
TagSelector: ({ selectedTagIds, selectedTags, onOpenTagManagement }: {
selectedTagIds: string[]
selectedTags: Tag[]
TagSelector: ({ value, onOpenTagManagement }: {
value: Tag[]
onOpenTagManagement?: () => void
}) => (
<div data-testid="tag-selector">
<div data-testid="tag-values">{selectedTagIds.join(',')}</div>
<div data-testid="tag-values">{value.map(tag => tag.id).join(',')}</div>
<div data-testid="selected-count">
{selectedTags.length}
{value.length}
{' '}
tags
</div>
Expand Down Expand Up @@ -75,7 +73,9 @@ describe('DatasetCardTags', () => {
const onClick = vi.fn()
const { container } = render(<DatasetCardTags {...defaultProps} onClick={onClick} />)

const wrapper = container.firstChild as HTMLElement
const wrapper = container.firstElementChild
if (!wrapper)
throw new Error('Expected dataset card tag wrapper')
fireEvent.click(wrapper)

expect(onClick).toHaveBeenCalledTimes(1)
Expand All @@ -94,13 +94,17 @@ describe('DatasetCardTags', () => {
describe('Styles', () => {
it('should have opacity class when embedding is not available', () => {
const { container } = render(<DatasetCardTags {...defaultProps} embeddingAvailable={false} />)
const wrapper = container.firstChild as HTMLElement
const wrapper = container.firstElementChild
if (!wrapper)
throw new Error('Expected dataset card tag wrapper')
expect(wrapper).toHaveClass('opacity-30')
})

it('should not have opacity class when embedding is available', () => {
const { container } = render(<DatasetCardTags {...defaultProps} embeddingAvailable={true} />)
const wrapper = container.firstChild as HTMLElement
const wrapper = container.firstElementChild
if (!wrapper)
throw new Error('Expected dataset card tag wrapper')
expect(wrapper).not.toHaveClass('opacity-30')
})

Expand All @@ -109,6 +113,7 @@ describe('DatasetCardTags', () => {
const maskDiv = container.querySelector('.bg-tag-selector-mask-bg')
expect(maskDiv).toBeInTheDocument()
expect(maskDiv).toHaveClass('group-hover/tag-area:hidden')
expect(maskDiv).toHaveClass('group-focus-within/tag-area:hidden')
expect(maskDiv).toHaveClass('group-hover:bg-tag-selector-mask-hover-bg')
})

Expand Down Expand Up @@ -139,10 +144,10 @@ describe('DatasetCardTags', () => {
})

it('should handle many tags', () => {
const manyTags: Tag[] = Array.from({ length: 20 }, (_, i) => ({
const manyTags: Tag[] = Array.from({ length: 20 }, (_, i): Tag => ({
id: `tag-${i}`,
name: `Tag ${i}`,
type: 'knowledge' as const,
type: 'knowledge',
binding_count: 0,
}))
render(<DatasetCardTags {...defaultProps} tags={manyTags} />)
Expand Down
16 changes: 8 additions & 8 deletions web/features/tag-management/__tests__/tag-filter.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ const defaultProps = {
// Helper: the i18n mock renders "ns.key" format (dot-separated)
const i18n = {
placeholder: 'common.tag.placeholder',
selectorPlaceholder: 'common.tag.selectorPlaceholder',
operationClear: 'common.operation.clear',
noTag: 'common.tag.noTag',
manageTags: 'common.tag.manageTags',
}
Expand Down Expand Up @@ -158,11 +160,9 @@ describe('TagFilter', () => {
await user.click(screen.getByText('Frontend'))

// The Check icon should be rendered for the selected tag
const tagItem = screen.getByTitle('Frontend')
const tagItem = screen.getByRole('option', { name: /Frontend/i })
expect(tagItem).toBeInTheDocument()
// The parent container of the tag has a Check SVG sibling
const checkIcons = screen.getAllByTestId('tag-filter-selected-icon')
expect(checkIcons?.length).toBeGreaterThanOrEqual(1)
expect(tagItem).toHaveAttribute('aria-selected', 'true')
})

it('should clear all selected tags when clear button is clicked', async () => {
Expand Down Expand Up @@ -197,7 +197,7 @@ describe('TagFilter', () => {

await user.click(screen.getByText(i18n.placeholder))

const searchInput = screen.getByRole('textbox')
const searchInput = screen.getByRole('combobox', { name: i18n.selectorPlaceholder })
await user.type(searchInput, 'Front')

expect(screen.getByText('Frontend')).toBeInTheDocument()
Expand All @@ -212,7 +212,7 @@ describe('TagFilter', () => {

await user.click(screen.getByText(i18n.placeholder))

const searchInput = screen.getByRole('textbox')
const searchInput = screen.getByRole('combobox', { name: i18n.selectorPlaceholder })
await user.type(searchInput, 'NonExistentTag')

expect(screen.getByText(i18n.noTag)).toBeInTheDocument()
Expand All @@ -225,12 +225,12 @@ describe('TagFilter', () => {

await user.click(screen.getByText(i18n.placeholder))

const searchInput = screen.getByRole('textbox')
const searchInput = screen.getByRole('combobox', { name: i18n.selectorPlaceholder })
await user.type(searchInput, 'Front')

expect(screen.queryByText('Backend')).not.toBeInTheDocument()

const clearButton = screen.getByTestId('input-clear')
const clearButton = screen.getByRole('button', { name: i18n.operationClear })
await user.click(clearButton)

expect(searchInput).toHaveValue('')
Expand Down
Loading
Loading