fix: update file paths and improve pointer event handling in chat components

This commit is contained in:
twwu
2026-07-07 09:56:22 +08:00
parent 89fb9321fe
commit 5e2cb2fb12
4 changed files with 73 additions and 38 deletions
+2 -2
View File
@@ -8,7 +8,7 @@ API_ENV_EXAMPLE="$ROOT/api/.env.example"
API_ENV="$ROOT/api/.env"
WEB_ENV_EXAMPLE="$ROOT/web/.env.example"
WEB_ENV="$ROOT/web/.env.local"
MIDDLEWARE_ENV_EXAMPLE="$ROOT/docker/middleware.env.example"
MIDDLEWARE_ENV_EXAMPLE="$ROOT/docker/envs/middleware.env.example"
MIDDLEWARE_ENV="$ROOT/docker/middleware.env"
# 1) Copy api/.env.example -> api/.env
@@ -17,7 +17,7 @@ cp "$API_ENV_EXAMPLE" "$API_ENV"
# 2) Copy web/.env.example -> web/.env.local
cp "$WEB_ENV_EXAMPLE" "$WEB_ENV"
# 3) Copy docker/middleware.env.example -> docker/middleware.env
# 3) Copy docker/envs/middleware.env.example -> docker/middleware.env
cp "$MIDDLEWARE_ENV_EXAMPLE" "$MIDDLEWARE_ENV"
# 4) Install deps
@@ -129,6 +129,9 @@ const defaultChatHookReturn: Partial<ChatHookReturn> = {
suggestedQuestions: [],
}
const getDisabledInputContainer = (textbox: HTMLElement) =>
textbox.closest('.opacity-50')
describe('ChatWrapper', () => {
beforeEach(() => {
vi.clearAllMocks()
@@ -320,7 +323,7 @@ describe('ChatWrapper', () => {
render(<ChatWrapper />)
const textboxes = screen.getAllByRole('textbox')
const chatInput = textboxes[textboxes.length - 1]
const disabledContainer = chatInput.closest('.pointer-events-none')
const disabledContainer = getDisabledInputContainer(chatInput)
expect(disabledContainer).toBeInTheDocument()
expect(disabledContainer).toHaveClass('opacity-50')
})
@@ -337,7 +340,7 @@ describe('ChatWrapper', () => {
render(<ChatWrapper />)
const textboxes = screen.getAllByRole('textbox')
const chatInput = textboxes[textboxes.length - 1]
const container = chatInput.closest('.pointer-events-none')
const container = getDisabledInputContainer(chatInput)
expect(container).not.toBeInTheDocument()
})
@@ -361,7 +364,7 @@ describe('ChatWrapper', () => {
render(<ChatWrapper />)
const textboxes = screen.getAllByRole('textbox')
const chatInput = textboxes[textboxes.length - 1]
const container = chatInput.closest('.pointer-events-none')
const container = getDisabledInputContainer(chatInput)
expect(container).toBeInTheDocument()
})
@@ -384,7 +387,7 @@ describe('ChatWrapper', () => {
render(<ChatWrapper />)
const textarea = screen.getByRole('textbox')
const container = textarea.closest('.pointer-events-none')
const container = getDisabledInputContainer(textarea)
expect(container).not.toBeInTheDocument()
})
@@ -411,7 +414,7 @@ describe('ChatWrapper', () => {
render(<ChatWrapper />)
const textboxes = screen.getAllByRole('textbox')
const chatInput = textboxes[textboxes.length - 1]
const container = chatInput.closest('.pointer-events-none')
const container = getDisabledInputContainer(chatInput)
expect(container).toBeInTheDocument()
})
@@ -437,7 +440,7 @@ describe('ChatWrapper', () => {
render(<ChatWrapper />)
const textarea = screen.getByRole('textbox')
const container = textarea.closest('.pointer-events-none')
const container = getDisabledInputContainer(textarea)
expect(container).not.toBeInTheDocument()
})
@@ -456,7 +459,7 @@ describe('ChatWrapper', () => {
render(<ChatWrapper />)
const textarea = screen.getByRole('textbox')
const container = textarea.closest('.pointer-events-none')
const container = getDisabledInputContainer(textarea)
expect(container).toBeInTheDocument()
})
@@ -472,7 +475,7 @@ describe('ChatWrapper', () => {
render(<ChatWrapper />)
const textarea = screen.getByRole('textbox')
const container = textarea.closest('.pointer-events-none')
const container = getDisabledInputContainer(textarea)
expect(container).not.toBeInTheDocument()
})
@@ -1101,7 +1104,7 @@ describe('ChatWrapper', () => {
render(<ChatWrapper />)
const textboxes = screen.getAllByRole('textbox')
const chatInput = textboxes[textboxes.length - 1]
const container = chatInput.closest('.pointer-events-none')
const container = getDisabledInputContainer(chatInput)
expect(container).toBeInTheDocument()
})
@@ -1242,7 +1245,7 @@ describe('ChatWrapper', () => {
// This tests line 106 - early return when hasEmptyInput is set
const textboxes = screen.getAllByRole('textbox')
const chatInput = textboxes[textboxes.length - 1]
const container = chatInput.closest('.pointer-events-none')
const container = getDisabledInputContainer(chatInput)
expect(container).toBeInTheDocument()
})
@@ -1270,7 +1273,7 @@ describe('ChatWrapper', () => {
// This tests line 109 - early return when fileIsUploading is set
const textboxes = screen.getAllByRole('textbox')
const chatInput = textboxes[textboxes.length - 1]
const container = chatInput.closest('.pointer-events-none')
const container = getDisabledInputContainer(chatInput)
expect(container).toBeInTheDocument()
})
@@ -1809,7 +1812,7 @@ describe('ChatWrapper', () => {
render(<ChatWrapper />)
const textboxes = screen.getAllByRole('textbox')
const chatInput = textboxes[textboxes.length - 1]
const container = chatInput.closest('.pointer-events-none')
const container = getDisabledInputContainer(chatInput)
// Should not be disabled because it's not required
expect(container).not.toBeInTheDocument()
})
@@ -954,6 +954,30 @@ describe('Chat', () => {
const innerDivs = screen.getByTestId('chat-footer').querySelectorAll('div')
expect(innerDivs.length).toBeGreaterThan(0)
})
it('should let footer whitespace pass pointer events through while preserving input interaction', () => {
renderChat({
noChatInput: false,
})
const footer = screen.getByTestId('chat-footer')
const footerInner = footer.firstElementChild as HTMLElement
const inputWrapper = screen.getByTestId('chat-input-area').parentElement as HTMLElement
expect(footer).toHaveClass('pointer-events-none')
expect(footerInner).toHaveClass('pointer-events-none')
expect(inputWrapper).toHaveClass('pointer-events-auto')
})
it('should keep stop responding action interactive inside the pass-through footer', () => {
renderChat({
isResponding: true,
noStopResponding: false,
noChatInput: true,
})
expect(screen.getByTestId('stop-responding-container')).toHaveClass('pointer-events-auto')
})
})
describe('Container and Spacing Variations', () => {
+32 -24
View File
@@ -256,6 +256,7 @@ const Chat: FC<ChatProps> = ({
}, [handleWindowResize, sidebarCollapseState])
const hasTryToAsk = config?.suggested_questions_after_answer?.enabled && !!suggestedQuestions?.length && onSend
const shouldApplyFooterClassName = hasTryToAsk || !noChatInput || !noStopResponding
return (
<ChatContextProvider
@@ -327,16 +328,19 @@ const Chat: FC<ChatProps> = ({
</div>
<div
data-testid="chat-footer"
className={`absolute bottom-0 z-10 flex justify-center bg-chat-input-mask ${(hasTryToAsk || !noChatInput || !noStopResponding) && chatFooterClassName}`}
className={cn(
'pointer-events-none absolute bottom-0 z-10 flex justify-center bg-chat-input-mask',
shouldApplyFooterClassName && chatFooterClassName,
)}
ref={chatFooterRef}
>
<div
ref={chatFooterInnerRef}
className={cn('relative', chatFooterInnerClassName, isTryApp && 'px-0')}
className={cn('pointer-events-none relative', chatFooterInnerClassName, isTryApp && 'px-0')}
>
{
!noStopResponding && isResponding && (
<div data-testid="stop-responding-container" className="mb-2 flex justify-center">
<div data-testid="stop-responding-container" className="pointer-events-auto mb-2 flex justify-center">
<Button className="border-components-panel-border bg-components-panel-bg text-components-button-secondary-text" onClick={onStopResponding}>
<div className="i-custom-vender-solid-mediaAndDevices-stop-circle mr-[5px] h-3.5 w-3.5" />
<span className="text-xs font-normal">{t('operation.stopResponding', { ns: 'appDebug' })}</span>
@@ -346,31 +350,35 @@ const Chat: FC<ChatProps> = ({
}
{
hasTryToAsk && (
<TryToAsk
suggestedQuestions={suggestedQuestions}
onSend={onSend}
/>
<div className="pointer-events-auto">
<TryToAsk
suggestedQuestions={suggestedQuestions}
onSend={onSend}
/>
</div>
)
}
{
!noChatInput && (
<ChatInputArea
botName={appData?.site?.title || 'Bot'}
disabled={inputDisabled}
showFeatureBar={showFeatureBar}
showFileUpload={showFileUpload}
featureBarDisabled={isResponding}
onFeatureBarClick={onFeatureBarClick}
visionConfig={config?.file_upload}
speechToTextConfig={config?.speech_to_text}
onSend={onSend}
inputs={inputs}
inputsForm={inputsForm}
theme={themeBuilder?.theme}
isResponding={isResponding}
readonly={readonly}
sendOnEnter={sendOnEnter}
/>
<div className="pointer-events-auto">
<ChatInputArea
botName={appData?.site?.title || 'Bot'}
disabled={inputDisabled}
showFeatureBar={showFeatureBar}
showFileUpload={showFileUpload}
featureBarDisabled={isResponding}
onFeatureBarClick={onFeatureBarClick}
visionConfig={config?.file_upload}
speechToTextConfig={config?.speech_to_text}
onSend={onSend}
inputs={inputs}
inputsForm={inputsForm}
theme={themeBuilder?.theme}
isResponding={isResponding}
readonly={readonly}
sendOnEnter={sendOnEnter}
/>
</div>
)
}
</div>