mirror of
https://github.com/langgenius/dify.git
synced 2026-07-20 19:50:06 -04:00
fix: update file paths and improve pointer event handling in chat components
This commit is contained in:
@@ -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', () => {
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user