diff --git a/server-ce/test/project-sharing.spec.ts b/server-ce/test/project-sharing.spec.ts index ab71cb7dc9..e26439264b 100644 --- a/server-ce/test/project-sharing.spec.ts +++ b/server-ce/test/project-sharing.spec.ts @@ -36,14 +36,13 @@ describe('Project Sharing', function () { login('user@example.com') createProject(projectName) - // TODO(25342): re-enable - // // Add chat message - // cy.findByText('Chat').click() - // // wait for lazy loading of the chat pane - // cy.findByText('Send your first message to your collaborators') - // cy.get( - // 'textarea[placeholder="Send a message to your collaborators…"]' - // ).type('New Chat Message{enter}') + // Add chat message + cy.findByText('Chat').click() + // wait for lazy loading of the chat pane + cy.findByText('Send your first message to your collaborators') + cy.get( + 'textarea[placeholder="Send a message to your collaborators…"]' + ).type('New Chat Message{enter}') // Get link sharing links enableLinkSharing().then( @@ -95,9 +94,8 @@ describe('Project Sharing', function () { } function expectChatAccess() { - // TODO(25342): re-enable - // cy.findByText('Chat').click() - // cy.findByText('New Chat Message') + cy.findByText('Chat').click() + cy.findByText('New Chat Message') } function expectHistoryAccess() { @@ -111,9 +109,8 @@ describe('Project Sharing', function () { } function expectNoChatAccess() { - // TODO(25342): re-enable - // cy.findByText('Layout') // wait for lazy loading - // cy.findByText('Chat').should('not.exist') + cy.findByText('Layout') // wait for lazy loading + cy.findByText('Chat').should('not.exist') } function expectNoHistoryAccess() { diff --git a/services/web/frontend/js/features/ide-react/hooks/use-chat-pane.ts b/services/web/frontend/js/features/ide-react/hooks/use-chat-pane.ts index 0c6d816181..44b165fd57 100644 --- a/services/web/frontend/js/features/ide-react/hooks/use-chat-pane.ts +++ b/services/web/frontend/js/features/ide-react/hooks/use-chat-pane.ts @@ -1,6 +1,7 @@ import { useLayoutContext } from '@/shared/context/layout-context' import useCollapsiblePanel from '@/features/ide-react/hooks/use-collapsible-panel' -import { useCallback, useRef, useState } from 'react' +import useDebounce from '@/shared/hooks/use-debounce' +import { useCallback, useEffect, useRef, useState } from 'react' import { ImperativePanelHandle } from 'react-resizable-panels' export const useChatPane = () => { @@ -8,6 +9,17 @@ export const useChatPane = () => { const [resizing, setResizing] = useState(false) const panelRef = useRef(null) + // Keep track of a debounced local state variable for panel openness and + // only update the external openness state when the debounced value changes. + // This prevents successive calls to onCollapse and onExpand from + // react-resizable-panels updating the openness state multiple times in quick + // succession, which causes confusing behaviour that is different in React 17 + // and 18. Collapsing the chat pane on initialization is necessary because + // react-resizable-panels does not provide a way to specify both that a panel + // should be collapsed and a default size for the panel when expanded. + const [localIsOpen, setLocalIsOpen] = useState(isOpen) + const debouncedLocalIsOpen = useDebounce(localIsOpen, 100) + useCollapsiblePanel(isOpen, panelRef) const togglePane = useCallback(() => { @@ -15,12 +27,16 @@ export const useChatPane = () => { }, [setIsOpen]) const handlePaneExpand = useCallback(() => { - setIsOpen(true) - }, [setIsOpen]) + setLocalIsOpen(true) + }, []) const handlePaneCollapse = useCallback(() => { - setIsOpen(false) - }, [setIsOpen]) + setLocalIsOpen(false) + }, []) + + useEffect(() => { + setIsOpen(debouncedLocalIsOpen) + }, [debouncedLocalIsOpen, setIsOpen]) return { isOpen,