Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
8 changes: 0 additions & 8 deletions oxlint-suppressions.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 15 additions & 2 deletions web/app/components/base/tab-slider-new/__tests__/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<TabSliderNew
ariaLabel="Tool categories"
value="all"
options={[
{ value: 'all', text: 'All' },
Expand All @@ -17,7 +18,19 @@ describe('TabSliderNew', () => {
/>,
)

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')
})
Expand Down
2 changes: 1 addition & 1 deletion web/app/components/base/tab-slider-new/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const TabSliderNewDemo = ({ initialValue = 'visual' }: { initialValue?: string }
return (
<div className="flex w-full max-w-sm flex-col gap-4 rounded-2xl border border-divider-subtle bg-components-panel-bg p-6">
<div className="text-xs tracking-[0.18em] text-text-tertiary uppercase">Pill tabs</div>
<TabSliderNew value={value} options={OPTIONS} onChange={setValue} />
<TabSliderNew ariaLabel="Builder mode" value={value} options={OPTIONS} onChange={setValue} />
</div>
)
}
Expand Down
29 changes: 18 additions & 11 deletions web/app/components/base/tab-slider-new/index.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,43 @@
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
text: string
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 (
<div data-testid="tab-slider-new" className={cn(className, 'relative flex')}>
<SegmentedControl
aria-label={ariaLabel}
data-testid="tab-slider-new"
value={[value]}
onValueChange={(nextValues) => {
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) => (
<div
<SegmentedControlItem
key={option.value}
value={option.value}
data-testid={`tab-item-${option.value}`}
onClick={() => 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}
</div>
</SegmentedControlItem>
))}
</div>
</SegmentedControl>
)
}

Expand Down
10 changes: 9 additions & 1 deletion web/app/components/integrations/tool-provider-toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -42,6 +43,8 @@ export function ToolProviderToolbar({
onKeywordsChange: (keywords: string) => void
onTagsChange: (tags: string[]) => void
}) {
const { t } = useTranslation()

return (
<div
className={cn(
Expand All @@ -52,7 +55,12 @@ export function ToolProviderToolbar({
)}
>
{!isRouteCategory && (
<TabSliderNew value={activeTab} onChange={onCategoryChange} options={options} />
<TabSliderNew
ariaLabel={t(($) => $['category.tools'], { ns: 'plugin' })}
value={activeTab}
onChange={onCategoryChange}
options={options}
/>
)}
<div className="flex min-w-50 flex-1 items-center justify-between gap-2">
<div className="flex min-w-0 items-center gap-2">
Expand Down