mirror of
https://github.com/langgenius/dify.git
synced 2026-05-04 09:28:04 +08:00
Merge main HEAD (segment 5) into sandboxed-agent-rebase
Resolve 83 conflicts: 10 backend, 62 frontend, 11 config/lock files. Preserve sandbox/agent/collaboration features while adopting main's UI refactorings (Dialog/AlertDialog/Popover), model provider updates, and enterprise features. Made-with: Cursor
This commit is contained in:
@ -27,7 +27,7 @@ vi.mock('../../..', () => ({
|
||||
useFieldContext: () => mockField,
|
||||
}))
|
||||
|
||||
vi.mock('next/navigation', () => ({
|
||||
vi.mock('@/next/navigation', () => ({
|
||||
useParams: () => ({ token: 'test-token' }),
|
||||
}))
|
||||
|
||||
|
||||
@ -22,12 +22,26 @@ describe('NumberInputField', () => {
|
||||
|
||||
it('should render current number value', () => {
|
||||
render(<NumberInputField label="Count" />)
|
||||
expect(screen.getByDisplayValue('2')).toBeInTheDocument()
|
||||
expect(screen.getByRole('textbox')).toHaveValue('2')
|
||||
})
|
||||
|
||||
it('should update value when users click increment', () => {
|
||||
render(<NumberInputField label="Count" />)
|
||||
fireEvent.click(screen.getByRole('button', { name: 'increment' }))
|
||||
fireEvent.click(screen.getByRole('button', { name: 'common.operation.increment' }))
|
||||
expect(mockField.handleChange).toHaveBeenCalledWith(3)
|
||||
})
|
||||
|
||||
it('should reset field value when users clear the input', () => {
|
||||
render(<NumberInputField label="Count" />)
|
||||
fireEvent.change(screen.getByRole('textbox'), { target: { value: '' } })
|
||||
expect(mockField.handleChange).toHaveBeenCalledWith(0)
|
||||
})
|
||||
|
||||
it('should clamp out-of-range edits before updating field state', () => {
|
||||
render(<NumberInputField label="Count" min={0} max={10} />)
|
||||
|
||||
fireEvent.change(screen.getByRole('textbox'), { target: { value: '12' } })
|
||||
|
||||
expect(mockField.handleChange).toHaveBeenLastCalledWith(10)
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,24 +1,52 @@
|
||||
import type { InputNumberProps } from '../../../input-number'
|
||||
import type { ReactNode } from 'react'
|
||||
import type { NumberFieldInputProps, NumberFieldRootProps, NumberFieldSize } from '../../../ui/number-field'
|
||||
import type { LabelProps } from '../label'
|
||||
import * as React from 'react'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { useFieldContext } from '../..'
|
||||
import { InputNumber } from '../../../input-number'
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldControls,
|
||||
NumberFieldDecrement,
|
||||
NumberFieldGroup,
|
||||
NumberFieldIncrement,
|
||||
NumberFieldInput,
|
||||
NumberFieldUnit,
|
||||
} from '../../../ui/number-field'
|
||||
import Label from '../label'
|
||||
|
||||
type TextFieldProps = {
|
||||
type NumberInputFieldProps = {
|
||||
label: string
|
||||
labelOptions?: Omit<LabelProps, 'htmlFor' | 'label'>
|
||||
className?: string
|
||||
} & Omit<InputNumberProps, 'id' | 'value' | 'onChange' | 'onBlur'>
|
||||
inputClassName?: string
|
||||
unit?: ReactNode
|
||||
size?: NumberFieldSize
|
||||
} & Omit<NumberFieldRootProps, 'children' | 'className' | 'id' | 'value' | 'defaultValue' | 'onValueChange'> & Omit<NumberFieldInputProps, 'children' | 'size' | 'onBlur' | 'className' | 'onChange'>
|
||||
|
||||
const NumberInputField = ({
|
||||
label,
|
||||
labelOptions,
|
||||
className,
|
||||
...inputProps
|
||||
}: TextFieldProps) => {
|
||||
inputClassName,
|
||||
unit,
|
||||
size = 'regular',
|
||||
...props
|
||||
}: NumberInputFieldProps) => {
|
||||
const field = useFieldContext<number>()
|
||||
const {
|
||||
value: _value,
|
||||
min,
|
||||
max,
|
||||
step,
|
||||
disabled,
|
||||
readOnly,
|
||||
required,
|
||||
name: _name,
|
||||
id: _id,
|
||||
...inputProps
|
||||
} = props
|
||||
const emptyValue = min ?? 0
|
||||
|
||||
return (
|
||||
<div className={cn('flex flex-col gap-y-0.5', className)}>
|
||||
@ -27,13 +55,36 @@ const NumberInputField = ({
|
||||
label={label}
|
||||
{...(labelOptions ?? {})}
|
||||
/>
|
||||
<InputNumber
|
||||
<NumberField
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
value={field.state.value}
|
||||
onChange={value => field.handleChange(value)}
|
||||
onBlur={field.handleBlur}
|
||||
{...inputProps}
|
||||
/>
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
disabled={disabled}
|
||||
readOnly={readOnly}
|
||||
required={required}
|
||||
onValueChange={value => field.handleChange(value ?? emptyValue)}
|
||||
>
|
||||
<NumberFieldGroup size={size}>
|
||||
<NumberFieldInput
|
||||
{...inputProps}
|
||||
size={size}
|
||||
className={inputClassName}
|
||||
onBlur={field.handleBlur}
|
||||
/>
|
||||
{Boolean(unit) && (
|
||||
<NumberFieldUnit size={size}>
|
||||
{unit}
|
||||
</NumberFieldUnit>
|
||||
)}
|
||||
<NumberFieldControls>
|
||||
<NumberFieldIncrement size={size} />
|
||||
<NumberFieldDecrement size={size} />
|
||||
</NumberFieldControls>
|
||||
</NumberFieldGroup>
|
||||
</NumberField>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ import { useAppForm } from '../../..'
|
||||
import BaseField from '../field'
|
||||
import { BaseFieldType } from '../types'
|
||||
|
||||
vi.mock('next/navigation', () => ({
|
||||
vi.mock('@/next/navigation', () => ({
|
||||
useParams: () => ({}),
|
||||
}))
|
||||
|
||||
@ -45,7 +45,7 @@ describe('BaseField', () => {
|
||||
it('should render a number input when configured as number input', () => {
|
||||
render(<FieldHarness config={createConfig({ type: BaseFieldType.numberInput, label: 'Age' })} initialData={{ fieldA: 20 }} />)
|
||||
|
||||
expect(screen.getByRole('spinbutton')).toBeInTheDocument()
|
||||
expect(screen.getByRole('textbox')).toBeInTheDocument()
|
||||
expect(screen.getByText('Age')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user