fix(web): make suggested questions native buttons (#40200)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
yyh
2026-08-10 14:47:19 +08:00
committed by GitHub
parent 2917d1d51c
commit ca4d7ff1f2
3 changed files with 31 additions and 30 deletions
-8
View File
@@ -779,14 +779,6 @@
"count": 1
}
},
"web/app/components/base/chat/chat/answer/suggested-questions.tsx": {
"jsx_a11y/click-events-have-key-events": {
"count": 1
},
"jsx_a11y/no-static-element-interactions": {
"count": 1
}
},
"web/app/components/base/chat/chat/answer/workflow-process.tsx": {
"eslint-react/set-state-in-effect": {
"count": 1
@@ -1,11 +1,10 @@
import type { Mock } from 'vitest' // Or 'jest' if using Jest
import type { Mock } from 'vitest'
import type { IChatItem } from '../../type'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { useChatContext } from '../../context'
import SuggestedQuestions from '../suggested-questions'
// Mock the chat context
vi.mock('../../context', () => ({
useChatContext: vi.fn(),
}))
@@ -15,7 +14,6 @@ describe('SuggestedQuestions', () => {
beforeEach(() => {
vi.clearAllMocks()
// Use 'as Mock' instead of 'as any'
;(useChatContext as Mock).mockReturnValue({
onSend: mockOnSend,
readonly: false,
@@ -33,41 +31,52 @@ describe('SuggestedQuestions', () => {
it('should render suggested questions and filter out empty ones', () => {
render(<SuggestedQuestions item={mockItem} />)
const questions = screen.getAllByTestId('suggested-question')
const questions = screen.getAllByRole('button')
expect(questions).toHaveLength(2)
expect(questions[0])!.toHaveTextContent('What is Dify?')
expect(questions[1])!.toHaveTextContent('How to use it?')
expect(questions[0]).toHaveAccessibleName('What is Dify?')
expect(questions[1]).toHaveAccessibleName('How to use it?')
})
it('should call onSend when a question is clicked', async () => {
const user = userEvent.setup()
render(<SuggestedQuestions item={mockItem} />)
const questions = screen.getAllByTestId('suggested-question')
await user.click(questions[0]!)
await user.click(screen.getByRole('button', { name: 'What is Dify?' }))
expect(mockOnSend).toHaveBeenCalledWith('What is Dify?')
})
it.each([
['Enter', '{Enter}'],
['Space', ' '],
])('should send a question with the %s key', async (_, key) => {
const user = userEvent.setup()
render(<SuggestedQuestions item={mockItem} />)
const question = screen.getByRole('button', { name: 'What is Dify?' })
question.focus()
await user.keyboard(key)
expect(mockOnSend).toHaveBeenCalledWith('What is Dify?')
})
it('should not render if isOpeningStatement is false', () => {
render(<SuggestedQuestions item={{ ...mockItem, isOpeningStatement: false }} />)
expect(screen.queryByTestId('suggested-question')).not.toBeInTheDocument()
expect(screen.queryByRole('button')).not.toBeInTheDocument()
})
it('should not render if suggestedQuestions is missing or empty', () => {
render(<SuggestedQuestions item={{ ...mockItem, suggestedQuestions: [] }} />)
expect(screen.queryByTestId('suggested-question')).not.toBeInTheDocument()
expect(screen.queryByRole('button')).not.toBeInTheDocument()
// Use 'as IChatItem' instead of 'as any'
render(
<SuggestedQuestions item={{ ...mockItem, suggestedQuestions: undefined } as IChatItem} />,
)
expect(screen.queryByTestId('suggested-question')).not.toBeInTheDocument()
expect(screen.queryByRole('button')).not.toBeInTheDocument()
})
it('should be disabled and not call onSend when readonly is true', async () => {
const user = userEvent.setup()
// Use 'as Mock' instead of 'as any'
;(useChatContext as Mock).mockReturnValue({
onSend: mockOnSend,
readonly: true,
@@ -75,11 +84,10 @@ describe('SuggestedQuestions', () => {
render(<SuggestedQuestions item={mockItem} />)
const questions = screen.getAllByTestId('suggested-question')
expect(questions[0])!.toHaveClass('pointer-events-none')
expect(questions[0])!.toHaveClass('opacity-50')
const question = screen.getByRole('button', { name: 'What is Dify?' })
expect(question).toBeDisabled()
await user.click(questions[0]!)
await user.click(question)
expect(mockOnSend).not.toHaveBeenCalled()
})
})
@@ -19,17 +19,18 @@ const SuggestedQuestions: FC<SuggestedQuestionsProps> = ({ item }) => {
{suggestedQuestions
.filter((q) => !!q && q.trim())
.map((question, index) => (
<div
<button
type="button"
key={index}
className={cn(
'mt-1 mr-1 inline-flex max-w-full shrink-0 cursor-pointer flex-wrap rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-3.5 py-2 system-sm-medium text-components-button-secondary-accent-text shadow-xs last:mr-0 hover:border-components-button-secondary-border-hover hover:bg-components-button-secondary-bg-hover',
'mt-1 mr-1 inline-flex max-w-full shrink-0 cursor-pointer appearance-none flex-wrap rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-3.5 py-2 text-start system-sm-medium text-components-button-secondary-accent-text shadow-xs last:mr-0 hover:border-components-button-secondary-border-hover hover:bg-components-button-secondary-bg-hover focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden',
readonly && 'pointer-events-none opacity-50',
)}
onClick={() => !readonly && onSend?.(question)}
data-testid="suggested-question"
disabled={readonly}
onClick={() => onSend?.(question)}
>
{question}
</div>
</button>
))}
</div>
)