diff --git a/e2e-tests/cypress/tests/integration/channels/collapsed_reply_threads/replies_spec.js b/e2e-tests/cypress/tests/integration/channels/collapsed_reply_threads/replies_spec.js index 16b30acf5b..6d7c252bfe 100644 --- a/e2e-tests/cypress/tests/integration/channels/collapsed_reply_threads/replies_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/collapsed_reply_threads/replies_spec.js @@ -10,6 +10,8 @@ // Stage: @prod // Group: @channels @collapsed_reply_threads +import * as TIMEOUTS from '../../../fixtures/timeouts'; + describe('Collapsed Reply Threads', () => { let testTeam; let testUser; @@ -174,4 +176,45 @@ describe('Collapsed Reply Threads', () => { cy.uiCloseRHS(); }); }); + + it('MM-T5413 should auto-scroll to bottom upon pasting long text in reply', () => { + // # Post a root post as current user + cy.postMessageAs({ + sender: testUser, + message: 'Another interesting post,', + channelId: testChannel.id, + }).then(({id: rootId}) => { + // # Post multiple replies as other user so that the new messages line is pushed up + Cypress._.times(20, (i) => { + cy.postMessageAs({ + sender: otherUser, + message: 'Reply ' + i, + channelId: testChannel.id, + rootId, + }); + }); + + // # Click root post + cy.get(`#post_${rootId}`).click(); + + // # Wait for RHS to open and scroll to position + cy.wait(TIMEOUTS.ONE_SEC); + + // * RHS should open and the editor's actions should not be visible. + cy.get('#rhsContainer').findByTestId('SendMessageButton').should('not.be.visible'); + + // # Close RHS + cy.uiCloseRHS(); + + // # Click root post + cy.get(`#post_${rootId}`).click(); + + // # Paste a multiline string in the RHS textbox. + const text = 'word '.repeat(2000); + cy.get('#rhsContainer').findByTestId('reply_textbox').clear().invoke('val', text).trigger('input'); + + // * RHS should open and the editor should be visible and focused + cy.get('#rhsContainer').findByTestId('SendMessageButton').should('be.visible'); + }); + }); }); diff --git a/webapp/channels/src/components/advanced_text_editor/advanced_text_editor.tsx b/webapp/channels/src/components/advanced_text_editor/advanced_text_editor.tsx index b3c683c4a2..9b76d8bd7a 100644 --- a/webapp/channels/src/components/advanced_text_editor/advanced_text_editor.tsx +++ b/webapp/channels/src/components/advanced_text_editor/advanced_text_editor.tsx @@ -131,7 +131,7 @@ const AdvanceTextEditor = ({ canUploadFiles, enableEmojiPicker, enableGifPicker, - handleBlur, + handleBlur: onBlur, handlePostError, emitTypingEvent, handleMouseUpKeyUp, @@ -170,6 +170,7 @@ const AdvanceTextEditor = ({ const [scrollbarWidth, setScrollbarWidth] = useState(0); const [renderScrollbar, setRenderScrollbar] = useState(false); const [showFormattingSpacer, setShowFormattingSpacer] = useState(shouldShowPreview); + const [keepEditorInFocus, setKeepEditorInFocus] = useState(false); const input = textboxRef.current?.getInputBox(); @@ -187,6 +188,15 @@ const AdvanceTextEditor = ({ setShowPreview(!shouldShowPreview); }, [shouldShowPreview, setShowPreview]); + const handleBlur = useCallback(() => { + onBlur?.(); + setKeepEditorInFocus(false); + }, [onBlur]); + + const handleFocus = useCallback(() => { + setKeepEditorInFocus(true); + }, []); + let serverErrorJsx = null; if (serverError) { serverErrorJsx = ( @@ -422,6 +432,7 @@ const AdvanceTextEditor = ({ /> )} slot2={null} + shouldScrollIntoView={keepEditorInFocus} /> ); @@ -479,6 +490,7 @@ const AdvanceTextEditor = ({ handlePostError={handlePostError} value={messageValue} onBlur={handleBlur} + onFocus={handleFocus} emojiEnabled={enableEmojiPicker} createMessage={createMessage} channelId={channelId} diff --git a/webapp/channels/src/components/textbox/textbox.tsx b/webapp/channels/src/components/textbox/textbox.tsx index dc89524e21..1ba345a75e 100644 --- a/webapp/channels/src/components/textbox/textbox.tsx +++ b/webapp/channels/src/components/textbox/textbox.tsx @@ -44,6 +44,7 @@ export type Props = { onMouseUp?: (e: React.MouseEvent) => void; onKeyUp?: (e: React.KeyboardEvent) => void; onBlur?: (e: FocusEvent) => void; + onFocus?: (e: FocusEvent) => void; supportsCommands?: boolean; handlePostError?: (message: JSX.Element | null) => void; onPaste?: (e: ClipboardEvent) => void; @@ -312,6 +313,7 @@ export default class Textbox extends React.PureComponent { onKeyUp={this.handleKeyUp} onComposition={this.props.onComposition} onBlur={this.handleBlur} + onFocus={this.props.onFocus} onHeightChange={this.props.onHeightChange} onWidthChange={this.props.onWidthChange} onPaste={this.props.onPaste}