Fix RHS reply input focus issues (#33965)

* extend shift+up e2e-test

* fix: MM-T203 Focus does not move when it has already been set elsewhere

* focus_move_spec: add coverage for replying to a reply

* Fix RHS reply input focus issues

- Addresses focus not shifting to RHS reply input when replying to messages
- Fixes issue where focus remains in center channel instead of RHS
- Related to MM-64520

* use requestAnimationFrame

* simplify

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
Jesse Hallam 2025-09-29 15:18:59 -03:00 committed by GitHub
parent 828c5040ef
commit a95be3d76e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 78 additions and 7 deletions

View file

@ -21,7 +21,7 @@ describe('Keyboard Shortcuts', () => {
it('MM-T1277 SHIFT+UP', () => {
// # Press shift+up to open the latest thread in the channel in the RHS
cy.uiGetPostTextBox().type('{shift}{uparrow}');
cy.uiGetPostTextBox().type('{shift+uparrow}', {delay: 50});
// * RHS Opens up
cy.get('.sidebar--right__header').should('be.visible');
@ -33,7 +33,28 @@ describe('Keyboard Shortcuts', () => {
cy.uiGetPostTextBox().click();
// # Press shift+up again
cy.uiGetPostTextBox().type('{shift}{uparrow}');
cy.uiGetPostTextBox().type('{shift+uparrow}', {delay: 50});
// * RHS textbox should be focused
cy.uiGetReplyTextBox().should('be.focused');
// # Post a reply in the thread
cy.uiGetReplyTextBox().type('This is a reply{enter}');
// # Close the RHS by clicking the X button
cy.get('#rhsCloseButton').click();
// * Verify RHS is closed
cy.get('.sidebar--right__header').should('not.exist');
// # Click into the center channel post textbox
cy.uiGetPostTextBox().click();
// # Press shift+up to open the thread with replies
cy.uiGetPostTextBox().type('{shift+uparrow}', {delay: 50});
// * RHS Opens up
cy.get('.sidebar--right__header').should('be.visible');
// * RHS textbox should be focused
cy.uiGetReplyTextBox().should('be.focused');

View file

@ -15,12 +15,14 @@ import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Messaging', () => {
let offTopicUrl;
let testChannelName;
let user;
before(() => {
// # Login as test user
cy.apiInitSetup({loginAfter: true}).then((out) => {
offTopicUrl = out.offTopicUrl;
testChannelName = out.channel.display_name;
user = out.user;
});
});
@ -97,6 +99,43 @@ describe('Messaging', () => {
cy.uiGetReplyTextBox().should('be.focused');
});
it('MM-T205 Focus to remain in RHS textbox when replying to reply post in center channel (CRT disabled)', () => {
// # Ensure collapsed reply threads is disabled
cy.apiSaveCRTPreference(user.id, 'off');
// # Post a thread root message
cy.postMessage('Thread root message');
// # Open RHS and post a reply
cy.clickPostCommentIcon();
cy.uiGetReplyTextBox().type('First reply{enter}');
// # Close RHS
cy.get('#rhsCloseButton').click();
// * Verify RHS is closed
cy.get('.sidebar--right__header').should('not.exist');
// # Get the reply post ID and click its comment icon
cy.getLastPostId().then((postId) => {
// # Click the reply arrow on the reply post
cy.clickPostCommentIcon(postId);
// * Verify RHS opens and textbox is focused
cy.get('.sidebar--right__header').should('be.visible');
cy.uiGetReplyTextBox().should('be.focused');
// # Focus away from RHS textbox
cy.get('#rhsContainer .post-right__content').click();
// # Click reply arrow on the same reply post again
cy.clickPostCommentIcon(postId);
});
// * Verify RHS textbox is focused again
cy.uiGetReplyTextBox().should('be.focused');
});
it('MM-T203 Focus does not move when it has already been set elsewhere', () => {
// # Verify Focus in add channel member modal
verifyFocusInAddChannelMemberModal();
@ -157,8 +196,8 @@ function verifyFocusInAddChannelMemberModal() {
// * Check that input box has character A
cy.get('#selectItems input').should('have.value', 'A');
// # Click anywhere in the modal that is not on a field that can take focus
cy.get('#deletePostModalLabel > span').click();
// # Remove the focus from the input box
cy.get('#selectItems input').blur();
// * Note the focus has been removed from the search box
cy.get('#selectItems input').should('not.be.focused');

View file

@ -236,7 +236,9 @@ describe('components/avanced_text_editor/advanced_text_editor', () => {
expect(textbox).not.toHaveFocus();
// save is called with a short delayed after pressing escape key
jest.advanceTimersByTime(Constants.SAVE_DRAFT_TIMEOUT + 50);
act(() => {
jest.advanceTimersByTime(Constants.SAVE_DRAFT_TIMEOUT + 50);
});
expect(mockedRemoveDraft).toHaveBeenCalled();
expect(mockedUpdateDraft).not.toHaveBeenCalled();
});

View file

@ -33,11 +33,20 @@ const useTextboxFocus = (
const focusTextbox = useCallback((keepFocus = false) => {
const postTextboxDisabled = !canPost;
if (textboxRef.current && postTextboxDisabled) {
textboxRef.current.blur(); // Fixes Firefox bug which causes keyboard shortcuts to be ignored (MM-22482)
// Fixes Firefox bug which causes keyboard shortcuts to be ignored (MM-22482)
requestAnimationFrame(() => {
textboxRef.current?.blur();
});
return;
}
if (textboxRef.current && (keepFocus || !UserAgent.isMobile())) {
textboxRef.current.focus();
// Focus immediately, so we capture any typed text.
textboxRef.current?.focus();
// Also re-focus after the next animation frame, to work around issues where the RHS is "opening".
requestAnimationFrame(() => {
textboxRef.current?.focus();
});
}
}, [canPost, textboxRef]);