diff --git a/oxlint-suppressions.json b/oxlint-suppressions.json
index c91d85911e4..8c87c9ae25d 100644
--- a/oxlint-suppressions.json
+++ b/oxlint-suppressions.json
@@ -368,14 +368,6 @@
"count": 1
}
},
- "web/app/components/app/configuration/debug/chat-user-input.tsx": {
- "jsx-a11y/no-autofocus": {
- "count": 2
- },
- "no-restricted-imports": {
- "count": 1
- }
- },
"web/app/components/app/configuration/debug/debug-with-multiple-model/chat-item.tsx": {
"typescript/no-explicit-any": {
"count": 6
@@ -414,14 +406,6 @@
"count": 1
}
},
- "web/app/components/app/configuration/prompt-value-panel/index.tsx": {
- "jsx-a11y/no-autofocus": {
- "count": 2
- },
- "no-restricted-imports": {
- "count": 1
- }
- },
"web/app/components/app/create-app-dialog/app-list/sidebar.tsx": {
"erasable-syntax-only/enums": {
"count": 1
@@ -464,11 +448,6 @@
"count": 1
}
},
- "web/app/components/app/overview/workflow-hidden-input-fields.tsx": {
- "no-restricted-imports": {
- "count": 1
- }
- },
"web/app/components/app/text-generate/item/index.tsx": {
"typescript/no-explicit-any": {
"count": 3
@@ -625,9 +604,6 @@
}
},
"web/app/components/base/chat/chat-with-history/inputs-form/content.tsx": {
- "no-restricted-imports": {
- "count": 1
- },
"typescript/no-explicit-any": {
"count": 3
}
@@ -715,9 +691,6 @@
}
},
"web/app/components/base/chat/embedded-chatbot/inputs-form/content.tsx": {
- "no-restricted-imports": {
- "count": 1
- },
"typescript/no-explicit-any": {
"count": 3
}
diff --git a/web/app/components/app/configuration/debug/__tests__/chat-user-input.spec.tsx b/web/app/components/app/configuration/debug/__tests__/chat-user-input.spec.tsx
index 636d64fcd4d..bc5522517e8 100644
--- a/web/app/components/app/configuration/debug/__tests__/chat-user-input.spec.tsx
+++ b/web/app/components/app/configuration/debug/__tests__/chat-user-input.spec.tsx
@@ -11,37 +11,6 @@ vi.mock('use-context-selector', () => ({
createContext: vi.fn(() => ({})),
}))
-vi.mock('@/app/components/base/input', () => ({
- default: ({
- value,
- onChange,
- placeholder,
- autoFocus,
- maxLength,
- readOnly,
- type,
- }: {
- value: string
- onChange: (e: { target: { value: string } }) => void
- placeholder?: string
- autoFocus?: boolean
- maxLength?: number
- readOnly?: boolean
- type?: string
- }) => (
-
- ),
-}))
-
vi.mock('@/app/components/workflow/nodes/_base/components/before-run-form/bool-input', () => ({
default: ({
name,
@@ -172,7 +141,7 @@ describe('ChatUserInput', () => {
)
render()
- expect(screen.getByTestId('input-Name')).toBeInTheDocument()
+ expect(screen.getByRole('textbox', { name: 'Name' })).toBeInTheDocument()
})
it('should render paragraph input type', () => {
@@ -204,7 +173,7 @@ describe('ChatUserInput', () => {
)
render()
- const select = screen.getByRole('combobox')
+ const select = screen.getByRole('combobox', { name: 'Choice' })
await user.click(select)
expect(await screen.findByRole('option', { name: 'A' })).toBeInTheDocument()
expect(screen.getByRole('option', { name: 'B' })).toBeInTheDocument()
@@ -221,7 +190,7 @@ describe('ChatUserInput', () => {
)
render()
- const input = screen.getByTestId('input-Count')
+ const input = screen.getByRole('spinbutton', { name: 'Count' })
expect(input).toBeInTheDocument()
expect(input).toHaveAttribute('type', 'number')
})
@@ -256,9 +225,9 @@ describe('ChatUserInput', () => {
)
render()
- expect(screen.getByTestId('input-Name')).toBeInTheDocument()
+ expect(screen.getByRole('textbox', { name: 'Name' })).toBeInTheDocument()
expect(screen.getByRole('textbox', { name: 'Description' })).toBeInTheDocument()
- expect(screen.getByRole('combobox')).toBeInTheDocument()
+ expect(screen.getByRole('combobox', { name: 'Choice' })).toBeInTheDocument()
})
it('should show optional label for non-required fields', () => {
@@ -313,7 +282,7 @@ describe('ChatUserInput', () => {
)
render()
- expect(screen.getByTestId('input-Name')).toHaveValue('John')
+ expect(screen.getByRole('textbox', { name: 'Name' })).toHaveValue('John')
})
it('should display existing input values for paragraph type', () => {
@@ -340,7 +309,7 @@ describe('ChatUserInput', () => {
render()
// Number type input still uses string value internally
- expect(screen.getByTestId('input-Count')).toHaveValue(42)
+ expect(screen.getByRole('spinbutton', { name: 'Count' })).toHaveValue(42)
})
it('should display checkbox as checked when value is truthy', () => {
@@ -381,7 +350,7 @@ describe('ChatUserInput', () => {
)
render()
- expect(screen.getByTestId('input-Name')).toHaveValue('')
+ expect(screen.getByRole('textbox', { name: 'Name' })).toHaveValue('')
})
it('should handle undefined values', () => {
@@ -394,7 +363,7 @@ describe('ChatUserInput', () => {
)
render()
- expect(screen.getByTestId('input-Name')).toHaveValue('')
+ expect(screen.getByRole('textbox', { name: 'Name' })).toHaveValue('')
})
})
@@ -409,7 +378,9 @@ describe('ChatUserInput', () => {
)
render()
- fireEvent.change(screen.getByTestId('input-Name'), { target: { value: 'New Value' } })
+ fireEvent.change(screen.getByRole('textbox', { name: 'Name' }), {
+ target: { value: 'New Value' },
+ })
expect(mockSetInputs).toHaveBeenCalledWith({ name: 'New Value' })
})
@@ -447,7 +418,7 @@ describe('ChatUserInput', () => {
)
render()
- await user.click(screen.getByRole('combobox'))
+ await user.click(screen.getByRole('combobox', { name: 'Choice' }))
await user.click(await screen.findByRole('option', { name: 'B' }))
expect(mockSetInputs).toHaveBeenCalledWith({ choice: 'B' })
@@ -463,7 +434,9 @@ describe('ChatUserInput', () => {
)
render()
- fireEvent.change(screen.getByTestId('input-Count'), { target: { value: '100' } })
+ fireEvent.change(screen.getByRole('spinbutton', { name: 'Count' }), {
+ target: { value: '100' },
+ })
expect(mockSetInputs).toHaveBeenCalledWith({ count: '100' })
})
@@ -508,7 +481,9 @@ describe('ChatUserInput', () => {
render()
- fireEvent.change(screen.getByTestId('input-Name'), { target: { value: 'Valid' } })
+ fireEvent.change(screen.getByRole('textbox', { name: 'Name' }), {
+ target: { value: 'Valid' },
+ })
expect(mockSetInputs).not.toHaveBeenCalled()
})
@@ -527,7 +502,7 @@ describe('ChatUserInput', () => {
)
render()
- expect(screen.getByTestId('input-Name')).not.toHaveAttribute('readonly')
+ expect(screen.getByRole('textbox', { name: 'Name' })).not.toHaveAttribute('readonly')
})
it('should set string input as readonly when test/run is denied even if configuration is editable', () => {
@@ -542,7 +517,7 @@ describe('ChatUserInput', () => {
)
render()
- expect(screen.getByTestId('input-Name')).toHaveAttribute('readonly')
+ expect(screen.getByRole('textbox', { name: 'Name' })).toHaveAttribute('readonly')
})
it('should set string input as readonly when configuration is readonly and test/run is denied', () => {
@@ -557,7 +532,7 @@ describe('ChatUserInput', () => {
)
render()
- expect(screen.getByTestId('input-Name')).toHaveAttribute('readonly')
+ expect(screen.getByRole('textbox', { name: 'Name' })).toHaveAttribute('readonly')
})
it('should set paragraph input as readonly when configuration is readonly and test/run is denied', () => {
@@ -592,7 +567,7 @@ describe('ChatUserInput', () => {
)
render()
- expect(screen.getByRole('combobox')).toBeDisabled()
+ expect(screen.getByRole('combobox', { name: 'Choice' })).toBeDisabled()
})
it('should disable checkbox when configuration is readonly and test/run is denied', () => {
@@ -718,39 +693,8 @@ describe('ChatUserInput', () => {
})
})
- describe('AutoFocus', () => {
- it('should set autoFocus on first string input', () => {
- mockUseContext.mockReturnValue(
- createContextValue({
- modelConfig: createModelConfig([
- createPromptVariable({ key: 'first', name: 'First', type: 'string' }),
- createPromptVariable({ key: 'second', name: 'Second', type: 'string' }),
- ]),
- }),
- )
-
- render()
- expect(screen.getByTestId('input-First')).toHaveAttribute('data-autofocus', 'true')
- expect(screen.getByTestId('input-Second')).not.toHaveAttribute('data-autofocus')
- })
-
- it('should set autoFocus on first number input when it is the first field', () => {
- mockUseContext.mockReturnValue(
- createContextValue({
- modelConfig: createModelConfig([
- createPromptVariable({ key: 'count', name: 'Count', type: 'number' }),
- createPromptVariable({ key: 'name', name: 'Name', type: 'string' }),
- ]),
- }),
- )
-
- render()
- expect(screen.getByTestId('input-Count')).toHaveAttribute('data-autofocus', 'true')
- })
- })
-
- describe('MaxLength', () => {
- it('should pass maxLength to string input', () => {
+ describe('Input constraints', () => {
+ it('preserves the text length constraint', () => {
mockUseContext.mockReturnValue(
createContextValue({
modelConfig: createModelConfig([
@@ -760,20 +704,8 @@ describe('ChatUserInput', () => {
)
render()
- expect(screen.getByTestId('input-Name')).toHaveAttribute('maxLength', '50')
- })
- it('should pass maxLength to number input', () => {
- mockUseContext.mockReturnValue(
- createContextValue({
- modelConfig: createModelConfig([
- createPromptVariable({ key: 'count', name: 'Count', type: 'number', max_length: 10 }),
- ]),
- }),
- )
-
- render()
- expect(screen.getByTestId('input-Count')).toHaveAttribute('maxLength', '10')
+ expect(screen.getByRole('textbox', { name: 'Name' })).toHaveAttribute('maxLength', '50')
})
})
@@ -789,7 +721,7 @@ describe('ChatUserInput', () => {
)
render()
- const select = screen.getByRole('combobox')
+ const select = screen.getByRole('combobox', { name: 'Choice' })
await user.click(select)
expect(screen.queryAllByRole('option')).toHaveLength(0)
})
@@ -805,7 +737,9 @@ describe('ChatUserInput', () => {
)
render()
- fireEvent.change(screen.getByTestId('input-Name'), { target: { value: 'Updated' } })
+ fireEvent.change(screen.getByRole('textbox', { name: 'Name' }), {
+ target: { value: 'Updated' },
+ })
expect(mockSetInputs).toHaveBeenCalledWith({
name: 'Updated',
@@ -823,7 +757,7 @@ describe('ChatUserInput', () => {
)
render()
- expect(screen.getByTestId('input-Value')).toHaveValue('123')
+ expect(screen.getByRole('textbox', { name: 'Value' })).toHaveValue('123')
})
it('should not hide label for checkbox type', () => {
diff --git a/web/app/components/app/configuration/debug/chat-user-input.tsx b/web/app/components/app/configuration/debug/chat-user-input.tsx
index 3ad495da64a..c52d9b77a7d 100644
--- a/web/app/components/app/configuration/debug/chat-user-input.tsx
+++ b/web/app/components/app/configuration/debug/chat-user-input.tsx
@@ -1,5 +1,6 @@
import type { Inputs } from '@/models/debug'
import { cn } from '@langgenius/dify-ui/cn'
+import { Input } from '@langgenius/dify-ui/input'
import {
Select,
SelectContent,
@@ -11,10 +12,9 @@ import {
} from '@langgenius/dify-ui/select'
import { Textarea } from '@langgenius/dify-ui/textarea'
import * as React from 'react'
-import { useEffect } from 'react'
+import { useEffect, useId } from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
-import Input from '@/app/components/base/input'
import BoolInput from '@/app/components/workflow/nodes/_base/components/before-run-form/bool-input'
import ConfigContext from '@/context/debug-configuration'
@@ -24,6 +24,7 @@ type Props = Readonly<{
const ChatUserInput = ({ inputs }: Props) => {
const { t } = useTranslation()
+ const baseId = useId()
const { modelConfig, setInputs, canTestAndRun = false } = useContext(ConfigContext)
const debugInputReadonly = !canTestAndRun
@@ -81,12 +82,14 @@ const ChatUserInput = ({ inputs }: Props) => {
)}
>
- {promptVariables.map(({ key, name, type, options, max_length, required }, index) => (
+ {promptVariables.map(({ key, name, type, options, max_length, required }) => (
{type !== 'checkbox' && (
-
{name || key}
+
+ {name || key}
+
{!required && (
{t(($) => $['panel.optional'], { ns: 'workflow' })}
@@ -97,12 +100,10 @@ const ChatUserInput = ({ inputs }: Props) => {
{type === 'string' && (
{
- handleInputValueChange(key, e.target.value)
- }}
+ onValueChange={(value) => handleInputValueChange(key, value)}
placeholder={name}
- autoFocus={index === 0}
maxLength={max_length}
readOnly={debugInputReadonly}
/>
@@ -110,7 +111,7 @@ const ChatUserInput = ({ inputs }: Props) => {
{type === 'paragraph' && (
{!userInputFieldCollapse && promptVariables.length > 0 && (
- {promptVariables.map(({ key, name, type, options, max_length, required }, index) => (
+ {promptVariables.map(({ key, name, type, options, max_length, required }) => (
{type !== 'checkbox' && (
-
{name || key}
+
+ {name || key}
+
{!required && (
{t(($) => $['panel.optional'], { ns: 'workflow' })}
@@ -175,19 +178,17 @@ const PromptValuePanel: FC = ({