mirror of
https://github.com/langgenius/dify.git
synced 2026-06-26 14:51:13 +08:00
fix: disable submit button when prompt is empty
This commit is contained in:
parent
69e9fa68db
commit
68c3fc0ef6
@ -533,14 +533,20 @@ describe('ChatInputArea', () => {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
describe('Validation & Constraints', () => {
|
||||
it('should notify and NOT send when query is blank', async () => {
|
||||
it('should disable send when query is blank', async () => {
|
||||
const user = userEvent.setup({ delay: null })
|
||||
const onSend = vi.fn()
|
||||
render(<ChatInputArea onSend={onSend} visionConfig={mockVisionConfig} />)
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'common.operation.send' }))
|
||||
const sendButton = screen.getByRole('button', { name: 'common.operation.send' })
|
||||
expect(sendButton).toBeDisabled()
|
||||
|
||||
await user.click(sendButton)
|
||||
expect(onSend).not.toHaveBeenCalled()
|
||||
expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({ type: 'info' }))
|
||||
expect(mockNotify).not.toHaveBeenCalled()
|
||||
|
||||
await user.type(getTextarea()!, ' ')
|
||||
expect(sendButton).toBeDisabled()
|
||||
})
|
||||
|
||||
it('should notify and NOT send while bot is responding', async () => {
|
||||
|
||||
@ -54,6 +54,7 @@ const ChatInputArea = ({ readonly, botName, placeholder, showFeatureBar, showFil
|
||||
const { t } = useTranslation()
|
||||
const { wrapperRef, textareaRef, textValueRef, holdSpaceRef, handleTextareaResize, isMultipleLine } = useTextAreaHeight()
|
||||
const [query, setQuery] = useState('')
|
||||
const canSend = !!query.trim()
|
||||
const [showVoiceInput, setShowVoiceInput] = useState(false)
|
||||
const filesStore = useFileStore()
|
||||
const { handleDragFileEnter, handleDragFileLeave, handleDragFileOver, handleDropFile, handleClipboardPasteFile, isDragActive } = useFile(visionConfig!, false)
|
||||
@ -66,6 +67,9 @@ const ChatInputArea = ({ readonly, botName, placeholder, showFeatureBar, showFil
|
||||
setTimeout(handleTextareaResize, 0)
|
||||
}, [handleTextareaResize])
|
||||
const handleSend = () => {
|
||||
if (!canSend)
|
||||
return
|
||||
|
||||
if (isResponding) {
|
||||
toast.info(t('errorMessage.waitForResponse', { ns: 'appDebug' }))
|
||||
return
|
||||
@ -76,10 +80,6 @@ const ChatInputArea = ({ readonly, botName, placeholder, showFeatureBar, showFil
|
||||
toast.info(t('errorMessage.waitForFileUpload', { ns: 'appDebug' }))
|
||||
return
|
||||
}
|
||||
if (!query || !query.trim()) {
|
||||
toast.info(t('errorMessage.queryRequired', { ns: 'appAnnotation' }))
|
||||
return
|
||||
}
|
||||
if (checkInputsForm(inputs, inputsForm)) {
|
||||
onSend(query, files)
|
||||
handleQueryChange('')
|
||||
@ -142,7 +142,7 @@ const ChatInputArea = ({ readonly, botName, placeholder, showFeatureBar, showFil
|
||||
toast.error(t('voiceInput.notAllow', { ns: 'common' }))
|
||||
})
|
||||
}, [t])
|
||||
const operation = (<Operation ref={holdSpaceRef} readonly={readonly} fileConfig={visionConfig} speechToTextConfig={speechToTextConfig} onShowVoiceInput={handleShowVoiceInput} onSend={handleSend} sendButtonLabel={sendButtonLabel} theme={theme} />)
|
||||
const operation = (<Operation ref={holdSpaceRef} readonly={readonly} fileConfig={visionConfig} speechToTextConfig={speechToTextConfig} onShowVoiceInput={handleShowVoiceInput} onSend={handleSend} sendButtonLabel={sendButtonLabel} disabled={!canSend} theme={theme} />)
|
||||
return (
|
||||
<>
|
||||
<div className={cn('relative z-10 overflow-hidden rounded-xl border border-components-chat-input-border bg-components-panel-bg-blur pb-[9px] shadow-md', isDragActive && 'border border-dashed border-components-option-card-option-selected-border', disabled && 'pointer-events-none border-components-panel-border opacity-50 shadow-none')}>
|
||||
|
||||
@ -10,7 +10,6 @@ import {
|
||||
RiMicLine,
|
||||
RiSendPlane2Fill,
|
||||
} from '@remixicon/react'
|
||||
import { noop } from 'es-toolkit/function'
|
||||
import { memo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
@ -23,6 +22,7 @@ type OperationProps = {
|
||||
onShowVoiceInput?: () => void
|
||||
onSend: () => void
|
||||
sendButtonLabel?: string
|
||||
disabled?: boolean
|
||||
theme?: Theme | null
|
||||
ref?: Ref<HTMLDivElement>
|
||||
}
|
||||
@ -34,6 +34,7 @@ const Operation: FC<OperationProps> = ({
|
||||
onShowVoiceInput,
|
||||
onSend,
|
||||
sendButtonLabel,
|
||||
disabled,
|
||||
theme,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
@ -70,7 +71,8 @@ const Operation: FC<OperationProps> = ({
|
||||
sendButtonLabel ? 'px-3' : 'w-8 px-0',
|
||||
)}
|
||||
variant="primary"
|
||||
onClick={readonly ? noop : onSend}
|
||||
disabled={readonly || disabled}
|
||||
onClick={onSend}
|
||||
style={
|
||||
theme
|
||||
? {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user