mirror of
https://github.com/langgenius/dify.git
synced 2026-05-03 17:08:03 +08:00
refactor: migrate PresetsParameter and ParameterItem to base/ui overlay primitives
Replace deprecated Dropdown, SimpleSelect, and Tooltip with DropdownMenu, Select, and Tooltip compound components from base/ui. Hoist TONE_ICONS to module level, remove FC in favor of function declarations, and prune obsolete ESLint suppressions.
This commit is contained in:
@ -109,7 +109,7 @@ describe('ParameterItem', () => {
|
||||
|
||||
it('should render select for string with options', () => {
|
||||
render(<ParameterItem parameterRule={createRule({ type: 'string', options: ['a', 'b'] })} value="a" />)
|
||||
// SimpleSelect renders an element with text 'a'
|
||||
// Select renders the selected value in the trigger
|
||||
expect(screen.getByText('a')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
import type { FC } from 'react'
|
||||
import type { ModelParameterRule } from '../declarations'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import Radio from '@/app/components/base/radio'
|
||||
import { SimpleSelect } from '@/app/components/base/select'
|
||||
import Slider from '@/app/components/base/slider'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
import TagInput from '@/app/components/base/tag-input'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/app/components/base/ui/select'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { useLanguage } from '../hooks'
|
||||
import { isNullOrUndefined } from '../utils'
|
||||
@ -20,13 +19,13 @@ type ParameterItemProps = {
|
||||
onSwitch?: (checked: boolean, assignValue: ParameterValue) => void
|
||||
isInWorkflow?: boolean
|
||||
}
|
||||
const ParameterItem: FC<ParameterItemProps> = ({
|
||||
function ParameterItem({
|
||||
parameterRule,
|
||||
value,
|
||||
onChange,
|
||||
onSwitch,
|
||||
isInWorkflow,
|
||||
}) => {
|
||||
}: ParameterItemProps) {
|
||||
const language = useLanguage()
|
||||
const [localValue, setLocalValue] = useState(value)
|
||||
const numberInputRef = useRef<HTMLInputElement>(null)
|
||||
@ -99,10 +98,6 @@ const ParameterItem: FC<ParameterItemProps> = ({
|
||||
handleInputChange(e.target.value)
|
||||
}
|
||||
|
||||
const handleSelect = (option: { value: string | number, name: string }) => {
|
||||
handleInputChange(option.value)
|
||||
}
|
||||
|
||||
const handleTagChange = (newSequences: string[]) => {
|
||||
handleInputChange(newSequences)
|
||||
}
|
||||
@ -222,13 +217,19 @@ const ParameterItem: FC<ParameterItemProps> = ({
|
||||
|
||||
if (parameterRule.type === 'string' && !!parameterRule?.options?.length) {
|
||||
return (
|
||||
<SimpleSelect
|
||||
className="!py-0"
|
||||
wrapperClassName={cn('!h-8 w-full')}
|
||||
defaultValue={renderValue as string}
|
||||
onSelect={handleSelect}
|
||||
items={parameterRule.options.map(option => ({ value: option, name: option }))}
|
||||
/>
|
||||
<Select
|
||||
value={renderValue as string}
|
||||
onValueChange={v => handleInputChange(v)}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{parameterRule.options!.map(option => (
|
||||
<SelectItem key={option} value={option}>{option}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)
|
||||
}
|
||||
|
||||
@ -272,13 +273,18 @@ const ParameterItem: FC<ParameterItemProps> = ({
|
||||
</div>
|
||||
{
|
||||
parameterRule.help && (
|
||||
<Tooltip
|
||||
popupContent={(
|
||||
<Tooltip>
|
||||
<TooltipTrigger
|
||||
render={(
|
||||
<span className="mr-1 flex h-4 w-4 shrink-0 items-center justify-center">
|
||||
<span aria-hidden className="i-ri-question-line h-3.5 w-3.5 text-text-quaternary" />
|
||||
</span>
|
||||
)}
|
||||
/>
|
||||
<TooltipContent popupClassName="mr-1">
|
||||
<div className="w-[150px] whitespace-pre-wrap">{parameterRule.help[language] || parameterRule.help.en_US}</div>
|
||||
)}
|
||||
popupClassName="mr-1"
|
||||
triggerClassName="mr-1 w-4 h-4 shrink-0"
|
||||
/>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
@ -18,13 +18,12 @@ describe('PresetsParameter', () => {
|
||||
expect(onSelect).toHaveBeenCalledWith(1)
|
||||
})
|
||||
|
||||
// open=true: trigger has bg-state-base-hover class
|
||||
it('should apply hover background class when open is true', () => {
|
||||
it('should mark trigger as open when dropdown is expanded', () => {
|
||||
render(<PresetsParameter onSelect={vi.fn()} />)
|
||||
fireEvent.click(screen.getByRole('button', { name: /common\.modelProvider\.loadPresets/i }))
|
||||
|
||||
const button = screen.getByRole('button', { name: /common\.modelProvider\.loadPresets/i })
|
||||
expect(button).toHaveClass('bg-state-base-hover')
|
||||
expect(button).toHaveAttribute('data-popup-open')
|
||||
})
|
||||
|
||||
// Tone map branch 2: Balanced → Scales02 icon
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
import type { FC } from 'react'
|
||||
import { RiArrowDownSLine } from '@remixicon/react'
|
||||
import { useCallback } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Dropdown from '@/app/components/base/dropdown'
|
||||
import { Brush01 } from '@/app/components/base/icons/src/vender/solid/editor'
|
||||
import { Scales02 } from '@/app/components/base/icons/src/vender/solid/FinanceAndECommerce'
|
||||
import { Target04 } from '@/app/components/base/icons/src/vender/solid/general'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/app/components/base/ui/dropdown-menu'
|
||||
import { TONE_LIST } from '@/config'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
const toneI18nKeyMap = {
|
||||
Creative: 'model.tone.Creative',
|
||||
@ -17,53 +19,42 @@ const toneI18nKeyMap = {
|
||||
Custom: 'model.tone.Custom',
|
||||
} as const
|
||||
|
||||
const TONE_ICONS: Record<number, ReactNode> = {
|
||||
1: <Brush01 className="mr-2 h-[14px] w-[14px] text-[#6938EF]" />,
|
||||
2: <Scales02 className="mr-2 h-[14px] w-[14px] text-indigo-600" />,
|
||||
3: <Target04 className="mr-2 h-[14px] w-[14px] text-[#107569]" />,
|
||||
}
|
||||
|
||||
type PresetsParameterProps = {
|
||||
onSelect: (toneId: number) => void
|
||||
}
|
||||
const PresetsParameter: FC<PresetsParameterProps> = ({
|
||||
onSelect,
|
||||
}) => {
|
||||
|
||||
function PresetsParameter({ onSelect }: PresetsParameterProps) {
|
||||
const { t } = useTranslation()
|
||||
const renderTrigger = useCallback((open: boolean) => {
|
||||
return (
|
||||
<Button
|
||||
size="small"
|
||||
variant="secondary"
|
||||
className={cn(open && 'bg-state-base-hover')}
|
||||
>
|
||||
{t('modelProvider.loadPresets', { ns: 'common' })}
|
||||
<RiArrowDownSLine className="ml-0.5 h-3.5 w-3.5" />
|
||||
</Button>
|
||||
)
|
||||
}, [t])
|
||||
const getToneIcon = (toneId: number) => {
|
||||
const className = 'mr-2 w-[14px] h-[14px]'
|
||||
const res = ({
|
||||
1: <Brush01 className={`${className} text-[#6938EF]`} />,
|
||||
2: <Scales02 className={`${className} text-indigo-600`} />,
|
||||
3: <Target04 className={`${className} text-[#107569]`} />,
|
||||
})[toneId]
|
||||
return res
|
||||
}
|
||||
const options = TONE_LIST.slice(0, 3).map((tone) => {
|
||||
return {
|
||||
value: tone.id,
|
||||
text: (
|
||||
<div className="flex h-full items-center">
|
||||
{getToneIcon(tone.id)}
|
||||
{t(toneI18nKeyMap[tone.name], { ns: 'common' })}
|
||||
</div>
|
||||
),
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
renderTrigger={renderTrigger}
|
||||
items={options}
|
||||
onSelect={item => onSelect(item.value as number)}
|
||||
popupClassName="z-[1003]"
|
||||
/>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
render={(
|
||||
<Button
|
||||
size="small"
|
||||
variant="secondary"
|
||||
className="data-[popup-open]:bg-state-base-hover"
|
||||
/>
|
||||
)}
|
||||
>
|
||||
{t('modelProvider.loadPresets', { ns: 'common' })}
|
||||
<span className="i-ri-arrow-down-s-line ml-0.5 h-3.5 w-3.5" />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
{TONE_LIST.slice(0, 3).map(tone => (
|
||||
<DropdownMenuItem key={tone.id} onClick={() => onSelect(tone.id)}>
|
||||
{TONE_ICONS[tone.id]}
|
||||
{t(toneI18nKeyMap[tone.name], { ns: 'common' })}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user