diff --git a/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_image_spec.js b/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_image_spec.js
index c5963ef7e1..5703f9c38a 100644
--- a/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_image_spec.js
+++ b/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_image_spec.js
@@ -26,9 +26,9 @@ describe('Verify Accessibility Support in Different Images', () => {
it('MM-T1508 Accessibility support in different images', () => {
// * Verify image alt in profile image
- cy.uiGetProfileHeader().
- find('.Avatar').
- should('have.attr', 'alt', 'user profile image');
+ cy.get('#userAccountMenuButton').within(() => {
+ cy.findByAltText('user profile image').should('be.visible');
+ });
// # Upload an image in the post
cy.get('#fileUploadInput').attachFile('small-image.png');
diff --git a/e2e-tests/cypress/tests/integration/channels/account_settings/profile/email_spec.ts b/e2e-tests/cypress/tests/integration/channels/account_settings/profile/email_spec.ts
index 44bac52cc8..8a264d2579 100644
--- a/e2e-tests/cypress/tests/integration/channels/account_settings/profile/email_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/account_settings/profile/email_spec.ts
@@ -163,9 +163,6 @@ describe('Profile > Profile Settings > Email', () => {
// # Click on the link
cy.visit(permalink);
- // * Verify announcement bar
- cy.get('.announcement-bar').should('be.visible').should('contain.text', 'Email verified');
-
// # Wait for one second for the mail to be sent out.
cy.wait(TIMEOUTS.FIVE_SEC);
@@ -199,7 +196,7 @@ describe('Profile > Profile Settings > Email', () => {
// # Close modal then logout
cy.get('body').type('{esc}');
- cy.uiOpenUserMenu('Log Out');
+ cy.uiLogout();
// # Wait for one second for the mail to be sent out.
cy.wait(TIMEOUTS.ONE_SEC);
diff --git a/e2e-tests/cypress/tests/integration/channels/account_settings/profile/username_spec.ts b/e2e-tests/cypress/tests/integration/channels/account_settings/profile/username_spec.ts
index 7fe8a4d767..6e2c5a7c6b 100644
--- a/e2e-tests/cypress/tests/integration/channels/account_settings/profile/username_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/account_settings/profile/username_spec.ts
@@ -116,8 +116,8 @@ describe('Settings > Sidebar > General > Edit', () => {
// # Step 3
// * Verify that we've logged in as the temp user
cy.visit(offTopicUrl);
- cy.uiOpenUserMenu().findByText(`@${newTempUserName}`);
- cy.uiGetSetStatusButton().click();
+ cy.uiOpenUserMenu().findByText(`@${newTempUserName}`).should('exist');
+ cy.get('body').type('{esc}');
// # Step 4
const text = `${newTempUserName} test message!`;
diff --git a/e2e-tests/cypress/tests/integration/channels/archived_channel/join_archived_channel_spec.ts b/e2e-tests/cypress/tests/integration/channels/archived_channel/join_archived_channel_spec.ts
index 231d8579ff..e4dd367d1a 100644
--- a/e2e-tests/cypress/tests/integration/channels/archived_channel/join_archived_channel_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/archived_channel/join_archived_channel_spec.ts
@@ -124,5 +124,5 @@ function verifyUsername(username) {
cy.uiOpenUserMenu().findByText(`@${username}`);
// # Close the user menu
- cy.uiGetSetStatusButton().click();
+ cy.get('body').type('{esc}');
}
diff --git a/e2e-tests/cypress/tests/integration/channels/channel_sidebar/sidebar_category_menu_spec.ts b/e2e-tests/cypress/tests/integration/channels/channel_sidebar/sidebar_category_menu_spec.ts
index 16f3e9cf1a..5413cae2de 100644
--- a/e2e-tests/cypress/tests/integration/channels/channel_sidebar/sidebar_category_menu_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/channel_sidebar/sidebar_category_menu_spec.ts
@@ -1,6 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
+import * as TIMEOUTS from '../../../fixtures/timeouts';
+
import {clickCategoryMenuItem} from './helpers';
// ***************************************************************
@@ -22,6 +24,8 @@ describe('Sidebar category menu', () => {
it('MM-T3171_1 Verify that the 3-dot menu on the Channels Category contains an option to Create New Category', () => {
clickCategoryMenuItem('CHANNELS', 'Create New Category');
+ cy.wait(TIMEOUTS.ONE_SEC);
+
cy.get('body').type('{esc}', {force: true});
});
@@ -41,6 +45,8 @@ describe('Sidebar category menu', () => {
// # Verify that Create New Category exists on Favorites category and click on it
clickCategoryMenuItem('FAVORITES', 'Create New Category');
+ cy.wait(TIMEOUTS.ONE_SEC);
+
cy.get('body').type('{esc}', {force: true});
});
});
diff --git a/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_2_spec.ts b/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_2_spec.ts
index 8fc58cc757..16f453e945 100644
--- a/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_2_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_2_spec.ts
@@ -28,15 +28,23 @@ describe('Custom Status - Setting a Custom Status', () => {
it('MM-T3836_1 should open status dropdown', () => {
// # Click on the sidebar header to open status dropdown
- cy.get('.MenuWrapper .status-wrapper').click();
+ cy.uiGetSetStatusButton().click();
// * Check if the status dropdown opens
- cy.get('#statusDropdownMenu').should('exist');
+ cy.uiGetStatusMenuContainer();
+
+ // # Close the status dropdown
+ cy.get('body').type('{esc}');
});
it('MM-T3836_2 Custom status modal opens with 5 default statuses listed', () => {
+ // # Click on the sidebar header to open status dropdown
+ cy.uiGetSetStatusButton().click();
+
+ // * Check if the status dropdown opens
+ cy.uiGetStatusMenuContainer().findByText('Set custom status').click();
+
// # Open custom status modal
- cy.get('#statusDropdownMenu li#status-menu-custom-status').click();
cy.get('#custom_status_modal').should('exist');
// * Check if all the default suggestions exist
@@ -111,13 +119,15 @@ describe('Custom Status - Setting a Custom Status', () => {
trigger('mouseover');
// * Custom status tooltip should be visible
- cy.get('#custom-status-tooltip').should('exist');
+ cy.get('#custom-status-tooltip').should('exist').and('be.visible');
- // * Tooltip should contain the correct custom status emoji
- cy.get('#custom-status-tooltip .custom-status span.emoticon').invoke('attr', 'data-emoticon').should('contain', customStatus.emoji);
+ cy.get('#custom-status-tooltip').within(() => {
+ // * Tooltip should contain the correct custom status emoji
+ cy.get(`span[data-emoticon="${customStatus.emoji}"]`).should('exist');
- // * Tooltip should contain the correct custom status text
- cy.get('#custom-status-tooltip .custom-status span.custom-status-text').should('have.text', customStatus.text);
+ // * Tooltip should contain the correct custom status text
+ cy.findByText(customStatus.text).should('exist');
+ });
});
it('MM-T3836_8 should open custom status modal when emoji in LHS header is clicked', () => {
diff --git a/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_3_spec.ts b/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_3_spec.ts
index b9a69f6671..ad1591b439 100644
--- a/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_3_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_3_spec.ts
@@ -27,7 +27,7 @@ describe('Custom Status - Setting Your Own Custom Status', () => {
it('MM-T3846_1 should change the emoji to speech balloon when typed in the input', () => {
// # Open the custom status modal
- cy.uiOpenUserMenu('Set a custom status');
+ cy.uiOpenUserMenu('Set custom status');
// * Default emoji is currently visible in the custom status input
cy.get('#custom_status_modal .StatusModal__emoji-button span').should('have.class', 'icon--emoji');
@@ -72,20 +72,26 @@ describe('Custom Status - Setting Your Own Custom Status', () => {
cy.uiGetProfileHeader().
find('.emoticon').
should('have.attr', 'data-emoticon', customStatus.emoji);
+
+ cy.get('body').type('{esc}');
});
it('MM-T3846_5 should show custom status with emoji in the status dropdown', () => {
// # Open user menu
- cy.uiOpenUserMenu();
+ cy.uiOpenUserMenu().within(() => {
+ // * Correct custom status text and emoji should be displayed in the status dropdown
+ cy.findByText(customStatus.text).should('exist');
+ cy.get(`span[data-emoticon="${customStatus.emoji}"]`).should('exist');
+ });
- // * Correct custom status text and emoji should be displayed in the status dropdown
- cy.get('.status-dropdown-menu .custom_status__container').should('have.text', customStatus.text);
- cy.get('.status-dropdown-menu .custom_status__row span.emoticon').invoke('attr', 'data-emoticon').should('contain', customStatus.emoji);
+ cy.get('body').type('{esc}');
});
- it('MM-T3846_6 should show previosly set status in the first position in Recents list', () => {
+ it('MM-T3846_6 should show previously set status in the first position in Recents list', () => {
// # Click on the "Set a Custom Status" option in the status dropdown
- cy.get('.status-dropdown-menu li#status-menu-custom-status').click();
+ cy.uiOpenUserMenu().within(() => {
+ cy.findByText(customStatus.text).should('exist').click({force: true});
+ });
// * Custom status modal should open
cy.get('#custom_status_modal').should('exist');
@@ -118,9 +124,9 @@ describe('Custom Status - Setting Your Own Custom Status', () => {
cy.findByText('Clear Status').click();
// # Open user menu
- cy.uiOpenUserMenu();
-
- // * Custom status text should not be displayed in the status dropdown
- cy.get('.status-dropdown-menu .custom_status__row').should('not.have.text', customStatus.text);
+ cy.uiOpenUserMenu().within(() => {
+ // * Custom status text should not be displayed in the status dropdown
+ cy.findByText(customStatus.text).should('not.exist');
+ });
});
});
diff --git a/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_4_spec.ts b/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_4_spec.ts
index 8daaf69319..8caddd651e 100644
--- a/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_4_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_4_spec.ts
@@ -32,7 +32,7 @@ describe('Custom Status - Recent Statuses', () => {
it('MM-T3847_1 set a status', () => {
// # Open the custom status modal
- cy.uiOpenUserMenu('Set a custom status');
+ cy.uiOpenUserMenu('Set custom status');
// # Type the custom status text in the custom status modal input
cy.get('#custom_status_modal .StatusModal__input input').typeWithForce(customStatus.text);
diff --git a/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_5_spec.ts b/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_5_spec.ts
index 1cc20c8d76..764e810180 100644
--- a/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_5_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_5_spec.ts
@@ -27,7 +27,7 @@ describe('Custom Status - Verifying Where Custom Status Appears', () => {
});
it('MM-T3850_1 set a status', () => {
- cy.uiOpenUserMenu('Set a Custom Status');
+ cy.uiOpenUserMenu('Set custom status');
// # Type the custom status text in the custom status modal input
cy.findByPlaceholderText('Set a status').type(customStatus.text, {force: true});
diff --git a/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_expiry/custom_status_expiry_1_spec.ts b/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_expiry/custom_status_expiry_1_spec.ts
index 13ae7afbb4..d09034c61b 100644
--- a/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_expiry/custom_status_expiry_1_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_expiry/custom_status_expiry_1_spec.ts
@@ -25,6 +25,7 @@ describe('MM-T4063 Custom status expiry', () => {
const defaultCustomStatuses = ['In a meeting', 'Out for lunch', 'Out sick', 'Working from home', 'On a vacation'];
const customStatus = {
emoji: 'hamburger',
+ emojiAriaLabel: ':hamburger:',
text: 'Out for lunch',
duration: '30 minutes',
};
@@ -36,19 +37,25 @@ describe('MM-T4063 Custom status expiry', () => {
const expiryTimeFormat = 'h:mm A';
it('MM-T4063_1 should open status dropdown', () => {
// # Click on the sidebar header to open status dropdown
- cy.get('.MenuWrapper .status-wrapper').click();
+ cy.uiGetSetStatusButton().click();
// * Check if the status dropdown opens
- cy.get('#statusDropdownMenu').should('exist');
+ cy.uiGetStatusMenu();
+
+ // # Close the status dropdown
+ cy.get('body').click();
});
it('MM-T4063_2 Custom status modal opens with 5 default statuses listed', () => {
// # Open custom status modal
- cy.get('#statusDropdownMenu li#status-menu-custom-status').click();
- cy.get('#custom_status_modal').should('exist');
+ cy.uiOpenUserMenu('Set custom status');
- // * Check if all the default suggestions exist
- defaultCustomStatuses.map((statusText) => cy.get('#custom_status_modal .statusSuggestion__content').contains('span', statusText));
+ cy.findByRole('dialog', {name: 'Set a status'}).should('exist').within(() => {
+ // * Check if all the default suggestions exist
+ defaultCustomStatuses.forEach((statusText) => {
+ cy.findByText(statusText).should('exist');
+ });
+ });
});
it('MM-T4063_3 Correct custom status is selected with the correct emoji and correct duration', () => {
@@ -94,17 +101,17 @@ describe('MM-T4063 Custom status expiry', () => {
it('MM-T4063_5 should show the set custom status with expiry when status dropdown is opened', () => {
// # Click on the sidebar header to open status dropdown
- cy.get('.MenuWrapper .status-wrapper').click();
+ cy.uiGetSetStatusButton().click();
// * Check if the status dropdown opens
- cy.get('#statusDropdownMenu').should('exist');
+ cy.uiGetStatusMenu().within(() => {
+ // * Correct custom status text and emoji should be displayed in the status dropdown
+ cy.findByText(customStatus.text).should('exist');
+ cy.findByLabelText(customStatus.emojiAriaLabel).should('exist');
- // * Correct custom status text and emoji should be displayed in the status dropdown
- cy.get('.status-dropdown-menu .custom_status__container').should('have.text', customStatus.text);
- cy.get('.status-dropdown-menu .custom_status__row span.emoticon').invoke('attr', 'data-emoticon').should('contain', customStatus.emoji);
-
- // * Correct clear time should be displayed in the status dropdown
- cy.get('.status-dropdown-menu .custom_status__expiry time').invoke('text').should('match', expiresAtRegexp);
+ // * Correct clear time should be displayed in the status dropdown
+ cy.findByText(expiresAtRegexp).should('exist');
+ });
});
it('MM-T4063_6 custom status should be cleared after duration of set custom status', () => {
@@ -118,8 +125,7 @@ describe('MM-T4063 Custom status expiry', () => {
it('MM-T4063_7 current custom status should display expiry time in custom status modal', () => {
// # Open custom status modal
- cy.get('#statusDropdownMenu li#status-menu-custom-status').click();
- cy.get('#custom_status_modal').should('exist');
+ cy.get('.userAccountMenu_customStatusMenuItem').should('be.visible').click();
// * Should show expiry time of status when current status is selected
cy.get('#custom_status_modal .statusSuggestion__content').contains('span', customStatus.text).click();
@@ -132,15 +138,12 @@ describe('MM-T4063 Custom status expiry', () => {
it('MM-T4063_8 previous custom status duration should be reset if custom status is expired', () => {
// # Forwarding the time by the duration of custom status
cy.clock(Date.now());
- cy.tick(waitingTime * 60 * 1000);
-
- // # Open custom status modal
- cy.get('.status-dropdown-menu').click();
- cy.get('#statusDropdownMenu li#status-menu-custom-status').click();
- cy.get('#custom_status_modal').should('exist');
-
- // * Should show duration of previous status when previous status is selected
- cy.get('#custom_status_modal .statusSuggestion__content').contains('span', customStatus.text).click();
- cy.get('#custom_status_modal .expiry-value').should('have.text', customStatus.duration);
+ cy.tick(waitingTime * 60 * 1000).then(() => {
+ // # Open custom status modal
+ cy.uiOpenUserMenu().within(() => {
+ // * Verify that there is no custom status in the dropdown
+ cy.findByText('Set custom status').should('exist').and('be.visible');
+ });
+ });
});
});
diff --git a/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_expiry/custom_status_expiry_2_spec.ts b/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_expiry/custom_status_expiry_2_spec.ts
index a15928b29c..61474e4ae8 100644
--- a/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_expiry/custom_status_expiry_2_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_expiry/custom_status_expiry_2_spec.ts
@@ -21,86 +21,30 @@ describe('MM-T4064 Status expiry visibility', () => {
cy.visit(channelUrl);
});
});
-
- const defaultCustomStatuses = ['In a meeting', 'Out for lunch', 'Out sick', 'Working from home', 'On a vacation'];
- const customStatus = {
- emoji: 'calendar',
- text: 'In a meeting',
- duration: '1 hour',
- };
-
const waitingTime = 60; //minutes
let expiresAt = dayjs();
const expiryTimeFormat = 'h:mm A';
- it('MM-T4064_1 should open status dropdown', () => {
- // # Click on the sidebar header to open status dropdown
- cy.get('.MenuWrapper .status-wrapper').click();
- // * Check if the status dropdown opens
- cy.get('#statusDropdownMenu').should('exist');
- });
+ it('MM-T4064_6 should show expiry time in the tooltip of custom status emoji in the post header', () => {
+ // # Open the user account menu
+ cy.uiOpenUserMenu('Set custom status');
- it('MM-T4064_2 Custom status modal opens with 5 default statuses listed', () => {
- // # Open custom status modal
- cy.get('#statusDropdownMenu li#status-menu-custom-status').click();
- cy.get('#custom_status_modal').should('exist');
+ // * Verify that the custom status modal opens
+ cy.findByRole('dialog', {name: 'Set a status'}).should('exist').within(() => {
+ // # Select a custom status from the suggestions
+ cy.get('.statusSuggestion__row').first().click();
- // * Check if all the default suggestions exist
- defaultCustomStatuses.map((statusText) => cy.get('#custom_status_modal .statusSuggestion__content').contains('span', statusText));
- });
-
- it('MM-T4064_3 Correct custom status is selected with the correct emoji and correct duration', () => {
- // * Default emoji is currently visible in the custom status input
- cy.get('#custom_status_modal .StatusModal__emoji-button span').should('have.class', 'icon--emoji');
-
- // * Input should be empty
- cy.get('#custom_status_modal input.form-control').should('have.value', '');
-
- // # Select a custom status from the suggestions
- cy.get('#custom_status_modal .statusSuggestion__content').contains('span', customStatus.text).click();
-
- // * Emoji in the custom status input should be changed
- cy.get('#custom_status_modal .StatusModal__emoji-button span').invoke('attr', 'data-emoticon').should('contain', customStatus.emoji);
-
- // * Selected custom status text should be in the input
- cy.get('#custom_status_modal input.form-control').should('have.value', customStatus.text);
-
- // * Selected custom status duration should be displayed in the Clear after section
- cy.get('#custom_status_modal .expiry-wrapper .expiry-value').should('have.text', customStatus.duration);
- });
-
- it('MM-T4064_4 should set custom status when click on Set Status', () => {
- // # Click on the Set Status button
- cy.get('#custom_status_modal .GenericModal__button.confirm').click();
+ // # Click on the Set Status button
+ cy.findByText('Set Status').click();
+ });
// * Modal should be closed
cy.get('#custom_status_modal').should('not.exist');
// # Setting the time at which the custom status should be expired
+ // # Note that we need to be flexible around accepted values, as this calculation and the server-side one may differ slightly
expiresAt = dayjs().add(waitingTime, 'minute');
- // * Status should be set and the emoji should be visible in the sidebar header
- cy.uiGetProfileHeader().
- find('.emoticon').
- should('have.attr', 'data-emoticon', customStatus.emoji);
- });
-
- it('MM-T4064_5 should show the set custom status with expiry when status dropdown is opened', () => {
- // # Click on the sidebar header to open status dropdown
- cy.get('.MenuWrapper .status-wrapper').click();
-
- // * Check if the status dropdown opens
- cy.get('#statusDropdownMenu').should('exist');
-
- // * Correct custom status text and emoji should be displayed in the status dropdown
- cy.get('.status-dropdown-menu .custom_status__container').should('have.text', customStatus.text);
- cy.get('.status-dropdown-menu .custom_status__row span.emoticon').invoke('attr', 'data-emoticon').should('contain', customStatus.emoji);
-
- // * Correct clear time should be displayed in the status dropdown
- cy.get('.status-dropdown-menu .custom_status__expiry time').should('have.text', expiresAt.format(expiryTimeFormat));
- });
-
- it('MM-T4064_6 should show expiry time in the tooltip of custom status emoji in the post header', () => {
// # Post a message in the channel
cy.postMessage('Hello World!');
diff --git a/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_expiry/custom_status_expiry_3_spec.ts b/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_expiry/custom_status_expiry_3_spec.ts
index 67eaebd3ae..ede43e3f20 100644
--- a/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_expiry/custom_status_expiry_3_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_expiry/custom_status_expiry_3_spec.ts
@@ -39,15 +39,15 @@ describe('MM-T4065 Setting manual status clear time less than 7 days away', () =
it('MM-T4065_1 should open status dropdown', () => {
// # Click on the sidebar header to open status dropdown
- cy.get('.MenuWrapper .status-wrapper').click();
+ cy.uiGetSetStatusButton().click();
// * Check if the status dropdown opens
- cy.get('#statusDropdownMenu').should('exist');
+ cy.get('#userAccountMenu').should('exist');
});
it('MM-T4065_2 Custom status modal opens with 5 default statuses listed', () => {
// # Open custom status modal
- cy.get('#statusDropdownMenu li#status-menu-custom-status').click();
+ cy.get('.userAccountMenu_setCustomStatusMenuItem').click();
cy.get('#custom_status_modal').should('exist');
// * Check if all the default suggestions exist
diff --git a/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_expiry/custom_status_expiry_4_spec.ts b/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_expiry/custom_status_expiry_4_spec.ts
index d75de18c17..7307dc3709 100644
--- a/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_expiry/custom_status_expiry_4_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/custom_status/custom_status_expiry/custom_status_expiry_4_spec.ts
@@ -38,15 +38,15 @@ describe('MM-T4066 Setting manual status clear time more than 7 days away', () =
const months = dateToBeSelected.get('month') - today.get('month');
it('MM-T4066_1 should open status dropdown', () => {
// # Click on the sidebar header to open status dropdown
- cy.get('.MenuWrapper .status-wrapper').click();
+ cy.uiGetSetStatusButton().click();
// * Check if the status dropdown opens
- cy.get('#statusDropdownMenu').should('exist');
+ cy.get('#userAccountMenu').should('exist');
});
it('MM-T4066_2 Custom status modal opens with 5 default statuses listed', () => {
// # Open custom status modal
- cy.get('#statusDropdownMenu li#status-menu-custom-status').click();
+ cy.get('.userAccountMenu_setCustomStatusMenuItem').click();
cy.get('#custom_status_modal').should('exist');
// * Check if all the default suggestions exist
diff --git a/e2e-tests/cypress/tests/integration/channels/integrations/builtin_commands/user_status_commands_spec.js b/e2e-tests/cypress/tests/integration/channels/integrations/builtin_commands/user_status_commands_spec.js
index d44747c6d0..840be19f45 100644
--- a/e2e-tests/cypress/tests/integration/channels/integrations/builtin_commands/user_status_commands_spec.js
+++ b/e2e-tests/cypress/tests/integration/channels/integrations/builtin_commands/user_status_commands_spec.js
@@ -12,10 +12,10 @@
describe('Integrations', () => {
const testCases = [
- {command: '/away', className: 'icon-clock', message: 'You are now away'},
- {command: '/dnd', className: 'icon-minus-circle', message: 'Do Not Disturb is enabled. You will not receive desktop or mobile push notifications until Do Not Disturb is turned off.'},
- {command: '/offline', className: 'icon-circle-outline', message: 'You are now offline'},
- {command: '/online', className: 'icon-check-circle', message: 'You are now online'},
+ {command: '/away', className: 'userAccountMenu_awayMenuItem_icon', message: 'You are now away'},
+ {command: '/dnd', className: 'userAccountMenu_dndMenuItem_icon', message: 'Do Not Disturb is enabled. You will not receive desktop or mobile push notifications until Do Not Disturb is turned off.'},
+ {command: '/offline', className: 'userAccountMenu_offlineMenuItem_icon', message: 'You are now offline'},
+ {command: '/online', className: 'userAccountMenu_onlineMenuItem_icon', message: 'You are now online'},
];
let offTopicUrl;
@@ -67,7 +67,7 @@ describe('Integrations', () => {
function verifyUserStatus(testCase, isCompactMode) {
// * Verify that the user status is as indicated
cy.uiGetProfileHeader().
- find('i').
+ find('svg').
should('be.visible').
and('have.class', testCase.className);
diff --git a/e2e-tests/cypress/tests/integration/channels/integrations/builtin_commands/user_status_spec.js b/e2e-tests/cypress/tests/integration/channels/integrations/builtin_commands/user_status_spec.js
index aadb723205..c5487cfdb6 100644
--- a/e2e-tests/cypress/tests/integration/channels/integrations/builtin_commands/user_status_spec.js
+++ b/e2e-tests/cypress/tests/integration/channels/integrations/builtin_commands/user_status_spec.js
@@ -26,14 +26,14 @@ describe('Integrations', () => {
it('MM-T670 /away', () => {
// # Set online status and verify it's changed as the initial status
- setStatus(online.name, online.profileClassName);
+ setStatus(online.name);
verifyUserStatus(away);
});
it('MM-T672 /offline', () => {
// # Set online status and verify it's changed as the initial status
- setStatus(online.name, online.profileClassName);
+ setStatus(online.name);
verifyUserStatus(offline);
@@ -60,17 +60,14 @@ describe('Integrations', () => {
it('MM-T674 /online', () => {
// # Set offline status and verify it's changed as the initial status
- setStatus(offline.name, offline.className);
+ setStatus(offline.name);
verifyUserStatus(online);
});
});
-function setStatus(status, icon) {
+function setStatus(status) {
cy.apiUpdateUserStatus(status);
- cy.uiGetProfileHeader().
- find('i').
- and('have.class', icon);
}
function verifyUserStatus(testCase) {
@@ -90,9 +87,7 @@ function verifyUserStatus(testCase) {
});
// * Verify status shown at user profile in LHS
- cy.uiGetProfileHeader().
- find('i').
- and('have.class', testCase.profileClassName || testCase.className);
+ cy.get('#userAccountMenuButtonDescribedBy').should('exist').and('include.text', `Status is "${testCase.name.charAt(0).toUpperCase() + testCase.name.slice(1)}"`);
// # Post a message
cy.postMessage(testCase.name);
diff --git a/e2e-tests/cypress/tests/integration/channels/menus/main_menu_spec.js b/e2e-tests/cypress/tests/integration/channels/menus/main_menu_spec.js
index 4cf116830a..ff5770ac3c 100644
--- a/e2e-tests/cypress/tests/integration/channels/menus/main_menu_spec.js
+++ b/e2e-tests/cypress/tests/integration/channels/menus/main_menu_spec.js
@@ -33,10 +33,9 @@ describe('Main menu', () => {
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/town-square`);
- cy.uiOpenUserMenu();
-
- cy.findByRole('menu').should('exist').find('.menu-divider:visible').last().click();
- cy.findByRole('menu').should('exist');
+ cy.uiOpenUserMenu().within(() => {
+ cy.findAllByRole('separator').last().click();
+ });
});
it('should show integrations option for system administrator', () => {
diff --git a/e2e-tests/cypress/tests/integration/channels/menus/status_dropdown_spec.js b/e2e-tests/cypress/tests/integration/channels/menus/status_dropdown_spec.js
index 1d10ee66f0..2e5892b485 100644
--- a/e2e-tests/cypress/tests/integration/channels/menus/status_dropdown_spec.js
+++ b/e2e-tests/cypress/tests/integration/channels/menus/status_dropdown_spec.js
@@ -10,15 +10,14 @@
// Stage: @prod
// Group: @channels @menu @custom_status @status_menu
-import * as TIMEOUTS from '../../../fixtures/timeouts';
import theme from '../../../fixtures/theme.json';
describe('Status dropdown menu', () => {
const statusTestCases = [
- {text: 'Online', className: 'icon-check-circle', profileClassName: 'icon-check-circle'},
- {text: 'Away', className: 'icon-clock'},
- {text: 'Do not disturb', className: 'icon-minus-circle'},
- {text: 'Offline', className: 'icon-circle-outline'},
+ {text: 'Online', className: 'userAccountMenu_onlineMenuItem_icon'},
+ {text: 'Away', className: 'userAccountMenu_awayMenuItem_icon'},
+ {text: 'Do not disturb', className: 'userAccountMenu_dndMenuItem_icon'},
+ {text: 'Offline', className: 'userAccountMenu_offlineMenuItem_icon'},
];
before(() => {
@@ -40,9 +39,10 @@ describe('Status dropdown menu', () => {
// * Verify all available statuses are shown with icon and text
statusTestCases.forEach((tc) => {
- cy.get('@userMenu').findByText(tc.text).
- parent().
- find('i').should('have.class', tc.className);
+ cy.uiGetStatusMenu().within(() => {
+ cy.findByText(tc.text).should('be.visible');
+ cy.get('svg').filter(`.${tc.className}`).should('have.length', 1);
+ });
});
});
@@ -67,7 +67,7 @@ describe('Status dropdown menu', () => {
cy.uiOpenUserMenu().as('userMenu');
// * Verify "Set a Custom Status" header is clickable
- cy.get('@userMenu').findByText('Set a custom status').should('have.css', 'cursor', 'pointer');
+ cy.get('@userMenu').findByText('Set custom status').should('have.css', 'cursor', 'pointer');
});
it('MM-T2927_5 When custom status is disabled, status menu is displayed when status icon is clicked', () => {
@@ -81,22 +81,12 @@ describe('Status dropdown menu', () => {
cy.uiOpenUserMenu();
});
- it('MM-T4420 Should stay open when dnd sub-menu header is clicked', () => {
- // # Open Dnd sub menu and click on header
- cy.uiOpenDndStatusSubMenu().find('#dndSubMenu-header_menuitem').click().then(() => {
- cy.wait(TIMEOUTS.HALF_SEC);
-
- // * Verify that dnd submenu is still visible
- cy.get('body').find('#dndSubMenu-header_menuitem').should('be.visible');
- });
- });
-
it('MM-T4914 Profile menu header is clickable, opens Profile settings', () => {
// # Open user menu
- cy.uiOpenUserMenu().as('userMenu');
-
- // * Verify menu header is clickable
- cy.get('@userMenu').get('.MenuHeader').should('have.css', 'cursor', 'pointer').click();
+ cy.uiOpenUserMenu().within(() => {
+ // * Verify menu header is clickable
+ cy.get('li').first().should('have.css', 'cursor', 'pointer').click();
+ });
// * Verify click on header opens Profile settings modal
cy.findByRole('dialog', {name: 'Profile'}).should('be.visible');
@@ -111,14 +101,14 @@ function stepThroughStatuses(statusTestCases = []) {
statusTestCases.forEach((tc) => {
// # Open user menu and click option
if (tc.text === 'Do not disturb') {
- cy.uiOpenDndStatusSubMenu().find('#dndTime-thirty_minutes_menuitem').click();
+ cy.uiOpenDndStatusSubMenuAndClick30Mins();
} else {
cy.uiOpenUserMenu(tc.text);
}
// # Verify correct status icon is shown on user's profile picture
- cy.uiGetProfileHeader().
- find('i').
- and('have.class', tc.profileClassName || tc.className);
+ cy.uiGetSetStatusButton().within(() => {
+ cy.get('svg').filter(`.${tc.className}`).should('have.length', 1);
+ });
});
}
diff --git a/e2e-tests/cypress/tests/integration/channels/notifications/users_with_same_firstname_channel_mentions_spec.js b/e2e-tests/cypress/tests/integration/channels/notifications/users_with_same_firstname_channel_mentions_spec.js
index 4a230d3ec0..6b1045d0e8 100644
--- a/e2e-tests/cypress/tests/integration/channels/notifications/users_with_same_firstname_channel_mentions_spec.js
+++ b/e2e-tests/cypress/tests/integration/channels/notifications/users_with_same_firstname_channel_mentions_spec.js
@@ -78,7 +78,7 @@ describe('Notifications', () => {
cy.uiOpenUserMenu().findByText(`@${firstUser.username}`);
// # Close the user menu
- cy.uiGetSetStatusButton().click();
+ cy.get('body').type('{esc}');
// * Check that 'Town Square' is currently being selected
cy.get('.active').within(() => {
diff --git a/e2e-tests/cypress/tests/integration/channels/profile_settings/profile_settings_spec.js b/e2e-tests/cypress/tests/integration/channels/profile_settings/profile_settings_spec.js
index b8dc510ef6..827dcb6ec8 100644
--- a/e2e-tests/cypress/tests/integration/channels/profile_settings/profile_settings_spec.js
+++ b/e2e-tests/cypress/tests/integration/channels/profile_settings/profile_settings_spec.js
@@ -71,7 +71,7 @@ describe('Profile Settings', () => {
fileTypes.forEach((fileType, index) => {
it(`MM-T2078_${index + 1} Profile picture: file ${fileType.extension} type accepted`, () => {
// # Save the default profile picture link so it can be compared to the new one
- cy.uiGetProfileHeader().findByRole('img').invoke('attr', 'src').as('defaultProfilePictureLink');
+ cy.uiGetSetStatusButton().get('img').invoke('attr', 'src').as('defaultProfilePictureLink');
cy.uiOpenProfileModal('Profile Settings');
@@ -95,7 +95,7 @@ describe('Profile Settings', () => {
cy.uiClose();
// # Save the new custom profile picture link so it can be compared to the old one
- cy.uiGetProfileHeader().findByRole('img').invoke('attr', 'src').as('customProfilePictureLink');
+ cy.uiGetSetStatusButton().get('img').invoke('attr', 'src').as('customProfilePictureLink');
cy.then(function() {
expect(this.customProfilePictureLink).to.not.equal(this.defaultProfilePictureLink);
diff --git a/e2e-tests/cypress/tests/integration/channels/settings/display/theme/custom_theme_sidebar_styles_spec.js b/e2e-tests/cypress/tests/integration/channels/settings/display/theme/custom_theme_sidebar_styles_spec.js
index 4bf17e1503..ce8c30f1ca 100644
--- a/e2e-tests/cypress/tests/integration/channels/settings/display/theme/custom_theme_sidebar_styles_spec.js
+++ b/e2e-tests/cypress/tests/integration/channels/settings/display/theme/custom_theme_sidebar_styles_spec.js
@@ -92,18 +92,18 @@ describe('Custom Theme - Sidebar Styles', () => {
cy.uiOpenUserMenu('Online');
// * Check Online Indicator color
- cy.get('.icon-check-circle').should('have.css', 'color', rgbArrayToString(themeRgbColor.onlineIndicator));
+ cy.uiGetSetStatusButton().find('svg').should('have.css', 'color', rgbArrayToString(themeRgbColor.onlineIndicator));
// # Set user status to away
cy.uiOpenUserMenu('Away');
// * Check Away Indicator color
- cy.get('.icon-clock').should('have.css', 'color', rgbArrayToString(themeRgbColor.awayIndicator));
+ cy.uiGetSetStatusButton().find('svg').should('have.css', 'color', rgbArrayToString(themeRgbColor.awayIndicator));
// # Set user status to do not disturb
- cy.uiOpenDndStatusSubMenu().find('#dndTime-thirty_minutes_menuitem').click();
+ cy.uiOpenDndStatusSubMenuAndClick30Mins();
// * Check Do Not Disturb Indicator color
- cy.get('.icon-minus-circle').should('have.css', 'color', rgbArrayToString(themeRgbColor.dndIndicator));
+ cy.uiGetSetStatusButton().find('svg').should('have.css', 'color', rgbArrayToString(themeRgbColor.dndIndicator));
});
});
diff --git a/e2e-tests/cypress/tests/integration/channels/signin_authentication/login_logout_smoke_spec.js b/e2e-tests/cypress/tests/integration/channels/signin_authentication/login_logout_smoke_spec.js
index 80ad91fa62..c104181236 100644
--- a/e2e-tests/cypress/tests/integration/channels/signin_authentication/login_logout_smoke_spec.js
+++ b/e2e-tests/cypress/tests/integration/channels/signin_authentication/login_logout_smoke_spec.js
@@ -42,7 +42,7 @@ describe('SignIn Authentication', () => {
cy.url().should('include', '/channels/town-square');
// # Click logout via user menu
- cy.uiOpenUserMenu('Log Out');
+ cy.uiOpenUserMenu('Log out');
// * Check that it logout successfully and it redirects into the login page
cy.url().should('include', '/login');
diff --git a/e2e-tests/cypress/tests/integration/channels/signin_authentication/login_open_server_spec.js b/e2e-tests/cypress/tests/integration/channels/signin_authentication/login_open_server_spec.js
index c4219a7947..bfb0f199d4 100644
--- a/e2e-tests/cypress/tests/integration/channels/signin_authentication/login_open_server_spec.js
+++ b/e2e-tests/cypress/tests/integration/channels/signin_authentication/login_open_server_spec.js
@@ -186,7 +186,7 @@ describe('Login page with open server', () => {
cy.url().should('include', '/channels/town-square');
// # Click logout via user menu
- cy.uiOpenUserMenu('Log Out');
+ cy.uiOpenUserMenu('Log out');
// * Check that it logout successfully and it redirects into the login page
cy.url().should('include', '/login');
@@ -209,7 +209,7 @@ describe('Login page with open server', () => {
cy.url().should('include', '/channels/town-square');
// # Click logout via user menu
- cy.uiOpenUserMenu('Log Out');
+ cy.uiOpenUserMenu('Log out');
// * Check that it logout successfully and it redirects into the login page
cy.url().should('include', '/login');
diff --git a/e2e-tests/cypress/tests/integration/channels/status/status_updates_spec.ts b/e2e-tests/cypress/tests/integration/channels/status/status_updates_spec.ts
index 78b904ce6b..bbe6bbdc18 100644
--- a/e2e-tests/cypress/tests/integration/channels/status/status_updates_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/status/status_updates_spec.ts
@@ -92,12 +92,12 @@ describe('Status of current user', () => {
});
function verifyStatus(status: 'online' | 'away' | 'offline' | 'dnd') {
- cy.get('[aria-label="Current status: Online. Select to open profile and status menu."]').
+ cy.get('[aria-label="Status is \\"Online\\". Open user\'s account menu."]').
should(status === 'online' ? 'exist' : 'not.exist');
- cy.get('[aria-label="Current status: Away. Select to open profile and status menu."]').
+ cy.get('[aria-label="Status is \\"Away\\". Open user\'s account menu."]').
should(status === 'away' ? 'exist' : 'not.exist');
- cy.get('[aria-label="Current status: Offline. Select to open profile and status menu."]').
+ cy.get('[aria-label="Status is \\"Offline\\". Open user\'s account menu."]').
should(status === 'offline' ? 'exist' : 'not.exist');
- cy.get('[aria-label="Current status: Do not disturb. Select to open profile and status menu."]').
+ cy.get('[aria-label="Status is \\"Do not disturb\\". Open user\'s account menu."]').
should(status === 'dnd' ? 'exist' : 'not.exist');
}
diff --git a/e2e-tests/cypress/tests/support/common_login_commands.js b/e2e-tests/cypress/tests/support/common_login_commands.js
index 8cf8efc7dd..f732258575 100644
--- a/e2e-tests/cypress/tests/support/common_login_commands.js
+++ b/e2e-tests/cypress/tests/support/common_login_commands.js
@@ -53,7 +53,7 @@ Cypress.Commands.add('checkLeftSideBar', (settings = {}) => {
cy.uiOpenUserMenu().findByText(`@${settings.user.username}`);
// # Close status menu
- cy.uiGetSetStatusButton().click();
+ cy.get('body').type('{esc}');
}
if (settings.user.userType === 'Admin' || settings.user.isAdmin) {
diff --git a/e2e-tests/cypress/tests/support/ui/global_header.js b/e2e-tests/cypress/tests/support/ui/global_header.js
index 64dc75ed45..4800e2841e 100644
--- a/e2e-tests/cypress/tests/support/ui/global_header.js
+++ b/e2e-tests/cypress/tests/support/ui/global_header.js
@@ -27,7 +27,8 @@ Cypress.Commands.add('uiOpenProductMenu', (item = '') => {
});
Cypress.Commands.add('uiGetSetStatusButton', () => {
- return cy.findByRole('button', {name: /Select to open profile and status menu\./i}).should('be.visible');
+ // # Get set status button
+ return cy.get('#userAccountMenuButton').should('be.visible');
});
Cypress.Commands.add('uiGetProfileHeader', () => {
@@ -36,22 +37,18 @@ Cypress.Commands.add('uiGetProfileHeader', () => {
Cypress.Commands.add('uiGetStatusMenuContainer', (options = {exist: true}) => {
if (options.exist) {
- return cy.findByRole('menu').should('exist');
+ return cy.get('#userAccountMenu').should('exist').and('be.visible');
}
- return cy.findByRole('menu').should('not.exist');
+ return cy.get('#userAccountMenu').should('not.exist');
});
Cypress.Commands.add('uiGetStatusMenu', (options = {visible: true}) => {
if (options.visible) {
- return cy.uiGetStatusMenuContainer().
- find('ul').
- should('be.visible');
+ return cy.get('#userAccountMenu').should('exist').and('be.visible');
}
- return cy.uiGetStatusMenuContainer().
- find('ul').
- should('not.be.visible');
+ return cy.get('#userAccountMenu').should('not.exist');
});
Cypress.Commands.add('uiOpenHelpMenu', (item = '') => {
@@ -147,7 +144,7 @@ Cypress.Commands.add('uiOpenSettingsModal', (section = '') => {
Cypress.Commands.add('uiLogout', () => {
// # Click logout via user menu
- cy.uiOpenUserMenu('Log Out');
+ cy.uiOpenUserMenu('Log out');
cy.url().should('include', '/login');
cy.get('.login-body-message').should('be.visible');
diff --git a/e2e-tests/cypress/tests/support/ui/menu.js b/e2e-tests/cypress/tests/support/ui/menu.js
index 689f0e2816..6dbed1dfe2 100644
--- a/e2e-tests/cypress/tests/support/ui/menu.js
+++ b/e2e-tests/cypress/tests/support/ui/menu.js
@@ -33,14 +33,8 @@ Cypress.Commands.add('uiGetSystemConsoleMainMenu', () => {
return getMenu(SYSTEM_CONSOLE_MAIN_MENU);
});
-Cypress.Commands.add('uiOpenDndStatusSubMenu', () => {
- cy.uiOpenUserMenu();
+Cypress.Commands.add('uiOpenDndStatusSubMenuAndClick30Mins', () => {
+ cy.uiOpenUserMenu().findByText('Do not disturb').should('be.visible').trigger('mouseover');
- // # Wait for status menu to transition in
- cy.get('.MenuWrapper.status-dropdown-menu .Menu__content.dropdown-menu').should('be.visible');
-
- // # Hover over Do Not Disturb option
- cy.get('.MenuWrapper.status-dropdown-menu .Menu__content.dropdown-menu li#status-menu-dnd_menuitem').trigger('mouseover');
-
- return cy.get('#status-menu-dnd');
+ cy.findByText('30 mins').should('be.visible').click({force: true});
});
diff --git a/webapp/channels/src/actions/views/status_dropdown.test.ts b/webapp/channels/src/actions/views/status_dropdown.test.ts
deleted file mode 100644
index 0b0da7a309..0000000000
--- a/webapp/channels/src/actions/views/status_dropdown.test.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-import {setStatusDropdown} from 'actions/views/status_dropdown';
-import {isStatusDropdownOpen} from 'selectors/views/status_dropdown';
-import configureStore from 'store';
-
-describe('status_dropdown view actions', () => {
- const initialState = {
- views: {
- statusDropdown: {
- isOpen: false,
- },
- },
- };
-
- it('setStatusDropdown should set the status dropdown open or not', () => {
- const store = configureStore(initialState);
-
- store.dispatch(setStatusDropdown(false));
-
- expect(isStatusDropdownOpen(store.getState())).toBe(false);
-
- store.dispatch(setStatusDropdown(true));
-
- expect(isStatusDropdownOpen(store.getState())).toBe(true);
- });
-});
diff --git a/webapp/channels/src/actions/views/status_dropdown.ts b/webapp/channels/src/actions/views/status_dropdown.ts
deleted file mode 100644
index 93fe4493cd..0000000000
--- a/webapp/channels/src/actions/views/status_dropdown.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-import {ActionTypes} from 'utils/constants';
-
-export function setStatusDropdown(open: boolean) {
- return {
- type: ActionTypes.STATUS_DROPDOWN_TOGGLE,
- open,
- };
-}
diff --git a/webapp/channels/src/components/custom_status/custom_status_emoji.tsx b/webapp/channels/src/components/custom_status/custom_status_emoji.tsx
index daa932298c..5cee41e1e4 100644
--- a/webapp/channels/src/components/custom_status/custom_status_emoji.tsx
+++ b/webapp/channels/src/components/custom_status/custom_status_emoji.tsx
@@ -2,6 +2,7 @@
// See LICENSE.txt for license information.
import React, {memo, useMemo, useRef} from 'react';
+import type {MouseEvent, KeyboardEvent} from 'react';
import {useSelector} from 'react-redux';
import {CustomStatusDuration} from '@mattermost/types/users';
@@ -23,7 +24,7 @@ interface Props {
spanStyle?: React.CSSProperties;
emojiStyle?: React.CSSProperties;
userID?: string;
- onClick?: () => void;
+ onClick?: (event: MouseEvent | KeyboardEvent) => void;
}
function CustomStatusEmoji({
diff --git a/webapp/channels/src/components/custom_status/expiry_time.tsx b/webapp/channels/src/components/custom_status/expiry_time.tsx
index f598b6b85d..283461635c 100644
--- a/webapp/channels/src/components/custom_status/expiry_time.tsx
+++ b/webapp/channels/src/components/custom_status/expiry_time.tsx
@@ -57,7 +57,8 @@ const ExpiryTime = ({time, timezone, className, showPrefix, withinBrackets}: Pro
id='custom_status.expiry.until'
defaultMessage='Until {time}'
values={{time: ''}}
- />{' '}
+ />
+ {' '}
>
);
diff --git a/webapp/channels/src/components/emoji/render_emoji.tsx b/webapp/channels/src/components/emoji/render_emoji.tsx
index c9558c2d09..4ed904a032 100644
--- a/webapp/channels/src/components/emoji/render_emoji.tsx
+++ b/webapp/channels/src/components/emoji/render_emoji.tsx
@@ -2,6 +2,7 @@
// See LICENSE.txt for license information.
import React from 'react';
+import type {MouseEvent, KeyboardEvent} from 'react';
import {useSelector} from 'react-redux';
import {getEmojiImageUrl} from 'mattermost-redux/utils/emoji_utils';
@@ -14,7 +15,7 @@ interface ComponentProps {
emojiName: string;
size?: number;
emojiStyle?: React.CSSProperties;
- onClick?: () => void;
+ onClick?: (event: MouseEvent | KeyboardEvent) => void;
}
const RenderEmoji = ({emojiName, emojiStyle, size, onClick}: ComponentProps) => {
diff --git a/webapp/channels/src/components/global_header/right_controls/right_controls.tsx b/webapp/channels/src/components/global_header/right_controls/right_controls.tsx
index defe1f7a5a..e06c09c0f7 100644
--- a/webapp/channels/src/components/global_header/right_controls/right_controls.tsx
+++ b/webapp/channels/src/components/global_header/right_controls/right_controls.tsx
@@ -9,12 +9,12 @@ import type {ProductIdentifier} from '@mattermost/types/products';
import {isCurrentUserGuestUser} from 'mattermost-redux/selectors/entities/users';
-import StatusDropdown from 'components/status_dropdown';
import {OnboardingTourSteps, OnboardingTourStepsForGuestUsers} from 'components/tours';
import {
CustomizeYourExperienceTour,
useShowOnboardingTutorialStep,
} from 'components/tours/onboarding_tour';
+import UserAccountMenu from 'components/user_account_menu';
import Pluggable from 'plugins/pluggable';
import {isChannels} from 'utils/products';
@@ -46,10 +46,6 @@ const StyledCustomizeYourExperienceTour = styled.div`
height: 100%
`;
-const StyledStatusDropdown = styled.div`
- margin-left: 8px;
-`;
-
export type Props = {
productId?: ProductIdentifier;
}
@@ -87,9 +83,7 @@ const RightControls = ({productId = null}: Props): JSX.Element => {
>
) : null
}
-
-
-
+
);
diff --git a/webapp/channels/src/components/menu/index.ts b/webapp/channels/src/components/menu/index.ts
index 66bbd91468..0f21a10199 100644
--- a/webapp/channels/src/components/menu/index.ts
+++ b/webapp/channels/src/components/menu/index.ts
@@ -1,7 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
+import './menu.scss';
+
export {Menu as Container} from './menu';
export {SubMenu} from './sub_menu';
export {MenuItem as Item} from './menu_item';
+export type {FirstMenuItemProps} from './menu_item';
export {MenuItemSeparator as Separator} from './menu_item_separator';
diff --git a/webapp/channels/src/components/menu/menu.scss b/webapp/channels/src/components/menu/menu.scss
new file mode 100644
index 0000000000..f8934f0236
--- /dev/null
+++ b/webapp/channels/src/components/menu/menu.scss
@@ -0,0 +1,28 @@
+@use "utils/variables";
+
+.MuiPopover-root.MuiModal-root.menu_menuStyled {
+ z-index: variables.$z-index-menu;
+
+ & .MuiPaper-root {
+ min-width: 114px;
+ max-width: 496px;
+ max-height: 80vh;
+ background-color: var(--center-channel-bg);
+ box-shadow: var(--elevation-4);
+ }
+ &.AsSubMenu {
+ & .MuiPaper-root {
+ box-shadow: var(--elevation-5);
+ }
+ }
+}
+
+.menuModal {
+ & .modal-header {
+ display: none;
+ }
+
+ & .GenericModal__body {
+ padding: 0;
+ }
+}
diff --git a/webapp/channels/src/components/menu/menu.tsx b/webapp/channels/src/components/menu/menu.tsx
index 76f0a8d2a1..48b20602d1 100644
--- a/webapp/channels/src/components/menu/menu.tsx
+++ b/webapp/channels/src/components/menu/menu.tsx
@@ -1,7 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
+import MuiMenu from '@mui/material/Menu';
import MuiMenuList from '@mui/material/MenuList';
+import type {PopoverOrigin} from '@mui/material/Popover';
+import classNames from 'classnames';
import React, {
useState,
useEffect,
@@ -28,7 +31,8 @@ import Constants, {A11yClassNames} from 'utils/constants';
import {isKeyPressed} from 'utils/keyboard';
import {MenuContext, useMenuContextValue} from './menu_context';
-import {MuiMenuStyled} from './menu_styled';
+
+import './menu.scss';
const MENU_OPEN_ANIMATION_DURATION = 150;
const MENU_CLOSE_ANIMATION_DURATION = 100;
@@ -37,6 +41,7 @@ type MenuButtonProps = {
id: string;
dateTestId?: string;
'aria-label'?: string;
+ 'aria-describedby'?: string;
disabled?: boolean;
class?: string;
as?: keyof JSX.IntrinsicElements;
@@ -55,18 +60,15 @@ type MenuProps = {
'aria-label'?: string;
/**
- * @warning Make the styling of your components such a way that they dont need this handler
+ * @warning Make the styling of your components such a way that they don't need this handler
*/
onToggle?: (isOpen: boolean) => void;
onKeyDown?: (event: KeyboardEvent, forceCloseMenu?: () => void) => void;
width?: string;
}
-const defaultAnchorOrigin = {vertical: 'bottom', horizontal: 'left'};
-const defaultTransformOrigin = {vertical: 'top', horizontal: 'left'};
-
-type VerticalOrigin = 'top' | 'center' | 'bottom';
-type HorizontalOrigin = 'left' | 'center' | 'right';
+const defaultAnchorOrigin = {vertical: 'bottom', horizontal: 'left'} as PopoverOrigin;
+const defaultTransformOrigin = {vertical: 'top', horizontal: 'left'} as PopoverOrigin;
interface Props {
menuButton: MenuButtonProps;
@@ -76,14 +78,8 @@ interface Props {
// Use MUI Anchor Playgroup to try various anchorOrigin
// and transformOrigin values - https://mui.com/material-ui/react-popover/#anchor-playground
- anchorOrigin?: {
- vertical: VerticalOrigin;
- horizontal: HorizontalOrigin;
- };
- transformOrigin?: {
- vertical: VerticalOrigin;
- horizontal: HorizontalOrigin;
- };
+ anchorOrigin?: PopoverOrigin;
+ transformOrigin?: PopoverOrigin;
}
/**
@@ -107,7 +103,7 @@ export function Menu(props: Props) {
const [disableAutoFocusItem, setDisableAutoFocusItem] = useState(false);
const isMenuOpen = Boolean(anchorElement);
- // Callback funtion handler called when menu is closed by escapeKeyDown, backdropClick or tabKeyDown
+ // Callback function handler called when menu is closed by escapeKeyDown, backdropClick or tabKeyDown
function handleMenuClose(event: MouseEvent) {
event.preventDefault();
setAnchorElement(null);
@@ -125,9 +121,9 @@ export function Menu(props: Props) {
setAnchorElement(null);
}
- // Stop sythetic events from bubbling up to the parent
+ // Stop synthetic events from bubbling up to the parent
// @see https://github.com/mui/material-ui/issues/32064
- function handleMenuClick(e: MouseEvent | KeyboardEvent) {
+ function handleMenuClick(e: MouseEvent | KeyboardEvent) {
e.stopPropagation();
}
@@ -193,7 +189,8 @@ export function Menu(props: Props) {
aria-haspopup={true}
aria-expanded={isMenuOpen}
disabled={props.menuButton?.disabled ?? false}
- aria-label={props.menuButton?.['aria-label'] ?? ''}
+ aria-label={props.menuButton?.['aria-label']}
+ aria-describedby={props.menuButton?.['aria-describedby']}
className={props.menuButton?.class ?? ''}
onClick={handleMenuButtonClick}
onMouseDown={handleMenuButtonMouseDown}
@@ -234,21 +231,23 @@ export function Menu(props: Props) {
{renderMenuButton()}
-
{props.children}
-
+
);
diff --git a/webapp/channels/src/components/menu/menu_item.tsx b/webapp/channels/src/components/menu/menu_item.tsx
index a3f766f102..120d36c0bc 100644
--- a/webapp/channels/src/components/menu/menu_item.tsx
+++ b/webapp/channels/src/components/menu/menu_item.tsx
@@ -89,6 +89,25 @@ export interface Props extends MuiMenuItemProps {
children?: ReactNode;
}
+/**
+ * The props for the first menu item to be passed in.
+ * @example
+ *
+ * <-- Container passes the props to the first item
+ *
+ *
+ */
+export type FirstMenuItemProps = Omit<
+Props,
+| 'onClick'
+| 'leadingElement'
+| 'labels'
+| 'trailingElements'
+| 'isDestructive'
+| 'isLabelsRowLayout'
+| 'children'
+>;
+
/**
* To be used as a child of Menu component.
* Checkout Compass's Menu Item(compass.mattermost.com) for terminology, styling and usage guidelines.
@@ -161,7 +180,11 @@ export function MenuItem(props: Props) {
}
// When both primary and secondary labels are passed, we need to apply minor changes to the styling. Check below in styled component for more details.
- const hasSecondaryLabel = labels && labels.props && labels.props.children && Children.count(labels.props.children) === 2;
+ // we count after converting to array as it removes falsy values from labels.props.children
+ const hasSecondaryLabel = labels &&
+ labels.props &&
+ labels.props.children &&
+ Children.count(Children.toArray(labels.props.children)) === 2;
return (
prop !== 'asSubMenu',
-})(
- ({asSubMenu, width}) => ({
- '& .MuiPaper-root': {
- backgroundColor: 'var(--center-channel-bg)',
- boxShadow: `${
- asSubMenu ? 'var(--elevation-5)' : 'var(--elevation-4)'
- }, 0 0 0 1px rgba(var(--center-channel-color-rgb), 0.12) inset`,
- minWidth: '114px',
- maxWidth: '496px',
- maxHeight: '80vh',
- width,
- },
- }),
-);
diff --git a/webapp/channels/src/components/menu/sub_menu.scss b/webapp/channels/src/components/menu/sub_menu.scss
deleted file mode 100644
index b30d419b5c..0000000000
--- a/webapp/channels/src/components/menu/sub_menu.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-.menuModal {
- & .modal-header {
- & > .close {
- visibility: hidden;
- }
- }
-
- & .GenericModal__body {
- padding: 0;
- }
-}
diff --git a/webapp/channels/src/components/menu/sub_menu.tsx b/webapp/channels/src/components/menu/sub_menu.tsx
index 26e3e182c1..a54111f5ea 100644
--- a/webapp/channels/src/components/menu/sub_menu.tsx
+++ b/webapp/channels/src/components/menu/sub_menu.tsx
@@ -1,6 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
+import MuiMenu from '@mui/material/Menu';
import MuiMenuList from '@mui/material/MenuList';
import type {PopoverOrigin} from '@mui/material/Popover';
import React, {
@@ -31,9 +32,6 @@ import {isKeyPressed} from 'utils/keyboard';
import {SubMenuContext, useMenuContextValue} from './menu_context';
import {MenuItem} from './menu_item';
import type {Props as MenuItemProps} from './menu_item';
-import {MuiMenuStyled} from './menu_styled';
-
-import './sub_menu.scss';
interface Props {
id: MenuItemProps['id'];
@@ -41,10 +39,12 @@ interface Props {
labels: MenuItemProps['labels'];
trailingElements?: MenuItemProps['trailingElements'];
isDestructive?: MenuItemProps['isDestructive'];
+ role?: MenuItemProps['role'];
// Menu props
menuId: string;
menuAriaLabel?: string;
+ menuAriaDescribedBy?: string;
forceOpenOnLeft?: boolean; // Most of the times this is not needed, since submenu position is calculated and placed
children: ReactNode;
@@ -57,8 +57,10 @@ export function SubMenu(props: Props) {
labels,
trailingElements,
isDestructive,
+ role,
menuId,
menuAriaLabel,
+ menuAriaDescribedBy,
forceOpenOnLeft,
children,
...rest
@@ -148,6 +150,7 @@ export function SubMenu(props: Props) {
labels,
trailingElements,
isDestructive,
+ role,
onClick: isMobileView ? handleOnClick : undefined, // OnClicks on parent menuItem of subMenu is only needed in mobile view
};
@@ -163,13 +166,13 @@ export function SubMenu(props: Props) {
onMouseLeave={handleMouseLeave}
onKeyDown={handleKeyDown}
>
-
{/* This component is needed here to re enable pointer events for the submenu items which we had to disable above as */}
{/* pointer turns to default as soon as it leaves the parent menu */}
@@ -178,6 +181,7 @@ export function SubMenu(props: Props) {
id={menuId}
component='ul'
aria-label={menuAriaLabel}
+ aria-describedby={menuAriaDescribedBy}
className={A11yClassNames.POPUP}
onKeyDown={handleSubMenuKeyDown}
sx={{
@@ -190,7 +194,7 @@ export function SubMenu(props: Props) {
{children}
-
+
);
}
diff --git a/webapp/channels/src/components/onboarding_tasks/complete_your_profile_tour_tip.tsx b/webapp/channels/src/components/onboarding_tasks/complete_your_profile_tour_tip.tsx
index b8682c9356..369db76804 100644
--- a/webapp/channels/src/components/onboarding_tasks/complete_your_profile_tour_tip.tsx
+++ b/webapp/channels/src/components/onboarding_tasks/complete_your_profile_tour_tip.tsx
@@ -43,7 +43,7 @@ export const CompleteYourProfileTour = () => {