From 1373c05931a32a55797429911106896164679a49 Mon Sep 17 00:00:00 2001
From: M-ZubairAhmed
Date: Wed, 22 Jan 2025 10:40:43 +0530
Subject: [PATCH] [MM-61966] "Browse or Create channel" menu on the right of
team drop-down should use a11y supported menu (#29739)
---
.../channel/archived_channels_1_spec.ts | 40 ++--
.../channels/channel/browse_channels_spec.ts | 10 +-
.../channel/browse_public_channels_spec.ts | 4 +-
.../channel_name_validations_spec.ts | 4 +-
.../category_sorting_1_spec.ts | 7 +-
.../channel_sidebar/category_sorting_spec.ts | 7 +-
.../channel_sidebar/custom_categories_spec.ts | 7 +-
.../new_channel_dropdown_spec.ts | 7 +-
.../accessibility_modals_dialogs_spec.ts | 4 +-
.../enterprise/ldap/ldap_group_sync_spec.ts | 6 +-
.../permissions/team_permissions_spec.ts | 2 +-
.../about/edition_and_license_spec.js | 2 +-
.../existing_channel_name_spec.js | 2 +-
e2e-tests/cypress/tests/support/ui/channel.js | 2 +-
.../tests/support/ui/channel_sidebar.ts | 7 +-
.../cypress/tests/support/ui/sidebar_left.ts | 20 +-
.../src/actions/views/add_channel_dropdown.ts | 7 -
webapp/channels/src/components/menu/index.ts | 1 +
webapp/channels/src/components/menu/menu.tsx | 17 +-
.../src/components/menu/menu_utils.ts | 29 +++
.../complete_your_profile_tour_tip.tsx | 7 +-
.../onboarding_tasks_manager.tsx | 5 +-
.../post_header_custom_status.tsx | 6 +-
.../__snapshots__/sidebar.test.tsx.snap | 3 -
.../sidebar/add_channel_dropdown.tsx | 177 ------------------
.../channel_navigator.test.tsx | 8 +-
.../channels/src/components/sidebar/index.ts | 4 +-
.../src/components/sidebar/sidebar.tsx | 6 +-
.../sidebar/sidebar_header/index.ts | 1 +
.../sidebar_browse_or_add_channel_menu.tsx | 176 +++++++++++++++++
.../sidebar_header/sidebar_header.scss | 10 +
.../sidebar_header/sidebar_header.test.tsx | 8 +-
.../sidebar/sidebar_header/sidebar_header.tsx | 133 ++++---------
.../__snapshots__/thread_footer.test.tsx.snap | 24 +--
webapp/channels/src/components/tours/hooks.ts | 11 +-
.../create_and_join_channels_tour_tip.tsx | 4 +-
.../invite_people_tour_tip.tsx | 4 +-
.../user_account_menu/user_account_menu.tsx | 4 +-
.../__snapshots__/avatars.test.tsx.snap | 36 ++--
.../widgets/users/avatars/avatars.tsx | 2 +-
.../components/with_tooltip/with_tooltip.scss | 2 +-
webapp/channels/src/i18n/en.json | 13 +-
.../reducers/views/add_channel_dropdown.ts | 21 ---
webapp/channels/src/reducers/views/index.ts | 2 -
.../src/sass/components/_buttons.scss | 20 +-
.../channels/src/sass/components/_modal.scss | 1 +
.../src/sass/layout/_sidebar-left.scss | 49 -----
.../channels/src/sass/responsive/_mobile.scss | 4 +-
.../channels/src/sass/utils/_variables.scss | 33 +++-
.../selectors/views/add_channel_dropdown.ts | 4 -
webapp/channels/src/types/store/views.ts | 4 -
webapp/channels/src/utils/constants.tsx | 1 -
webapp/channels/src/utils/keyboard.ts | 17 --
.../components/src/tour_tip/tour_tip.scss | 5 +-
.../components/src/tour_tip/tour_tip.tsx | 5 +-
55 files changed, 449 insertions(+), 546 deletions(-)
create mode 100644 webapp/channels/src/components/menu/menu_utils.ts
delete mode 100644 webapp/channels/src/components/sidebar/add_channel_dropdown.tsx
create mode 100644 webapp/channels/src/components/sidebar/sidebar_header/sidebar_browse_or_add_channel_menu.tsx
delete mode 100644 webapp/channels/src/reducers/views/add_channel_dropdown.ts
diff --git a/e2e-tests/cypress/tests/integration/channels/channel/archived_channels_1_spec.ts b/e2e-tests/cypress/tests/integration/channels/channel/archived_channels_1_spec.ts
index 215fb828d4..206d8abc8b 100644
--- a/e2e-tests/cypress/tests/integration/channels/channel/archived_channels_1_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/channel/archived_channels_1_spec.ts
@@ -94,11 +94,8 @@ describe('Leave an archived channel', () => {
// # Archive the channel
cy.uiArchiveChannel();
- // # Click on add channel button
- cy.get('#SidebarContainer .AddChannelDropdown_dropdownButton').click();
-
// # Click on browse channels
- cy.get('#showMoreChannels').click();
+ cy.uiBrowseOrCreateChannel('Browse channels');
// # More channels modal opens
cy.get('#browseChannelsModal').should('be.visible');
@@ -141,11 +138,8 @@ describe('Leave an archived channel', () => {
cy.uiArchiveChannel();
});
- // # Click on add channel button
- cy.get('#SidebarContainer .AddChannelDropdown_dropdownButton').click();
-
- // # Click on browse channels
- cy.get('#showMoreChannels').click();
+ // # Click on browse channels from menu
+ cy.uiBrowseOrCreateChannel('Browse channels');
// # More channels modal opens
cy.get('#browseChannelsModal').should('be.visible').then(() => {
@@ -194,11 +188,8 @@ describe('Leave an archived channel', () => {
cy.url().should('include', `/${testTeam.name}/channels/${archivedPublicChannel1.name}`);
});
- // # Click on add channel button
- cy.get('#SidebarContainer .AddChannelDropdown_dropdownButton').click();
-
- // # Click on browse channels
- cy.get('#showMoreChannels').click();
+ // # Click on browse channels from menu
+ cy.uiBrowseOrCreateChannel('Browse channels');
// # More channels modal opens
cy.get('#browseChannelsModal').should('be.visible').then(() => {
@@ -248,11 +239,8 @@ describe('Leave an archived channel', () => {
cy.get('#confirmModal').should('be.visible');
cy.get('#confirmModalButton').click();
- // # Click on add channel button
- cy.get('#SidebarContainer .AddChannelDropdown_dropdownButton').click();
-
- // # Click on browse channels
- cy.get('#showMoreChannels').click();
+ // # Click on browse channels from menu
+ cy.uiBrowseOrCreateChannel('Browse channels');
// # More channels modal opens
cy.get('#browseChannelsModal').should('be.visible').then(() => {
@@ -283,11 +271,8 @@ describe('Leave an archived channel', () => {
cy.uiArchiveChannel();
});
- // # Click on add channel button
- cy.get('#SidebarContainer .AddChannelDropdown_dropdownButton').click();
-
- // # Click on browse channels
- cy.get('#showMoreChannels').click();
+ // # Click on browse channels from menu
+ cy.uiBrowseOrCreateChannel('Browse channels');
// # More channels modal opens and lands on all channels
cy.get('#browseChannelsModal').should('be.visible').then(() => {
@@ -318,11 +303,8 @@ describe('Leave an archived channel', () => {
// # Archive the channel
cy.uiArchiveChannel();
- // # Click on add channel button
- cy.get('#SidebarContainer .AddChannelDropdown_dropdownButton').click();
-
- // # Click on browse channels
- cy.get('#showMoreChannels').click();
+ // # Click on browse channels from menu
+ cy.uiBrowseOrCreateChannel('Browse channels');
// # Modal should not contain the created channel
cy.findByText(channelType.all).should('be.visible').click();
diff --git a/e2e-tests/cypress/tests/integration/channels/channel/browse_channels_spec.ts b/e2e-tests/cypress/tests/integration/channels/channel/browse_channels_spec.ts
index edc0739e55..2396442511 100644
--- a/e2e-tests/cypress/tests/integration/channels/channel/browse_channels_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/channel/browse_channels_spec.ts
@@ -70,7 +70,7 @@ describe('Channels', () => {
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Go to LHS and click 'Browse channels'
- cy.uiBrowseOrCreateChannel('Browse channels').click();
+ cy.uiBrowseOrCreateChannel('Browse channels');
cy.get('#browseChannelsModal').should('be.visible').within(() => {
// * Dropdown should be visible, defaulting to "All Channels"
@@ -113,7 +113,7 @@ describe('Channels', () => {
});
// # Go to LHS and click 'Browse channels'
- cy.uiBrowseOrCreateChannel('Browse channels').click();
+ cy.uiBrowseOrCreateChannel('Browse channels');
cy.get('#browseChannelsModal').should('be.visible');
@@ -167,7 +167,7 @@ describe('Channels', () => {
});
// # Go to LHS and click 'Browse channels'
- cy.uiBrowseOrCreateChannel('Browse channels').click();
+ cy.uiBrowseOrCreateChannel('Browse channels');
cy.get('#browseChannelsModal').should('be.visible').within(() => {
// * Verify that channel has zero members
@@ -233,7 +233,7 @@ describe('Channels', () => {
});
// # Go to LHS and click 'Browse channels'
- cy.uiBrowseOrCreateChannel('Browse channels').click();
+ cy.uiBrowseOrCreateChannel('Browse channels');
// * Dropdown should be visible, defaulting to "All channels"
cy.get('#menuWrapper').should('be.visible').within((el) => {
@@ -288,7 +288,7 @@ function verifyBrowseChannelsModalWithArchivedSelection(isEnabled, testUser, tes
function verifyBrowseChannelsModal(isEnabled) {
// # Go to LHS and click 'Browse channels'
- cy.uiBrowseOrCreateChannel('Browse channels').click();
+ cy.uiBrowseOrCreateChannel('Browse channels');
// * Verify that the browse channels modal is open and with or without option to view archived channels
cy.get('#browseChannelsModal').should('be.visible').within(() => {
diff --git a/e2e-tests/cypress/tests/integration/channels/channel/browse_public_channels_spec.ts b/e2e-tests/cypress/tests/integration/channels/channel/browse_public_channels_spec.ts
index 1d97d60d7f..29c83de595 100644
--- a/e2e-tests/cypress/tests/integration/channels/channel/browse_public_channels_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/channel/browse_public_channels_spec.ts
@@ -66,7 +66,7 @@ describe('browse public channels', () => {
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Go to LHS and click 'Browse channels'
- cy.uiBrowseOrCreateChannel('Browse channels').click();
+ cy.uiBrowseOrCreateChannel('Browse channels');
// * Assert that the browse channel modal is visible
cy.findByRole('dialog', {name: 'Browse Channels'}).should('be.visible').then(() => {
@@ -108,7 +108,7 @@ describe('browse public channels', () => {
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Go to LHS and click 'Browse channels'
- cy.uiBrowseOrCreateChannel('Browse channels').click();
+ cy.uiBrowseOrCreateChannel('Browse channels');
// * Assert the moreChannelsModel is visible
cy.findByRole('dialog', {name: 'Browse Channels'}).should('be.visible').then(() => {
diff --git a/e2e-tests/cypress/tests/integration/channels/channel_settings/channel_name_validations_spec.ts b/e2e-tests/cypress/tests/integration/channels/channel_settings/channel_name_validations_spec.ts
index 3b1041ac10..5160f77fe8 100644
--- a/e2e-tests/cypress/tests/integration/channels/channel_settings/channel_name_validations_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/channel_settings/channel_name_validations_spec.ts
@@ -53,7 +53,7 @@ describe('Channel routing', () => {
it('MM-T884_2 Creating new channel validates against two user IDs being used as channel name', () => {
// # click on create public channel
- cy.uiBrowseOrCreateChannel('Create new channel').click();
+ cy.uiBrowseOrCreateChannel('Create new channel');
// * Verify that the new channel modal is visible
cy.get('#new-channel-modal').should('be.visible').within(() => {
@@ -73,7 +73,7 @@ describe('Channel routing', () => {
it('MM-T884_3 Creating a new channel validates against gm-like names being used as channel name', () => {
// # click on create public channel
- cy.uiBrowseOrCreateChannel('Create new channel').click();
+ cy.uiBrowseOrCreateChannel('Create new channel');
// * Verify that the new channel modal is visible
cy.findByRole('dialog', {name: 'Create a new channel'}).within(() => {
diff --git a/e2e-tests/cypress/tests/integration/channels/channel_sidebar/category_sorting_1_spec.ts b/e2e-tests/cypress/tests/integration/channels/channel_sidebar/category_sorting_1_spec.ts
index e4ba8271b3..c4c39ff061 100644
--- a/e2e-tests/cypress/tests/integration/channels/channel_sidebar/category_sorting_1_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/channel_sidebar/category_sorting_1_spec.ts
@@ -139,11 +139,8 @@ function createCategoryFromSidebarMenu() {
// # Start with a new category
const categoryName = `category-${getRandomId()}`;
- // # Click on the sidebar menu dropdown
- cy.uiGetLHSAddChannelButton().click();
-
- // # Click on create category link
- cy.findByText('Create new category').should('be.visible').click();
+ // # Click on the sidebar menu dropdown and select Create Category
+ cy.uiBrowseOrCreateChannel('Create new category');
// # Verify that Create Category modal has shown up.
// # Wait for a while until the modal has fully loaded, especially during first-time access.
diff --git a/e2e-tests/cypress/tests/integration/channels/channel_sidebar/category_sorting_spec.ts b/e2e-tests/cypress/tests/integration/channels/channel_sidebar/category_sorting_spec.ts
index 0e46cd6d62..aec2827e7c 100644
--- a/e2e-tests/cypress/tests/integration/channels/channel_sidebar/category_sorting_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/channel_sidebar/category_sorting_spec.ts
@@ -22,11 +22,8 @@ describe('Category sorting', () => {
});
it('MM-T3916 Create Category character limit', () => {
- // # Click on the sidebar menu dropdown
- cy.uiGetLHSAddChannelButton().click();
-
- // # Click on create category link
- cy.findByText('Create new category').should('be.visible').click();
+ // # Click on the sidebar menu dropdown and select Create Category
+ cy.uiBrowseOrCreateChannel('Create new category');
// # Add a name 26 characters in length e.g `abcdefghijklmnopqrstuvwxyz`
cy.get('#editCategoryModal').should('be.visible').wait(TIMEOUTS.HALF_SEC).within(() => {
diff --git a/e2e-tests/cypress/tests/integration/channels/channel_sidebar/custom_categories_spec.ts b/e2e-tests/cypress/tests/integration/channels/channel_sidebar/custom_categories_spec.ts
index 226ad58698..ab813297d7 100644
--- a/e2e-tests/cypress/tests/integration/channels/channel_sidebar/custom_categories_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/channel_sidebar/custom_categories_spec.ts
@@ -89,11 +89,8 @@ function createCategoryFromSidebarMenu() {
// # Start with a new category
const categoryName = `category-${getRandomId()}`;
- // # Click on the sidebar menu dropdown
- cy.uiGetLHSAddChannelButton().click();
-
- // # Click on create category link
- cy.findByText('Create new category').should('be.visible').click();
+ // # Click on the sidebar menu dropdown and select Create Category
+ cy.uiBrowseOrCreateChannel('Create new category');
// # Verify that Create Category modal has shown up.
// # Wait for a while until the modal has fully loaded, especially during first-time access.
diff --git a/e2e-tests/cypress/tests/integration/channels/channel_sidebar/new_channel_dropdown_spec.ts b/e2e-tests/cypress/tests/integration/channels/channel_sidebar/new_channel_dropdown_spec.ts
index 96655a6902..f1acfa370f 100644
--- a/e2e-tests/cypress/tests/integration/channels/channel_sidebar/new_channel_dropdown_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/channel_sidebar/new_channel_dropdown_spec.ts
@@ -61,11 +61,8 @@ describe('Channel sidebar', () => {
cy.get('#channelHeaderTitle').should('contain', 'Town Square');
cy.url().should('include', `/${teamName}/channels/town-square`);
- // # Click the New Channel Dropdown button
- cy.uiGetLHSAddChannelButton().should('be.visible').click();
-
- // # Click the Browse channels dropdown item
- cy.get('.AddChannelDropdown .MenuItem:contains(Browse channels) button').should('be.visible').click();
+ // # Click on the sidebar menu dropdown and select browse channels
+ cy.uiBrowseOrCreateChannel('Browse channels');
// * Verify that the more channels modal is visible
cy.get('#browseChannelsModal').should('be.visible');
diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/accessibility/accessibility_modals_dialogs_spec.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/accessibility/accessibility_modals_dialogs_spec.ts
index 5486671a20..c11aad30d3 100644
--- a/e2e-tests/cypress/tests/integration/channels/enterprise/accessibility/accessibility_modals_dialogs_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/enterprise/accessibility/accessibility_modals_dialogs_spec.ts
@@ -104,7 +104,7 @@ describe('Verify Accessibility Support in Modals & Dialogs', () => {
cy.reload();
// * Verify the aria-label in more public channels button
- cy.uiBrowseOrCreateChannel('Browse channels').click();
+ cy.uiBrowseOrCreateChannel('Browse channels');
// * Verify the accessibility support in More Channels Dialog
cy.findByRole('dialog', {name: 'Browse Channels'}).within(() => {
@@ -140,7 +140,7 @@ describe('Verify Accessibility Support in Modals & Dialogs', () => {
it.skip('MM-T1468 Accessibility Support in Add people to Channel Dialog screen', () => {
// # Add atleast 5 users
for (let i = 0; i < 5; i++) {
- cy.apiCreateUser().then(({user}) => { // eslint-disable-line
+ cy.apiCreateUser().then(({user}) => { // eslint-disable-line
cy.apiAddUserToTeam(testTeam.id, user.id);
});
}
diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/ldap/ldap_group_sync_spec.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/ldap/ldap_group_sync_spec.ts
index 725eb48427..1211829fcf 100644
--- a/e2e-tests/cypress/tests/integration/channels/enterprise/ldap/ldap_group_sync_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/enterprise/ldap/ldap_group_sync_spec.ts
@@ -234,7 +234,7 @@ context('ldap', () => {
// # Go to team page to look for this channel in public channel directory
cy.visit(`/${testTeam.name}`);
- cy.uiBrowseOrCreateChannel('Browse channels').click();
+ cy.uiBrowseOrCreateChannel('Browse channels');
// * Search private channel name and make sure it is still visible
cy.get('#searchChannelsTextbox').type(testChannel.display_name);
@@ -456,7 +456,7 @@ context('ldap', () => {
cy.visit(`/${testTeam.name}/channels/off-topic`);
// # Go to LHS and click 'Browse channels'
- cy.uiBrowseOrCreateChannel('Browse channels').click();
+ cy.uiBrowseOrCreateChannel('Browse channels');
// * Search public channel and ensure it appears in the list
cy.get('#searchChannelsTextbox').type(publicChannel.display_name);
@@ -473,7 +473,7 @@ context('ldap', () => {
cy.visit(`/${testTeam.name}/channels/off-topic`);
// # Go to LHS and click 'Browse channels'
- cy.uiBrowseOrCreateChannel('Browse channels').click();
+ cy.uiBrowseOrCreateChannel('Browse channels');
// * Search private channel name and make sure it isn't there in public channel directory
cy.get('#searchChannelsTextbox').type(publicChannel.display_name);
diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/permissions/team_permissions_spec.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/permissions/team_permissions_spec.ts
index 336b125d15..a1b0160fe3 100644
--- a/e2e-tests/cypress/tests/integration/channels/enterprise/permissions/team_permissions_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/enterprise/permissions/team_permissions_spec.ts
@@ -148,7 +148,7 @@ describe('Team Permissions', () => {
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Click on create new channel at LHS
- cy.uiBrowseOrCreateChannel('Create new channel').click();
+ cy.uiBrowseOrCreateChannel('Create new channel');
// * Verify that the create private channel is disabled
cy.findByRole('dialog', {name: 'Create a new channel'}).find('#public-private-selector-button-P').should('have.class', 'disabled');
diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/system_console/about/edition_and_license_spec.js b/e2e-tests/cypress/tests/integration/channels/enterprise/system_console/about/edition_and_license_spec.js
index 8b8068d5be..ac05be32d3 100644
--- a/e2e-tests/cypress/tests/integration/channels/enterprise/system_console/about/edition_and_license_spec.js
+++ b/e2e-tests/cypress/tests/integration/channels/enterprise/system_console/about/edition_and_license_spec.js
@@ -105,7 +105,7 @@ function verifyCreatePublicChannel(teamName, testUsers) {
cy.visit(`/${teamName}/channels/town-square`);
// # Click on create new channel at LHS
- cy.uiBrowseOrCreateChannel('Create new channel').click();
+ cy.uiBrowseOrCreateChannel('Create new channel');
cy.findByRole('dialog', {name: 'Create a new channel'}).within(() => {
// * Verify if creating a public channel is disabled or not
diff --git a/e2e-tests/cypress/tests/integration/channels/multi_team_and_dm/existing_channel_name_spec.js b/e2e-tests/cypress/tests/integration/channels/multi_team_and_dm/existing_channel_name_spec.js
index 24ad292117..17a2822701 100644
--- a/e2e-tests/cypress/tests/integration/channels/multi_team_and_dm/existing_channel_name_spec.js
+++ b/e2e-tests/cypress/tests/integration/channels/multi_team_and_dm/existing_channel_name_spec.js
@@ -77,7 +77,7 @@ describe('Channel', () => {
*/
function verifyExistingChannelError(newChannelName, makePrivate = false) {
// Click on '+' button for Public or Private Channel
- cy.uiBrowseOrCreateChannel('Create new channel').click();
+ cy.uiBrowseOrCreateChannel('Create new channel');
if (makePrivate) {
cy.get('#public-private-selector-button-P').click();
diff --git a/e2e-tests/cypress/tests/support/ui/channel.js b/e2e-tests/cypress/tests/support/ui/channel.js
index 978c50af42..43c1e87c3e 100644
--- a/e2e-tests/cypress/tests/support/ui/channel.js
+++ b/e2e-tests/cypress/tests/support/ui/channel.js
@@ -11,7 +11,7 @@ Cypress.Commands.add('uiCreateChannel', ({
name = '',
createBoard = '',
}) => {
- cy.uiBrowseOrCreateChannel('Create new channel').click();
+ cy.uiBrowseOrCreateChannel('Create new channel');
cy.get('#new-channel-modal').should('be.visible');
if (isPrivate) {
diff --git a/e2e-tests/cypress/tests/support/ui/channel_sidebar.ts b/e2e-tests/cypress/tests/support/ui/channel_sidebar.ts
index b279fc77ca..bdbed42164 100644
--- a/e2e-tests/cypress/tests/support/ui/channel_sidebar.ts
+++ b/e2e-tests/cypress/tests/support/ui/channel_sidebar.ts
@@ -13,11 +13,8 @@ import {getRandomId} from '../../utils';
* cy.uiCreateSidebarCategory();
*/
function uiCreateSidebarCategory(categoryName: string = `category-${getRandomId()}`): ChainableT {
- // # Click the New Category/Channel Dropdown button
- cy.uiGetLHSAddChannelButton().click();
-
- // # Click the Create new category dropdown item
- cy.get('.AddChannelDropdown').should('be.visible').contains('.MenuItem', 'Create new category').click();
+ // # Click on the sidebar menu dropdown and select Create Category
+ cy.uiBrowseOrCreateChannel('Create new category');
cy.findByRole('dialog', {name: 'Rename Category'}).should('be.visible').within(() => {
// # Fill in the category name and click 'Create'
diff --git a/e2e-tests/cypress/tests/support/ui/sidebar_left.ts b/e2e-tests/cypress/tests/support/ui/sidebar_left.ts
index aad4631134..1393478a87 100644
--- a/e2e-tests/cypress/tests/support/ui/sidebar_left.ts
+++ b/e2e-tests/cypress/tests/support/ui/sidebar_left.ts
@@ -31,8 +31,7 @@ Cypress.Commands.add('uiOpenTeamMenu', (item = '') => {
});
Cypress.Commands.add('uiGetLHSAddChannelButton', () => {
- return cy.uiGetLHS().
- find('.AddChannelDropdown_dropdownButton');
+ return cy.uiGetLHS().findByRole('button', {name: 'Browse or create channels'});
});
Cypress.Commands.add('uiGetLHSTeamMenu', () => {
@@ -88,15 +87,16 @@ Cypress.Commands.add('uiGetLhsSection', (section) => {
parent();
});
-Cypress.Commands.add('uiBrowseOrCreateChannel', (item) => {
- cy.get('.AddChannelDropdown_dropdownButton').
- should('be.visible').
- click();
- cy.get('.dropdown-menu').should('be.visible');
+Cypress.Commands.add('uiBrowseOrCreateChannel', (menuitem) => {
+ cy.uiGetLHSAddChannelButton().should('be.visible').click();
- if (item) {
- cy.findByRole('menuitem', {name: item});
- }
+ cy.get('#browserOrAddChannelMenu').
+ should('exist').and('be.visible').
+ within(() => {
+ if (menuitem) {
+ cy.findByText(menuitem).should('exist').click();
+ }
+ });
});
Cypress.Commands.add('uiAddDirectMessage', () => {
diff --git a/webapp/channels/src/actions/views/add_channel_dropdown.ts b/webapp/channels/src/actions/views/add_channel_dropdown.ts
index 251d2acf5e..b49cf3bd20 100644
--- a/webapp/channels/src/actions/views/add_channel_dropdown.ts
+++ b/webapp/channels/src/actions/views/add_channel_dropdown.ts
@@ -3,13 +3,6 @@
import {ActionTypes} from 'utils/constants';
-export function setAddChannelDropdown(open: boolean) {
- return {
- type: ActionTypes.ADD_CHANNEL_DROPDOWN_TOGGLE,
- open,
- };
-}
-
export function setAddChannelCtaDropdown(open: boolean) {
return {
type: ActionTypes.ADD_CHANNEL_CTA_DROPDOWN_TOGGLE,
diff --git a/webapp/channels/src/components/menu/index.ts b/webapp/channels/src/components/menu/index.ts
index 0f21a10199..5c4a46de45 100644
--- a/webapp/channels/src/components/menu/index.ts
+++ b/webapp/channels/src/components/menu/index.ts
@@ -8,3 +8,4 @@ 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';
+export {openMenu, dismissMenu} from './menu_utils';
diff --git a/webapp/channels/src/components/menu/menu.tsx b/webapp/channels/src/components/menu/menu.tsx
index 48b20602d1..28600decb3 100644
--- a/webapp/channels/src/components/menu/menu.tsx
+++ b/webapp/channels/src/components/menu/menu.tsx
@@ -34,6 +34,8 @@ import {MenuContext, useMenuContextValue} from './menu_context';
import './menu.scss';
+export const ELEMENT_ID_FOR_MENU_BACKDROP = 'backdropForMenuComponent';
+
const MENU_OPEN_ANIMATION_DURATION = 150;
const MENU_CLOSE_ANIMATION_DURATION = 100;
@@ -56,8 +58,13 @@ type MenuButtonTooltipProps = {
}
type MenuProps = {
+
+ /**
+ * ID is mandatory as it is used in mobileWebview to open modal equivalent to menu
+ */
id: string;
'aria-label'?: string;
+ 'aria-labelledby'?: string;
/**
* @warning Make the styling of your components such a way that they don't need this handler
@@ -244,9 +251,10 @@ export function Menu(props: Props) {
disableAutoFocusItem={disableAutoFocusItem} // This is not anti-pattern, see handleMenuButtonMouseDown
MenuListProps={{
id: props.menu.id,
- 'aria-label': props.menu?.['aria-label'] ?? '',
+ 'aria-label': props.menu?.['aria-label'],
+ 'aria-labelledby': props.menu?.['aria-labelledby'],
style: {
- width: props.menu.width || 'inherit',
+ width: props.menu?.width,
},
}}
TransitionProps={{
@@ -257,6 +265,11 @@ export function Menu(props: Props) {
exit: MENU_CLOSE_ANIMATION_DURATION,
},
}}
+ slotProps={{
+ backdrop: {
+ id: ELEMENT_ID_FOR_MENU_BACKDROP,
+ },
+ }}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error This exists in source code of mui, but its types are missing
onTransitionExited={providerValue.handleClosed}
diff --git a/webapp/channels/src/components/menu/menu_utils.ts b/webapp/channels/src/components/menu/menu_utils.ts
new file mode 100644
index 0000000000..3f354e3513
--- /dev/null
+++ b/webapp/channels/src/components/menu/menu_utils.ts
@@ -0,0 +1,29 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import {ELEMENT_ID_FOR_MENU_BACKDROP} from './menu';
+
+/**
+ * Since the menu component requires actual interaction with the button
+ * of the menu for opening the menus, we trigger it from here by clicking on the menu button
+ */
+export function openMenu(buttonId: string) {
+ const menuButton = document.getElementById(buttonId);
+ if (!menuButton) {
+ return;
+ }
+
+ menuButton.click();
+}
+
+/**
+ * Dismisses the menu by clicking on the backdrop
+ */
+export function dismissMenu() {
+ const menuOverlay = document.getElementById(ELEMENT_ID_FOR_MENU_BACKDROP);
+ if (!menuOverlay) {
+ return;
+ }
+
+ menuOverlay.click();
+}
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 369db76804..4ede9f1b21 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
@@ -13,7 +13,7 @@ import {isShowOnboardingCompleteProfileTour} from 'selectors/views/onboarding_ta
import {OnboardingTasksName, TaskNameMapToSteps} from './constants';
import {useHandleOnBoardingTaskData} from './onboarding_tasks_manager';
-const translate = {x: 0, y: -2};
+const translate = {x: 1, y: -9};
export const CompleteYourProfileTour = () => {
const dispatch = useDispatch();
@@ -43,7 +43,7 @@ export const CompleteYourProfileTour = () => {
);
- const overlayPunchOut = useMeasurePunchouts(['userAccountMenu'], [], {y: -6, height: 6, x: 0, width: 0});
+ const overlayPunchOut = useMeasurePunchouts(['userAccountMenu'], [], {x: -2.5, y: -2.5, width: 5, height: 5});
const onDismiss = (e: React.MouseEvent) => {
e.stopPropagation();
e.preventDefault();
@@ -58,13 +58,12 @@ export const CompleteYourProfileTour = () => {
overlayPunchOut={overlayPunchOut}
step={steps.STARTED}
placement='left-start'
- pulsatingDotPlacement='left'
+ pulsatingDotPlacement='left-end'
pulsatingDotTranslate={translate}
handleDismiss={onDismiss}
singleTip={true}
showOptOut={false}
interactivePunchOut={true}
- zIndex={1058}
/>
);
};
diff --git a/webapp/channels/src/components/onboarding_tasks/onboarding_tasks_manager.tsx b/webapp/channels/src/components/onboarding_tasks/onboarding_tasks_manager.tsx
index 011346935e..bcfdb9af8f 100644
--- a/webapp/channels/src/components/onboarding_tasks/onboarding_tasks_manager.tsx
+++ b/webapp/channels/src/components/onboarding_tasks/onboarding_tasks_manager.tsx
@@ -29,6 +29,7 @@ import Phone from 'components/common/svg_images_components/phone_svg';
import Security from 'components/common/svg_images_components/security_svg';
import Sunglasses from 'components/common/svg_images_components/sunglasses_svg';
import LearnMoreTrialModal from 'components/learn_more_trial_modal/learn_more_trial_modal';
+import {openMenu} from 'components/menu';
import {
AutoTourStatus,
FINISHED,
@@ -37,9 +38,9 @@ import {
TTNameMapToATStatusKey,
TutorialTourName,
} from 'components/tours';
+import {ELEMENT_ID_FOR_USER_ACCOUNT_MENU_BUTTON} from 'components/user_account_menu/user_account_menu';
import {ModalIdentifiers, TELEMETRY_CATEGORIES} from 'utils/constants';
-import {clickOnMenuButton, MenuButtonIds} from 'utils/keyboard';
import type {GlobalState} from 'types/store';
@@ -239,7 +240,7 @@ export const useHandleOnBoardingTaskTrigger = () => {
break;
}
case OnboardingTasksName.COMPLETE_YOUR_PROFILE: {
- clickOnMenuButton(MenuButtonIds.userAccountMenu);
+ openMenu(ELEMENT_ID_FOR_USER_ACCOUNT_MENU_BUTTON);
dispatch(setShowOnboardingCompleteProfileTour(true));
handleSaveData(taskName, TaskNameMapToSteps[taskName].STARTED, true);
if (inAdminConsole) {
diff --git a/webapp/channels/src/components/post_view/post_header_custom_status/post_header_custom_status.tsx b/webapp/channels/src/components/post_view/post_header_custom_status/post_header_custom_status.tsx
index 79231ea9b6..af7ad7458a 100644
--- a/webapp/channels/src/components/post_view/post_header_custom_status/post_header_custom_status.tsx
+++ b/webapp/channels/src/components/post_view/post_header_custom_status/post_header_custom_status.tsx
@@ -8,10 +8,10 @@ import {useSelector} from 'react-redux';
import {makeGetCustomStatus, showPostHeaderUpdateStatusButton, isCustomStatusEnabled} from 'selectors/views/custom_status';
import CustomStatusEmoji from 'components/custom_status/custom_status_emoji';
+import {openMenu} from 'components/menu';
+import {ELEMENT_ID_FOR_USER_ACCOUNT_MENU_BUTTON} from 'components/user_account_menu/user_account_menu';
import EmojiIcon from 'components/widgets/icons/emoji_icon';
-import {clickOnMenuButton, MenuButtonIds} from 'utils/keyboard';
-
import type {GlobalState} from 'types/store';
interface ComponentProps {
@@ -51,7 +51,7 @@ const PostHeaderCustomStatus = (props: ComponentProps) => {
const updateStatus = (event: React.MouseEvent) => {
event.preventDefault();
- clickOnMenuButton(MenuButtonIds.userAccountMenu);
+ openMenu(ELEMENT_ID_FOR_USER_ACCOUNT_MENU_BUTTON);
};
return (
diff --git a/webapp/channels/src/components/sidebar/__snapshots__/sidebar.test.tsx.snap b/webapp/channels/src/components/sidebar/__snapshots__/sidebar.test.tsx.snap
index 0cda1e5e7d..99d837490b 100644
--- a/webapp/channels/src/components/sidebar/__snapshots__/sidebar.test.tsx.snap
+++ b/webapp/channels/src/components/sidebar/__snapshots__/sidebar.test.tsx.snap
@@ -18,7 +18,6 @@ exports[`components/sidebar should match snapshot 1`] = `
showMoreChannelsModal={[Function]}
showNewChannelModal={[Function]}
unreadFilterEnabled={false}
- userGroupsEnabled={false}
/>
void;
- showCreateUserGroupModal: () => void;
- invitePeopleModal: () => void;
- showNewChannelModal: () => void;
- showCreateCategoryModal: () => void;
- handleOpenDirectMessagesModal: (e: Event) => void;
- unreadFilterEnabled: boolean;
- showCreateTutorialTip: boolean;
- showInviteTutorialTip: boolean;
- isAddChannelOpen: boolean;
- openAddChannelOpen: (open: boolean) => void;
- canCreateCustomGroups: boolean;
-};
-
-const AddChannelDropdown = ({
- canCreateChannel,
- canJoinPublicChannel,
- showMoreChannelsModal,
- showCreateUserGroupModal,
- invitePeopleModal,
- showNewChannelModal,
- showCreateCategoryModal,
- handleOpenDirectMessagesModal,
- unreadFilterEnabled,
- showCreateTutorialTip,
- showInviteTutorialTip,
- isAddChannelOpen,
- openAddChannelOpen,
- canCreateCustomGroups,
-}: Props) => {
- const intl = useIntl();
-
- const renderDropdownItems = () => {
- const invitePeople = (
-
- }
- text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.invitePeople', defaultMessage: 'Invite people'})}
- extraText={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.invitePeopleExtraText', defaultMessage: 'Add people to the team'})}
- />
- {showInviteTutorialTip && }
-
- );
-
- let joinPublicChannel;
- if (canJoinPublicChannel) {
- joinPublicChannel = (
-
}
- text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.browseChannels', defaultMessage: 'Browse channels'})}
- />
- );
- }
-
- let createChannel;
- if (canCreateChannel) {
- createChannel = (
-
}
- text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.createNewChannel', defaultMessage: 'Create new channel'})}
- />
- );
- }
-
- let createCategory;
- if (!unreadFilterEnabled) {
- createCategory = (
-
- }
- text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.createCategory', defaultMessage: 'Create new category'})}
- />
- );
- }
-
- const createDirectMessage = (
- }
- text={intl.formatMessage({id: 'sidebar.openDirectMessage', defaultMessage: 'Open a direct message'})}
- />
- );
-
- let createUserGroup;
- if (canCreateCustomGroups) {
- createUserGroup = (
-
}
- text={intl.formatMessage({id: 'sidebar.createUserGroup', defaultMessage: 'Create New User Group'})}
- />
- );
- }
-
- return (
- <>
-
- {createChannel}
- {joinPublicChannel}
- {createDirectMessage}
- {showCreateTutorialTip && }
- {createUserGroup}
-
- {createCategory}
- {invitePeople}
- >
- );
- };
-
- const trackOpen = (opened: boolean) => {
- openAddChannelOpen(opened);
- if (opened) {
- trackEvent('ui', 'ui_add_channel_dropdown_opened');
- }
- };
-
- if (!(canCreateChannel || canJoinPublicChannel)) {
- return null;
- }
-
- return (
-
-
-
-
-
-
- );
-};
-
-export default AddChannelDropdown;
diff --git a/webapp/channels/src/components/sidebar/channel_navigator/channel_navigator.test.tsx b/webapp/channels/src/components/sidebar/channel_navigator/channel_navigator.test.tsx
index 3139b7a35f..d6f1e63d65 100644
--- a/webapp/channels/src/components/sidebar/channel_navigator/channel_navigator.test.tsx
+++ b/webapp/channels/src/components/sidebar/channel_navigator/channel_navigator.test.tsx
@@ -4,11 +4,11 @@
import {shallow} from 'enzyme';
import React from 'react';
+import BrowserOrAddChannelMenu from 'components/sidebar/sidebar_header/sidebar_browse_or_add_channel_menu';
+
import ChannelNavigator from './channel_navigator';
import type {Props} from './channel_navigator';
-import AddChannelDropdown from '../add_channel_dropdown';
-
let props: Props;
describe('Components/ChannelNavigator', () => {
@@ -23,8 +23,8 @@ describe('Components/ChannelNavigator', () => {
};
});
- it('should not show AddChannelDropdown', () => {
+ it('should not show BrowserOrAddChannelMenu', () => {
const wrapper = shallow(
);
- expect(wrapper.find(AddChannelDropdown).length).toBe(0);
+ expect(wrapper.find(BrowserOrAddChannelMenu).length).toBe(0);
});
});
diff --git a/webapp/channels/src/components/sidebar/index.ts b/webapp/channels/src/components/sidebar/index.ts
index fe67d9ebb2..98ba9eedfa 100644
--- a/webapp/channels/src/components/sidebar/index.ts
+++ b/webapp/channels/src/components/sidebar/index.ts
@@ -29,7 +29,6 @@ import Sidebar from './sidebar';
function mapStateToProps(state: GlobalState) {
const currentTeam = getCurrentTeam(state);
const unreadFilterEnabled = isUnreadFilterEnabled(state);
- const userGroupsEnabled = isCustomGroupsEnabled(state);
let canCreatePublicChannel = false;
let canCreatePrivateChannel = false;
@@ -41,7 +40,7 @@ function mapStateToProps(state: GlobalState) {
canJoinPublicChannel = haveICurrentChannelPermission(state, Permissions.JOIN_PUBLIC_CHANNELS);
}
- const canCreateCustomGroups = haveISystemPermission(state, {permission: Permissions.CREATE_CUSTOM_GROUP}) && isCustomGroupsEnabled(state);
+ const canCreateCustomGroups = isCustomGroupsEnabled(state) && haveISystemPermission(state, {permission: Permissions.CREATE_CUSTOM_GROUP});
return {
teamId: currentTeam ? currentTeam.id : '',
@@ -52,7 +51,6 @@ function mapStateToProps(state: GlobalState) {
unreadFilterEnabled,
isMobileView: getIsMobileView(state),
isKeyBoardShortcutModalOpen: isModalOpen(state, ModalIdentifiers.KEYBOARD_SHORTCUTS_MODAL),
- userGroupsEnabled,
canCreateCustomGroups,
rhsState: getRhsState(state),
rhsOpen: getIsRhsOpen(state),
diff --git a/webapp/channels/src/components/sidebar/sidebar.tsx b/webapp/channels/src/components/sidebar/sidebar.tsx
index 8291183127..10d6b5923e 100644
--- a/webapp/channels/src/components/sidebar/sidebar.tsx
+++ b/webapp/channels/src/components/sidebar/sidebar.tsx
@@ -48,7 +48,6 @@ type Props = {
unreadFilterEnabled: boolean;
isMobileView: boolean;
isKeyBoardShortcutModalOpen: boolean;
- userGroupsEnabled: boolean;
canCreateCustomGroups: boolean;
rhsState?: RhsState;
rhsOpen?: boolean;
@@ -180,8 +179,8 @@ export default class Sidebar extends React.PureComponent
{
trackEvent('ui', 'ui_channels_create_user_group');
};
- handleOpenMoreDirectChannelsModal = (e: Event) => {
- e.preventDefault();
+ handleOpenMoreDirectChannelsModal = (e?: Event) => {
+ e?.preventDefault();
if (this.state.showDirectChannelsModal) {
this.hideMoreDirectChannelsModal();
} else {
@@ -248,7 +247,6 @@ export default class Sidebar extends React.PureComponent {
canJoinPublicChannel={this.props.canJoinPublicChannel}
handleOpenDirectMessagesModal={this.handleOpenMoreDirectChannelsModal}
unreadFilterEnabled={this.props.unreadFilterEnabled}
- userGroupsEnabled={this.props.userGroupsEnabled}
canCreateCustomGroups={this.props.canCreateCustomGroups}
/>
)}
diff --git a/webapp/channels/src/components/sidebar/sidebar_header/index.ts b/webapp/channels/src/components/sidebar/sidebar_header/index.ts
index 9c25973bbe..648ced088c 100644
--- a/webapp/channels/src/components/sidebar/sidebar_header/index.ts
+++ b/webapp/channels/src/components/sidebar/sidebar_header/index.ts
@@ -2,4 +2,5 @@
// See LICENSE.txt for license information.
import SidebarHeader from './sidebar_header';
+
export default SidebarHeader;
diff --git a/webapp/channels/src/components/sidebar/sidebar_header/sidebar_browse_or_add_channel_menu.tsx b/webapp/channels/src/components/sidebar/sidebar_header/sidebar_browse_or_add_channel_menu.tsx
new file mode 100644
index 0000000000..5f8f389358
--- /dev/null
+++ b/webapp/channels/src/components/sidebar/sidebar_header/sidebar_browse_or_add_channel_menu.tsx
@@ -0,0 +1,176 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React from 'react';
+import {FormattedMessage, useIntl} from 'react-intl';
+
+import {
+ PlusIcon,
+ AccountPlusOutlineIcon,
+ FolderPlusOutlineIcon,
+ AccountMultiplePlusOutlineIcon,
+ GlobeIcon,
+ AccountOutlineIcon,
+} from '@mattermost/compass-icons/components';
+
+import * as Menu from 'components/menu';
+import {OnboardingTourSteps} from 'components/tours';
+import {useShowOnboardingTutorialStep, CreateAndJoinChannelsTour, InvitePeopleTour} from 'components/tours/onboarding_tour';
+
+export const ELEMENT_ID_FOR_BROWSE_OR_ADD_CHANNEL_MENU = 'browseOrAddChannelMenuButton';
+
+type Props = {
+ canCreateChannel: boolean;
+ onCreateNewChannelClick: () => void;
+ canJoinPublicChannel: boolean;
+ onBrowseChannelClick: () => void;
+ onOpenDirectMessageClick: () => void;
+ canCreateCustomGroups: boolean;
+ onCreateNewUserGroupClick: () => void;
+ unreadFilterEnabled: boolean;
+ onCreateNewCategoryClick: () => void;
+ onInvitePeopleClick: () => void;
+};
+
+export default function SidebarBrowserOrAddChannelMenu(props: Props) {
+ const {formatMessage} = useIntl();
+
+ const showCreateAndJoinChannelsTutorialTip = useShowOnboardingTutorialStep(OnboardingTourSteps.CREATE_AND_JOIN_CHANNELS);
+ const showInvitePeopleTutorialTip = useShowOnboardingTutorialStep(OnboardingTourSteps.INVITE_PEOPLE);
+
+ let createNewChannelMenuItem: JSX.Element | null = null;
+ if (props.canCreateChannel) {
+ createNewChannelMenuItem = (
+