Skip to content

Commit d1e4e40

Browse files
committed
fix: render user chat input as plain text
1 parent 0d20844 commit d1e4e40

3 files changed

Lines changed: 24 additions & 18 deletions

File tree

eslint-suppressions.json

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,11 +1236,6 @@
12361236
"count": 1
12371237
}
12381238
},
1239-
"web/app/components/base/chat/chat/question.tsx": {
1240-
"jsx-a11y/no-autofocus": {
1241-
"count": 1
1242-
}
1243-
},
12441239
"web/app/components/base/chat/chat/type.ts": {
12451240
"ts/no-explicit-any": {
12461241
"count": 5

web/app/components/base/chat/chat/__tests__/question.spec.tsx

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { Theme } from '../../embedded-chatbot/theme/theme-context'
22
import type { ChatConfig, ChatItem, OnRegenerate } from '../../types'
33
import type { FileEntity } from '@/app/components/base/file-uploader/types'
44
import { toast } from '@langgenius/dify-ui/toast'
5-
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
5+
import { act, fireEvent, render, screen, waitFor, within } from '@testing-library/react'
66
import userEvent from '@testing-library/user-event'
77
import copy from 'copy-to-clipboard'
88
import * as React from 'react'
@@ -48,9 +48,6 @@ vi.mock('../content-switch', () => ({
4848
},
4949
}))
5050
vi.mock('copy-to-clipboard', () => ({ default: vi.fn() }))
51-
vi.mock('@/app/components/base/markdown', () => ({
52-
Markdown: ({ content }: { content: string }) => <div className="markdown-body">{content}</div>,
53-
}))
5451

5552
// Mock ResizeObserver and capture lifecycle for targeted coverage
5653
const observeMock = vi.fn()
@@ -132,13 +129,23 @@ describe('Question component', () => {
132129
it('should render the question content container and default avatar when hideAvatar is false', () => {
133130
const { container } = renderWithProvider(makeItem())
134131

135-
const markdown = container.querySelector('.markdown-body')
136-
expect(markdown).toBeInTheDocument()
132+
expect(screen.getByTestId('question-content')).toHaveTextContent('This is the question content')
137133

138134
const avatar = container.querySelector('.size-10') || container.querySelector('.size-10.shrink-0')
139135
expect(avatar).toBeTruthy()
140136
})
141137

138+
it('should render user supplied HTML as plain text', () => {
139+
const content = '<button class="primary-button">Confirm</button>'
140+
renderWithProvider(makeItem({ content }))
141+
142+
const contentContainer = screen.getByTestId('question-content')
143+
144+
expect(contentContainer).toHaveTextContent(content)
145+
expect(within(contentContainer).queryByRole('button', { name: 'Confirm' })).not.toBeInTheDocument()
146+
expect(contentContainer.querySelector('.primary-button')).not.toBeInTheDocument()
147+
})
148+
142149
it('should hide avatar when hideAvatar is true', () => {
143150
const { container } = renderWithProvider(makeItem(), vi.fn() as unknown as OnRegenerate, { hideAvatar: true })
144151
const avatar = container.querySelector('.size-10')
@@ -223,9 +230,9 @@ describe('Question component', () => {
223230
})
224231
})
225232

226-
it('should cancel editing and revert to original markdown when cancel is clicked', async () => {
233+
it('should cancel editing and revert to original text when cancel is clicked', async () => {
227234
const user = userEvent.setup()
228-
const { container } = renderWithProvider(makeItem())
235+
renderWithProvider(makeItem())
229236

230237
const editBtn = screen.getByRole('button', { name: 'common.operation.edit' })
231238
await user.click(editBtn)
@@ -239,8 +246,7 @@ describe('Question component', () => {
239246

240247
await waitFor(() => {
241248
expect(screen.queryByRole('textbox')).not.toBeInTheDocument()
242-
const md = container.querySelector('.markdown-body')
243-
expect(md).toBeInTheDocument()
249+
expect(screen.getByTestId('question-content')).toHaveTextContent('This is the question content')
244250
})
245251
})
246252

web/app/components/base/chat/chat/question.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import { useTranslation } from 'react-i18next'
1919
import Textarea from 'react-textarea-autosize'
2020
import { FileList } from '@/app/components/base/file-uploader'
2121
import { User } from '@/app/components/base/icons/src/public/avatar'
22-
import { Markdown } from '@/app/components/base/markdown'
2322
import ActionButton from '../../action-button'
2423
import { CssTransform } from '../embedded-chatbot/theme/utils'
2524
import ContentSwitch from './content-switch'
@@ -59,6 +58,7 @@ const Question: FC<QuestionProps> = ({
5958
const [editedContent, setEditedContent] = useState(content)
6059
const [contentWidth, setContentWidth] = useState(0)
6160
const contentRef = useRef<HTMLDivElement>(null)
61+
const editInputRef = useRef<HTMLTextAreaElement>(null)
6262
const isComposingRef = useRef(false)
6363
const compositionEndTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
6464

@@ -160,6 +160,11 @@ const Question: FC<QuestionProps> = ({
160160
}
161161
}, [clearCompositionEndTimer])
162162

163+
useEffect(() => {
164+
if (isEditing)
165+
editInputRef.current?.focus()
166+
}, [isEditing])
167+
163168
return (
164169
<div className="mb-2 flex justify-end last:mb-0">
165170
<div className={cn('group relative mr-4 flex max-w-full items-start overflow-x-hidden pl-14', isEditing && 'flex-1')}>
@@ -206,15 +211,15 @@ const Question: FC<QuestionProps> = ({
206211
)
207212
}
208213
{!isEditing
209-
? <Markdown content={content} />
214+
? <div className="break-words whitespace-pre-wrap">{content}</div>
210215
: (
211216
<div className="flex flex-col gap-4">
212217
<div className="max-h-[158px] overflow-x-hidden overflow-y-auto pr-1">
213218
<Textarea
219+
ref={editInputRef}
214220
className={cn(
215221
'w-full resize-none bg-transparent p-0 body-lg-regular leading-7 text-text-primary outline-hidden',
216222
)}
217-
autoFocus
218223
minRows={1}
219224
value={editedContent}
220225
onChange={e => setEditedContent(e.target.value)}

0 commit comments

Comments
 (0)