diff --git a/oxlint-suppressions.json b/oxlint-suppressions.json index c0b968118fccd1..e8cbffd9ee719b 100644 --- a/oxlint-suppressions.json +++ b/oxlint-suppressions.json @@ -1929,14 +1929,6 @@ "count": 1 } }, - "web/app/components/base/tab-slider-new/index.tsx": { - "jsx_a11y/click-events-have-key-events": { - "count": 1 - }, - "jsx_a11y/no-static-element-interactions": { - "count": 1 - } - }, "web/app/components/base/tab-slider-plain/index.tsx": { "jsx_a11y/click-events-have-key-events": { "count": 1 diff --git a/web/app/components/base/tab-slider-new/__tests__/index.spec.tsx b/web/app/components/base/tab-slider-new/__tests__/index.spec.tsx index 6068e9362b6b8a..db28bfdedee5fc 100644 --- a/web/app/components/base/tab-slider-new/__tests__/index.spec.tsx +++ b/web/app/components/base/tab-slider-new/__tests__/index.spec.tsx @@ -3,11 +3,12 @@ import userEvent from '@testing-library/user-event' import TabSliderNew from '../index' describe('TabSliderNew', () => { - it('selects a tab', async () => { + it('exposes and changes the selected tool category', async () => { const user = userEvent.setup() const onChange = vi.fn() render( { />, ) - await user.click(screen.getByText('Active')) + expect(screen.getByRole('group', { name: 'Tool categories' })).toBeInTheDocument() + + const allButton = screen.getByRole('button', { name: 'All' }) + const activeButton = screen.getByRole('button', { name: 'Active' }) + + expect(allButton).toHaveAttribute('aria-pressed', 'true') + expect(activeButton).toHaveAttribute('aria-pressed', 'false') + + await user.click(allButton) + expect(onChange).not.toHaveBeenCalled() + + activeButton.focus() + await user.keyboard('{Enter}') expect(onChange).toHaveBeenCalledWith('active') }) diff --git a/web/app/components/base/tab-slider-new/index.stories.tsx b/web/app/components/base/tab-slider-new/index.stories.tsx index a0e876f987a956..11e13cfbea3b87 100644 --- a/web/app/components/base/tab-slider-new/index.stories.tsx +++ b/web/app/components/base/tab-slider-new/index.stories.tsx @@ -22,7 +22,7 @@ const TabSliderNewDemo = ({ initialValue = 'visual' }: { initialValue?: string } return (
Pill tabs
- +
) } diff --git a/web/app/components/base/tab-slider-new/index.tsx b/web/app/components/base/tab-slider-new/index.tsx index 2c770f399e5d39..1b154ea7aecd5b 100644 --- a/web/app/components/base/tab-slider-new/index.tsx +++ b/web/app/components/base/tab-slider-new/index.tsx @@ -1,5 +1,6 @@ import type { ReactNode } from 'react' import { cn } from '@langgenius/dify-ui/cn' +import { SegmentedControl, SegmentedControlItem } from '@langgenius/dify-ui/segmented-control' type Option = { value: string @@ -7,30 +8,36 @@ type Option = { icon?: ReactNode } type TabSliderProps = { + ariaLabel: string className?: string value: string onChange: (v: string) => void options: Option[] } -function TabSliderNew({ className, value, onChange, options }: TabSliderProps) { +function TabSliderNew({ ariaLabel, className, value, onChange, options }: TabSliderProps) { return ( -
+ { + const nextValue = nextValues[0] + if (nextValue && nextValue !== value) onChange(nextValue) + }} + className={cn(className, 'relative flex gap-0 rounded-none bg-transparent p-0')} + > {options.map((option) => ( -
onChange(option.value)} - className={cn( - 'mr-1 flex h-8 cursor-pointer items-center rounded-lg border-[0.5px] border-transparent px-3 py-1.75 text-[13px] leading-4.5 font-medium text-text-tertiary hover:bg-state-base-hover', - value === option.value && - 'border-components-main-nav-nav-button-border bg-state-base-hover text-components-main-nav-nav-button-text-active shadow-xs', - )} + className="mr-1 h-8 justify-start gap-0 overflow-visible px-3 py-1.75 text-[13px] leading-4.5 font-medium whitespace-normal text-text-tertiary transition-none hover:bg-state-base-hover hover:text-text-tertiary data-pressed:border-components-main-nav-nav-button-border data-pressed:bg-state-base-hover data-pressed:text-components-main-nav-nav-button-text-active data-pressed:shadow-xs" > {option.icon} {option.text} -
+ ))} -
+ ) } diff --git a/web/app/components/integrations/tool-provider-toolbar.tsx b/web/app/components/integrations/tool-provider-toolbar.tsx index c72ef4402c09f7..d29ef57da438f1 100644 --- a/web/app/components/integrations/tool-provider-toolbar.tsx +++ b/web/app/components/integrations/tool-provider-toolbar.tsx @@ -2,6 +2,7 @@ import type { ReactNode } from 'react' import { cn } from '@langgenius/dify-ui/cn' +import { useTranslation } from 'react-i18next' import { SearchInput } from '@/app/components/base/search-input' import TabSliderNew from '@/app/components/base/tab-slider-new' import UpdateSettingDialog from '@/app/components/header/account-setting/update-setting-dialog' @@ -42,6 +43,8 @@ export function ToolProviderToolbar({ onKeywordsChange: (keywords: string) => void onTagsChange: (tags: string[]) => void }) { + const { t } = useTranslation() + return (
{!isRouteCategory && ( - + $['category.tools'], { ns: 'plugin' })} + value={activeTab} + onChange={onCategoryChange} + options={options} + /> )}