mirror of
https://github.com/langgenius/dify.git
synced 2026-09-06 01:04:49 +08:00
fix(select): isolate trigger states and unavailable package selection (#41843)
This commit is contained in:
parent
ad90cb9111
commit
00e5786067
@ -62,7 +62,7 @@ type SelectGroupProps = BaseSelect.Group.Props
|
||||
|
||||
const selectTriggerVariants = cva(
|
||||
[
|
||||
'group flex w-full items-center border-0 bg-components-input-bg-normal text-start text-components-input-text-filled outline-hidden',
|
||||
'group/select-trigger flex w-full items-center border-0 bg-components-input-bg-normal text-start text-components-input-text-filled outline-hidden',
|
||||
'hover:bg-state-base-hover-alt focus-visible:bg-state-base-hover-alt data-popup-open:bg-state-base-hover-alt',
|
||||
'focus-visible:ring-2 focus-visible:ring-state-accent-solid',
|
||||
'data-placeholder:text-components-input-text-placeholder',
|
||||
@ -94,7 +94,7 @@ function SelectTrigger({ className, children, size, ...props }: SelectTriggerPro
|
||||
return (
|
||||
<BaseSelect.Trigger className={cn(selectTriggerVariants({ size, className }))} {...props}>
|
||||
<span className="min-w-0 grow truncate">{children}</span>
|
||||
<BaseSelect.Icon className="shrink-0 text-text-quaternary transition-colors group-hover:text-text-secondary group-data-readonly:hidden data-popup-open:text-text-secondary">
|
||||
<BaseSelect.Icon className="shrink-0 text-text-quaternary transition-colors group-data-readonly/select-trigger:hidden group-[:hover:not([data-disabled])]/select-trigger:text-text-secondary data-popup-open:text-text-secondary">
|
||||
<span className="i-ri-arrow-down-s-line h-4 w-4" aria-hidden="true" />
|
||||
</BaseSelect.Icon>
|
||||
</BaseSelect.Trigger>
|
||||
|
||||
@ -12,8 +12,8 @@ import {
|
||||
SelectPortal,
|
||||
SelectPositioner,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@langgenius/dify-ui/select'
|
||||
import { RiArrowDownSLine } from '@remixicon/react'
|
||||
import dayjs from 'dayjs'
|
||||
import * as React from 'react'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
@ -35,7 +35,6 @@ type Props = Readonly<{
|
||||
|
||||
const RangeSelector: FC<Props> = ({ isCustomRange, ranges, onSelect }) => {
|
||||
const { t } = useTranslation()
|
||||
const [open, setOpen] = useState(false)
|
||||
const items = useMemo<TimePeriodOption[]>(() => {
|
||||
return ranges.map((range) => ({
|
||||
...range,
|
||||
@ -69,8 +68,6 @@ const RangeSelector: FC<Props> = ({ isCustomRange, ranges, onSelect }) => {
|
||||
return (
|
||||
<Select<number>
|
||||
value={selectedItem?.value ?? null}
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
onValueChange={(nextValue) => {
|
||||
if (nextValue == null) return
|
||||
const nextItem = items.find((item) => item.value === nextValue)
|
||||
@ -79,15 +76,12 @@ const RangeSelector: FC<Props> = ({ isCustomRange, ranges, onSelect }) => {
|
||||
handleSelectRange(nextItem)
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="h-auto w-fit max-w-none border-0 bg-transparent p-0 hover:bg-transparent focus-visible:bg-transparent [&>*:last-child]:hidden">
|
||||
<div className="flex h-8 cursor-pointer items-center space-x-1.5 rounded-lg bg-components-input-bg-normal pr-2 pl-3 group-data-popup-open:bg-state-base-hover-alt">
|
||||
<div className="system-sm-regular text-components-input-text-filled">
|
||||
{isCustomRange
|
||||
? t(($) => $['filter.period.custom'], { ns: 'appLog' })
|
||||
: selectedItem?.name}
|
||||
</div>
|
||||
<RiArrowDownSLine className="size-4 text-text-quaternary group-data-popup-open:text-text-secondary" />
|
||||
</div>
|
||||
<SelectTrigger className="w-fit">
|
||||
<SelectValue>
|
||||
{isCustomRange
|
||||
? t(($) => $['filter.period.custom'], { ns: 'appLog' })
|
||||
: selectedItem?.name}
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectPortal>
|
||||
<SelectPositioner className="-translate-x-6">
|
||||
|
||||
@ -262,16 +262,28 @@ describe('SelectPackage', () => {
|
||||
expect(onSelectVersion).toHaveBeenCalledWith({ value: 'v0.9.0', name: 'v0.9.0' })
|
||||
})
|
||||
|
||||
it('should select a valid package option', async () => {
|
||||
it('should only allow package selection after a version is selected', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onSelectPackage = vi.fn()
|
||||
renderSelectPackage({
|
||||
selectedVersion: 'v1.0.0',
|
||||
const props = {
|
||||
...createDefaultProps(),
|
||||
updatePayload: createUpdatePayload(),
|
||||
onSelectPackage,
|
||||
})
|
||||
}
|
||||
const { rerender } = render(<SelectPackage {...props} packages={[]} />)
|
||||
|
||||
const section = getSection('plugin.installFromGitHub.selectPackage')
|
||||
await user.click(within(section).getByRole('combobox'))
|
||||
const packageTrigger = screen.getByRole('combobox', {
|
||||
name: 'plugin.installFromGitHub.selectPackage',
|
||||
})
|
||||
expect(packageTrigger).toBeDisabled()
|
||||
await user.click(packageTrigger)
|
||||
expect(screen.queryByRole('listbox')).not.toBeInTheDocument()
|
||||
expect(onSelectPackage).not.toHaveBeenCalled()
|
||||
|
||||
rerender(<SelectPackage {...props} selectedVersion="v1.0.0" />)
|
||||
|
||||
expect(packageTrigger).toBeEnabled()
|
||||
await user.click(packageTrigger)
|
||||
await user.click(await screen.findByRole('option', { name: 'plugin.tar.gz' }))
|
||||
|
||||
expect(onSelectPackage).toHaveBeenCalledWith({
|
||||
@ -472,27 +484,6 @@ describe('SelectPackage', () => {
|
||||
})
|
||||
})
|
||||
|
||||
// ================================
|
||||
// PortalSelect Readonly State Tests
|
||||
// ================================
|
||||
describe('PortalSelect Readonly State', () => {
|
||||
it('should make package select readonly when no version selected', () => {
|
||||
renderSelectPackage({ selectedVersion: '' })
|
||||
|
||||
// When no version is selected, package select should be readonly
|
||||
const trigger = screen.getAllByRole('combobox')[1]
|
||||
expect(trigger).toHaveAttribute('aria-readonly', 'true')
|
||||
})
|
||||
|
||||
it('should make package select active when version is selected', () => {
|
||||
renderSelectPackage({ selectedVersion: 'v1.0.0' })
|
||||
|
||||
// When version is selected, package select should be active
|
||||
const trigger = screen.getAllByRole('combobox')[1]
|
||||
expect(trigger).not.toHaveAttribute('aria-readonly', 'true')
|
||||
})
|
||||
})
|
||||
|
||||
// ================================
|
||||
// installedValue Props Tests
|
||||
// ================================
|
||||
|
||||
@ -128,7 +128,7 @@ const SelectPackage: React.FC<SelectPackageProps> = ({
|
||||
<Field name="package" className="gap-4 self-stretch">
|
||||
<Select
|
||||
value={selectedPackageOption?.value ?? null}
|
||||
readOnly={!selectedVersion}
|
||||
disabled={!selectedVersion}
|
||||
onValueChange={(value) => {
|
||||
if (value == null) return
|
||||
const selectedItem = packages.find((item) => item.value === value)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user