diff --git a/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_dropdowns_spec.js b/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_dropdowns_spec.js index a1fd352f58..ad80b62047 100644 --- a/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_dropdowns_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_dropdowns_spec.js @@ -105,9 +105,9 @@ describe('Verify Accessibility Support in Dropdown Menus', () => { {id: 'invitePeople', label: 'Invite People dialog'}, {id: 'teamSettings', label: 'Team Settings dialog'}, {id: 'manageMembers', label: 'Manage Members dialog'}, - {id: 'joinTeam', text: 'Join Another Team'}, + {id: 'joinTeam', text: 'Join another team'}, {id: 'leaveTeam', label: 'Leave Team dialog'}, - {id: 'createTeam', text: 'Create a Team'}, + {id: 'createTeam', text: 'Create a team'}, ]; menuItems.forEach((item) => { diff --git a/e2e-tests/cypress/tests/integration/channels/account_settings/profile/fullname_truncate_spec.ts b/e2e-tests/cypress/tests/integration/channels/account_settings/profile/fullname_truncate_spec.ts index 1f9a900c8d..dc28554ab1 100644 --- a/e2e-tests/cypress/tests/integration/channels/account_settings/profile/fullname_truncate_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/account_settings/profile/fullname_truncate_spec.ts @@ -70,7 +70,7 @@ describe('Profile > Profile Settings> Full Name', () => { it('MM-T2048 Empty full name: @ still displays before username', () => { // # Open any user list ("View Members", "Add Members", "Manage Members", ..) - cy.uiOpenTeamMenu('View Members'); + cy.uiOpenTeamMenu('View members'); // # Find a user who hasn't set their full name cy.get('.modal-title').should('be.visible'); diff --git a/e2e-tests/cypress/tests/integration/channels/account_settings/profile/nickname_spec.ts b/e2e-tests/cypress/tests/integration/channels/account_settings/profile/nickname_spec.ts index dac0017355..3836f1bf5a 100644 --- a/e2e-tests/cypress/tests/integration/channels/account_settings/profile/nickname_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/account_settings/profile/nickname_spec.ts @@ -39,7 +39,7 @@ describe('Settings > Sidebar > General', () => { }); // # Open team menu and click "View Members" - cy.uiOpenTeamMenu('View Members'); + cy.uiOpenTeamMenu('View members'); // # Search for username and check that no nickname is present cy.get('.modal-title').should('be.visible'); @@ -68,7 +68,7 @@ describe('Settings > Sidebar > General', () => { }); // # Open team menu and click "View Members" - cy.uiOpenTeamMenu('View Members'); + cy.uiOpenTeamMenu('View members'); // # Search for username and check that expected nickname is present cy.get('.modal-title').should('be.visible'); diff --git a/e2e-tests/cypress/tests/integration/channels/archived_channel/archived_leave_channel_spec.ts b/e2e-tests/cypress/tests/integration/channels/archived_channel/archived_leave_channel_spec.ts index f3878e4aad..02177d40cf 100644 --- a/e2e-tests/cypress/tests/integration/channels/archived_channel/archived_leave_channel_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/archived_channel/archived_leave_channel_spec.ts @@ -67,7 +67,7 @@ describe('Leave an archived channel', () => { cy.uiArchiveChannel(); // # Open channel menu and click View Members - cy.uiOpenChannelMenu('View Members'); + cy.uiOpenChannelMenu('View members'); // * Channel Members modal opens cy.get('div#channelMembersModal').should('be.visible'); diff --git a/e2e-tests/cypress/tests/integration/channels/auth_sso/authentication_4_spec.ts b/e2e-tests/cypress/tests/integration/channels/auth_sso/authentication_4_spec.ts index ea0d702e44..d53c73467e 100644 --- a/e2e-tests/cypress/tests/integration/channels/auth_sso/authentication_4_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/auth_sso/authentication_4_spec.ts @@ -267,7 +267,7 @@ describe('Authentication', () => { cy.postMessage('hello'); // # Open team menu and click on "Invite People" - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // # Click invite members if needed cy.findByText('Copy invite link').click(); diff --git a/e2e-tests/cypress/tests/integration/channels/bot_accounts/in_lists_2_spec.ts b/e2e-tests/cypress/tests/integration/channels/bot_accounts/in_lists_2_spec.ts index e2e7af4339..2577eb2170 100644 --- a/e2e-tests/cypress/tests/integration/channels/bot_accounts/in_lists_2_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/bot_accounts/in_lists_2_spec.ts @@ -72,7 +72,7 @@ describe('Bots in lists', () => { // # Open channel members cy.get('.channel-header__trigger').click(); - cy.findByText('Manage Members').click(); + cy.findByText('Manage members').click(); cy.get('.more-modal__row .more-modal__name').then(async ($query) => { // # Extract usernames from jQuery collection diff --git a/e2e-tests/cypress/tests/integration/channels/bot_accounts/promote_demote_spec.ts b/e2e-tests/cypress/tests/integration/channels/bot_accounts/promote_demote_spec.ts index c29b304d8c..1f4a70c0c6 100644 --- a/e2e-tests/cypress/tests/integration/channels/bot_accounts/promote_demote_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/bot_accounts/promote_demote_spec.ts @@ -37,7 +37,7 @@ describe('Managing bots in Teams and Channels', () => { await client.addToTeam(team.id, bot.user_id); // # Open team menu and click 'Manage Members' - cy.uiOpenTeamMenu('Manage Members'); + cy.uiOpenTeamMenu('Manage members'); // # Find bot cy.get('.more-modal__list').find('.more-modal__row').its('length').should('be.gt', 0); 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 764e810180..8746f42f7d 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 @@ -105,7 +105,7 @@ describe('Custom Status - Verifying Where Custom Status Appears', () => { it('MM-T3850_8 should show custom status emoji next to username in the team members modal', () => { // # Open team menu and click on "View Members" - cy.uiOpenTeamMenu('View Members'); + cy.uiOpenTeamMenu('View members'); cy.get('#teamMembersModal').should('exist'); // # Search the current user's username in the search input diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/accessibility/accessibility_input_fields_spec.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/accessibility/accessibility_input_fields_spec.ts index 0553be7b62..99649337fa 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/accessibility/accessibility_input_fields_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/accessibility/accessibility_input_fields_spec.ts @@ -36,7 +36,7 @@ describe('Verify Accessibility Support in different input fields', () => { it('MM-T1456 Verify Accessibility Support in Input fields in Invite People Flow', () => { // # Open team menu and click 'Invite People' - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // # Click invite members if needed cy.get('.InviteAs').findByTestId('inviteMembersLink').click(); diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/accessibility/accessibility_modals_dialogs_1_spec.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/accessibility/accessibility_modals_dialogs_1_spec.ts index b3733db527..ddfa6a0300 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/accessibility/accessibility_modals_dialogs_1_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/accessibility/accessibility_modals_dialogs_1_spec.ts @@ -47,7 +47,7 @@ describe('Verify Accessibility Support in Modals & Dialogs', () => { verifyUserMenuModal('Profile'); // * Verify the accessibility support in Team Settings Dialog - verifyMainMenuModal('Team Settings'); + verifyMainMenuModal('Team settings'); // * Verify the accessibility support in Manage Members Dialog verifyMainMenuModal('Manage Members', `${testTeam.display_name} Members`); @@ -72,7 +72,7 @@ describe('Verify Accessibility Support in Modals & Dialogs', () => { // # Open Channel Members Dialog cy.get('#channelHeaderTitle').click(); - cy.findByText('Manage Members').click().wait(TIMEOUTS.FIVE_SEC); + cy.findByText('Manage members').click().wait(TIMEOUTS.FIVE_SEC); // * Verify the accessibility support in Manage Members Dialog cy.findByRole('dialog', {name: 'Off-Topic Members'}).within(() => { 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 e01479dca4..9ae67a9b1a 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 @@ -44,7 +44,7 @@ describe('Verify Accessibility Support in Modals & Dialogs', () => { cy.visit(`/${testTeam.name}/channels/town-square`); }); - it.skip('MM-T1466 Accessibility Support in Direct Messages Dialog screen', () => { + it('MM-T1466 Accessibility Support in Direct Messages Dialog screen', () => { // * Verify the aria-label in create direct message button cy.uiAddDirectMessage().click(); @@ -53,11 +53,11 @@ describe('Verify Accessibility Support in Modals & Dialogs', () => { cy.findByRole('heading', {name: 'Direct Messages'}); // * Verify the accessibility support in search input - cy.findByRole('textbox', {name: 'Search for people'}). + cy.findByLabelText('Search for people'). should('have.attr', 'aria-autocomplete', 'list'); // # Search for a text and then check up and down arrow - cy.findByRole('textbox', {name: 'Search for people'}). + cy.findByLabelText('Search for people'). typeWithForce('s'). wait(TIMEOUTS.HALF_SEC). typeWithForce('{downarrow}{downarrow}{downarrow}{uparrow}'); @@ -80,7 +80,7 @@ describe('Verify Accessibility Support in Modals & Dialogs', () => { // # Search for an invalid text const additionalSearchTerm = 'somethingwhichdoesnotexist'; - cy.findByRole('textbox', {name: 'Search for people'}).clear(). + cy.findByLabelText('Search for people').clear(). typeWithForce(additionalSearchTerm). wait(TIMEOUTS.HALF_SEC); @@ -137,7 +137,7 @@ describe('Verify Accessibility Support in Modals & Dialogs', () => { }); }); - it.skip('MM-T1468 Accessibility Support in Add people to Channel Dialog screen', () => { + it('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 @@ -159,11 +159,11 @@ describe('Verify Accessibility Support in Modals & Dialogs', () => { cy.wait(TIMEOUTS.ONE_SEC); // * Verify the accessibility support in search input - cy.findByRole('textbox', {name: 'Search for people or groups'}). + cy.findByLabelText('Search for people or groups'). should('have.attr', 'aria-autocomplete', 'list'); // # Search for a text and then check up and down arrow - cy.findByRole('textbox', {name: 'Search for people or groups'}). + cy.findByLabelText('Search for people or groups'). wait(TIMEOUTS.HALF_SEC). typeWithForce('u'). wait(TIMEOUTS.HALF_SEC). @@ -189,7 +189,7 @@ describe('Verify Accessibility Support in Modals & Dialogs', () => { }); // # Search for an invalid text and check if reader can read no results - cy.findByRole('textbox', {name: 'Search for people or groups'}). + cy.findByLabelText('Search for people or groups'). typeWithForce('somethingwhichdoesnotexist'). wait(TIMEOUTS.HALF_SEC); @@ -203,7 +203,7 @@ describe('Verify Accessibility Support in Modals & Dialogs', () => { it('MM-T1515 Verify Accessibility Support in Invite People Flow', () => { // # Open Invite People cy.uiGetLHSHeader().click(); - cy.get('#invitePeople').should('be.visible').click(); + cy.get("#sidebarTeamMenu li:contains('Invite people')").should('be.visible').click(); // * Verify accessibility support in Invite People Dialog cy.findByTestId('invitationModal').should('have.attr', 'aria-modal', 'true').and('have.attr', 'aria-labelledby', 'invitation_modal_title').and('have.attr', 'role', 'dialog'); diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/cloud/billing/notify_admin_spec.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/cloud/billing/notify_admin_spec.ts index 9f4c0d33f9..36f3cc99ab 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/cloud/billing/notify_admin_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/cloud/billing/notify_admin_spec.ts @@ -111,9 +111,14 @@ function userGroupsNotification() { } function creatNewTeamNotification() { - cy.get('.test-team-header').click().then(() => { + // # Click on the team menu button + cy.get('#sidebarTeamMenuButton').click(); + + // # Click on the lock button on Create a team menu item + cy.get('#sidebarTeamMenu').within(() => { cy.get('#mattermost_feature_create_multiple_teams-restricted-indicator').click(); }); + cy.get('#FeatureRestrictedModal').should('exist'); cy.get('#button-plans').as('notifyButton').should('have.text', 'Notify admin').click(); cy.get('@notifyButton').should('have.text', 'Admin notified!'); diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_experience_ui_spec.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_experience_ui_spec.ts index cba8cd6db5..0d5cf52853 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_experience_ui_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_experience_ui_spec.ts @@ -64,24 +64,27 @@ describe('Guest Account - Guest User Experience', () => { // * Verify reduced options in Team Menu const missingMainOptions = [ - 'Invite People', - 'Team Settings', - 'Manage Members', - 'Join Another Team', - 'Create a Team', + 'Invite people', + 'Team settings', + 'Manage members', + 'Join another team', + 'Create a team', ]; missingMainOptions.forEach((missingOption) => { cy.uiGetLHSTeamMenu().should('not.contain', missingOption); }); const includeMainOptions = [ - 'View Members', - 'Leave Team', + 'View members', + 'Leave team', ]; includeMainOptions.forEach((includeOption) => { cy.uiGetLHSTeamMenu().findByText(includeOption); }); + // # Close the main menu + cy.get('body').type('{esc}'); + // * Verify Reduced Options in LHS cy.uiGetLHSAddChannelButton().should('not.exist'); @@ -120,7 +123,7 @@ describe('Guest Account - Guest User Experience', () => { cy.uiGetLhsSection('CHANNELS').find('.SidebarChannel').should('have.length', 3); // * Verify list of Users a Guest User can see in Team Members dialog - cy.uiOpenTeamMenu('View Members'); + cy.uiOpenTeamMenu('View members'); cy.get('#searchableUserListTotal').should('be.visible').and('have.text', '1 - 2 members of 2 total'); }); @@ -136,17 +139,18 @@ describe('Guest Account - Guest User Experience', () => { // * Verify options in team menu are changed cy.uiOpenTeamMenu(); const includeOptions = [ - 'Invite People', - 'View Members', - 'Leave Team', - 'Create a Team', + 'Invite people', + 'View members', + 'Leave team', + 'Create a team', ]; includeOptions.forEach((option) => { cy.uiGetLHSTeamMenu().findByText(option); }); - // # Close the main menu - cy.uiGetLHSHeader().click(); + // Close the main menu with Escape key + cy.get('body').type('{esc}'); + cy.uiGetLHSTeamMenu().should('not.exist'); // * Verify Options in LHS are changed cy.uiGetLHSAddChannelButton(); diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_identification_spec.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_identification_spec.ts index 71d326c2b3..2ade05b9be 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_identification_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_identification_spec.ts @@ -101,7 +101,7 @@ describe('Guest Accounts', () => { cy.visit(`/${testTeam.name}/channels/town-square`); // # Open team menu, click Invite People, then invite guest - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); cy.findByTestId('inviteGuestLink').click(); // # Type guest user e-mail address. diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_identification_ui_spec.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_identification_ui_spec.ts index 66fe20055b..889096fcf9 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_identification_ui_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_identification_ui_spec.ts @@ -68,7 +68,7 @@ describe('Verify Guest User Identification in different screens', () => { it('Verify Guest Badge in Team Members dialog', () => { // # Open team menu and click 'View Members' - cy.uiOpenTeamMenu('View Members'); + cy.uiOpenTeamMenu('View members'); cy.get('#teamMembersModal').should('be.visible').within(($el) => { cy.wrap($el).findAllByTestId('userListItemDetails').each(($elChild) => { diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_invitation_ui_spec.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_invitation_ui_spec.ts index 1e3222cc59..1fa5264415 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_invitation_ui_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_invitation_ui_spec.ts @@ -53,7 +53,7 @@ describe('Guest Account - Guest User Invitation Flow', () => { it('MM-T4451 Verify UI Elements of Guest User Invitation Flow', () => { // # Open team menu and click 'Invite People' - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // * Verify Invite Guest link cy.findByTestId('inviteGuestLink').should('be.visible').click(); @@ -117,7 +117,7 @@ describe('Guest Account - Guest User Invitation Flow', () => { cy.visit(`/${testTeam.name}/channels/town-square`); // # Open team menu and click 'Invite People' - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // * Verify if Invite Members modal is displayed when guest account feature is disabled cy.findByTestId('invitationModal').find('h1').should('have.text', `Invite people to ${testTeam.display_name}`); @@ -164,7 +164,7 @@ describe('Guest Account - Guest User Invitation Flow', () => { cy.apiDemoteUserToGuest(newUser.id); // # Open team menu and click 'Invite People' - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // # Click invite members if needed cy.get('.InviteAs').findByTestId('inviteMembersLink').click(); @@ -226,7 +226,7 @@ describe('Guest Account - Guest User Invitation Flow', () => { it('hides the copy link button when inviting guests', () => { // # Open team menu and click 'Invite People' - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // # Select Guest cy.findByTestId('inviteGuestLink').should('be.visible').click(); diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/helpers.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/helpers.ts index 341f0cf0ed..3205a0cad7 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/helpers.ts +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/helpers.ts @@ -16,7 +16,7 @@ export function changeGuestFeatureSettings(featureFlag = true, emailInvitation = export function invitePeople(typeText: string, resultsCount: number, verifyText: string, channelName = 'Town Square', clickInvite = true) { // # Open team menu and click 'Invite People' - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // # Click on the next icon to invite guest cy.findByTestId('inviteGuestLink').click(); diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/member_invitation_ui_spec.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/member_invitation_ui_spec.ts index 3b64ec9396..7fabe813bd 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/member_invitation_ui_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/member_invitation_ui_spec.ts @@ -52,7 +52,7 @@ describe('Guest Account - Member Invitation Flow', () => { const email = `temp-${getRandomId()}@mattermost.com`; // # Open team menu and click 'Invite People' - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // * Verify UI Elements in initial step cy.findByTestId('invitationModal').within(() => { @@ -251,7 +251,7 @@ describe('Guest Account - Member Invitation Flow', () => { function invitePeople(typeText, resultsCount, verifyText, clickInvite = true) { // # Open team menu and click 'Invite People' - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // # Search and add a member cy.get('.users-emails-input__control').should('be.visible').within(() => { diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/system_console_guest_access_ui_spec.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/system_console_guest_access_ui_spec.ts index 09d222d47d..f5af86137a 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/system_console_guest_access_ui_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/system_console_guest_access_ui_spec.ts @@ -103,7 +103,7 @@ describe('Guest Account - Verify Guest Access UI', () => { }); // # Open team menu and click 'Invite People' - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // * Verify that an option to Invite via Guest should not be available cy.findByTestId('inviteGuestLink').should('not.exist'); diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/ldap/ldap_guest_spec.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/ldap/ldap_guest_spec.ts index 369af83800..3413a30fbf 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/ldap/ldap_guest_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/ldap/ldap_guest_spec.ts @@ -219,7 +219,7 @@ describe('LDAP guest', () => { cy.visit(`/${team.name}/channels/town-square`); // # Open team menu and click 'Invite People' - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); cy.wait(TIMEOUTS.TWO_SEC); diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/ldap_group/invite_bot_spec.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/ldap_group/invite_bot_spec.ts index 8bffb1172c..9a181e0cc6 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/ldap_group/invite_bot_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/ldap_group/invite_bot_spec.ts @@ -54,7 +54,7 @@ describe('Group Synced Team - Bot invitation flow', () => { cy.visit(`/${groupConstrainedTeam.name}`); // # Click 'Invite People' at team menu - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // # Type the first letters of a bot cy.get('.users-emails-input__control input').typeWithForce(bot.username); @@ -75,7 +75,7 @@ describe('Group Synced Team - Bot invitation flow', () => { cy.visit(`/${groupConstrainedTeam.name}`); // # Click 'Manage Members' at team menu - cy.uiOpenTeamMenu('Manage Members'); + cy.uiOpenTeamMenu('Manage members'); // # Search for the bot that we want to remove cy.get('#searchUsersInput').should('be.visible').type(bot.username); 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 866ba589f1..c3c971cd95 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 @@ -95,10 +95,10 @@ describe('Team Permissions', () => { cy.uiOpenTeamMenu().wait(TIMEOUTS.HALF_SEC); // * Verify `Invite People` menu item is not present - cy.get('#invitePeople').should('not.exist'); + cy.get("#sidebarTeamMenu li:contains('Invite people')").should('not.exist'); // # Click `View Members` menu item - cy.get('#viewMembers').should('be.visible').click().wait(TIMEOUTS.HALF_SEC); + cy.get("#sidebarTeamMenu li:contains('View members')").should('be.visible').click().wait(TIMEOUTS.HALF_SEC); // * Verify team members modal opens cy.get('#teamMembersModal').should('be.visible'); diff --git a/e2e-tests/cypress/tests/integration/channels/keyboard_shortcuts/ctrl_cmd_shift_slash/not_open_emoji_picker_spec.js b/e2e-tests/cypress/tests/integration/channels/keyboard_shortcuts/ctrl_cmd_shift_slash/not_open_emoji_picker_spec.js index 16ae0b1cca..cebccc88a3 100644 --- a/e2e-tests/cypress/tests/integration/channels/keyboard_shortcuts/ctrl_cmd_shift_slash/not_open_emoji_picker_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/keyboard_shortcuts/ctrl_cmd_shift_slash/not_open_emoji_picker_spec.js @@ -108,7 +108,7 @@ describe('Keyboard shortcut CTRL/CMD+Shift+\\ for adding reaction to last messag cy.uiOpenProductMenu('About Mattermost'); verifyEmojiPickerNotOpen(); - cy.uiOpenTeamMenu('View Members'); + cy.uiOpenTeamMenu('View members'); verifyEmojiPickerNotOpen(); cy.uiOpenProfileModal('Profile Settings'); @@ -167,9 +167,6 @@ describe('Keyboard shortcut CTRL/CMD+Shift+\\ for adding reaction to last messag // # Close the expanded RHS cy.findByLabelText('Collapse Sidebar Icon').click(); - // # Open the pinned posts - cy.uiGetChannelPinButton().click(); - // # Expand RHS cy.findByLabelText('Expand Sidebar Icon').click(); diff --git a/e2e-tests/cypress/tests/integration/channels/messaging/message_reply_spec.js b/e2e-tests/cypress/tests/integration/channels/messaging/message_reply_spec.js index 86cf307a7a..b004206383 100644 --- a/e2e-tests/cypress/tests/integration/channels/messaging/message_reply_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/messaging/message_reply_spec.js @@ -21,6 +21,7 @@ describe('Message Reply', () => { newChannel = channel; cy.visit(`/${team.name}/channels/${channel.name}`); + // HERE WE NEED TO UPDATE THE AREA SINCE WE REMOVED THIS ID // # Wait for the page to fully load before continuing cy.get('#sidebar-header-container').should('be.visible').and('have.text', team.display_name); }); diff --git a/e2e-tests/cypress/tests/integration/channels/multi_team_and_dm/send_dm_user_no_team_spec.js b/e2e-tests/cypress/tests/integration/channels/multi_team_and_dm/send_dm_user_no_team_spec.js index 0b3fb03982..26d52ade6f 100644 --- a/e2e-tests/cypress/tests/integration/channels/multi_team_and_dm/send_dm_user_no_team_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/multi_team_and_dm/send_dm_user_no_team_spec.js @@ -38,7 +38,7 @@ describe('Send a DM', () => { cy.visit(testChannelUrl); cy.get('#postListContent', {timeout: TIMEOUTS.HALF_MIN}).should('be.visible'); cy.uiGetLHSHeader().click(); - cy.findByText('Leave Team').click(); + cy.findByText('Leave team').click(); cy.findByText('Yes').click(); cy.url().should('include', '/select_team'); diff --git a/e2e-tests/cypress/tests/integration/channels/notifications/browser_tab_notification_2_spec.js b/e2e-tests/cypress/tests/integration/channels/notifications/browser_tab_notification_2_spec.js index 878bf6c797..121ce066fa 100644 --- a/e2e-tests/cypress/tests/integration/channels/notifications/browser_tab_notification_2_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/notifications/browser_tab_notification_2_spec.js @@ -108,7 +108,7 @@ describe('Notifications', () => { // # Invites User 1 cy.get('#member_popover').should('be.visible').click(); - cy.contains('Manage Members').click(); + cy.contains('Manage members').click(); cy.contains('Add Members').click(); cy.get('.channel-invite__content'). should('be.visible'). diff --git a/e2e-tests/cypress/tests/integration/channels/onboarding/invalidate_pending_email_invitations_spec.js b/e2e-tests/cypress/tests/integration/channels/onboarding/invalidate_pending_email_invitations_spec.js index 29206155ac..6305d09b42 100644 --- a/e2e-tests/cypress/tests/integration/channels/onboarding/invalidate_pending_email_invitations_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/onboarding/invalidate_pending_email_invitations_spec.js @@ -64,7 +64,7 @@ describe('Onboarding', () => { cy.reload(); // # Open the 'Invite People' modal - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // # Wait half a second to ensure that the modal has been fully loaded cy.wait(TIMEOUTS.HALF_SEC); diff --git a/e2e-tests/cypress/tests/integration/channels/onboarding/login_page_link_account_creation_spec.js b/e2e-tests/cypress/tests/integration/channels/onboarding/login_page_link_account_creation_spec.js index 3e9bc9a1ea..b1c6bd9866 100644 --- a/e2e-tests/cypress/tests/integration/channels/onboarding/login_page_link_account_creation_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/onboarding/login_page_link_account_creation_spec.js @@ -44,7 +44,7 @@ describe('Onboarding', () => { it('MM-T400 Create account from login page link using email-password', () => { // # Open team menu and click on "Team Settings" - cy.uiOpenTeamMenu('Team Settings'); + cy.uiOpenTeamMenu('Team settings'); // * Check that the 'Team Settings' modal was opened cy.get('#teamSettingsModal').should('exist').within(() => { diff --git a/e2e-tests/cypress/tests/integration/channels/onboarding/use_team_invite_link_to_sign_up_spec.js b/e2e-tests/cypress/tests/integration/channels/onboarding/use_team_invite_link_to_sign_up_spec.js index ca47b253b6..ea10dd6eb6 100644 --- a/e2e-tests/cypress/tests/integration/channels/onboarding/use_team_invite_link_to_sign_up_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/onboarding/use_team_invite_link_to_sign_up_spec.js @@ -47,7 +47,7 @@ describe('Onboarding', () => { stubClipboard().as('clipboard'); // # Open the 'Invite People' full screen modal and get the invite url - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // # Copy invite link to clipboard cy.findByTestId('InviteView__copyInviteLink').click(); diff --git a/e2e-tests/cypress/tests/integration/channels/system_console/environment_spec.js b/e2e-tests/cypress/tests/integration/channels/system_console/environment_spec.js index 1fe8f8c5bb..ad67f76ad1 100644 --- a/e2e-tests/cypress/tests/integration/channels/system_console/environment_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/system_console/environment_spec.js @@ -35,7 +35,7 @@ describe('Environment', () => { cy.visit(townsquareLink); // # Open team menu and click "Team Settings" - cy.uiOpenTeamMenu('Team Settings'); + cy.uiOpenTeamMenu('Team settings'); // * Check that the 'Team Settings' modal was opened cy.get('#teamSettingsModal').should('exist').within(() => { @@ -79,7 +79,7 @@ describe('Environment', () => { cy.visit(townsquareLink); // # Open team menu and click "Team Settings" - cy.uiOpenTeamMenu('Team Settings'); + cy.uiOpenTeamMenu('Team settings'); // * Check that the 'Team Settings' modal was opened cy.get('#teamSettingsModal').should('exist').within(() => { @@ -123,7 +123,7 @@ describe('Environment', () => { cy.visit(townsquareLink); // # Open team menu and click "Team Settings" - cy.uiOpenTeamMenu('Team Settings'); + cy.uiOpenTeamMenu('Team settings'); // * Check that the 'Team Settings' modal was opened cy.get('#teamSettingsModal').should('exist').within(() => { diff --git a/e2e-tests/cypress/tests/integration/channels/system_console/user_management_not_cloud_spec.js b/e2e-tests/cypress/tests/integration/channels/system_console/user_management_not_cloud_spec.js index 800c06e924..4173546a70 100644 --- a/e2e-tests/cypress/tests/integration/channels/system_console/user_management_not_cloud_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/system_console/user_management_not_cloud_spec.js @@ -66,7 +66,7 @@ describe('User Management', () => { cy.visit(`/${testTeam.name}/channels/${testChannel.name}`); // # Open team menu and click View Members - cy.uiOpenTeamMenu('View Members'); + cy.uiOpenTeamMenu('View members'); // * Check View Members modal dialog cy.get('#teamMembersModal').should('be.visible').within(() => { diff --git a/e2e-tests/cypress/tests/integration/channels/team_settings/closed_team_invite_by_email_spec.js b/e2e-tests/cypress/tests/integration/channels/team_settings/closed_team_invite_by_email_spec.js index e130335449..f79a019a9f 100644 --- a/e2e-tests/cypress/tests/integration/channels/team_settings/closed_team_invite_by_email_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/team_settings/closed_team_invite_by_email_spec.js @@ -55,7 +55,7 @@ describe('Team Settings', () => { it('MM-T385 Invite new user to closed team using email invite', () => { // # Open 'Team Settings' modal - cy.uiOpenTeamMenu('Team Settings'); + cy.uiOpenTeamMenu('Team settings'); // * Check that the 'Team Settings' modal was opened cy.get('#teamSettingsModal').should('exist').within(() => { @@ -75,7 +75,7 @@ describe('Team Settings', () => { }); // # Open the 'Invite People' full screen modal - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // # Wait half a second to ensure that the modal has been fully loaded cy.wait(TIMEOUTS.HALF_SEC); diff --git a/e2e-tests/cypress/tests/integration/channels/team_settings/create_a_team_spec.js b/e2e-tests/cypress/tests/integration/channels/team_settings/create_a_team_spec.js index c2ee706eb8..4e55b55d71 100644 --- a/e2e-tests/cypress/tests/integration/channels/team_settings/create_a_team_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/team_settings/create_a_team_spec.js @@ -23,7 +23,7 @@ describe('Teams Suite', () => { it('MM-T383 Create a new team', () => { // # Open team menu and click "Create a Team" - cy.uiOpenTeamMenu('Create a Team'); + cy.uiOpenTeamMenu('Create a team'); // # Input team name as Team Test const teamName = 'Team Test'; @@ -69,7 +69,7 @@ describe('Teams Suite', () => { function tryReservedTeamURLAndVerifyError(teamURL) { // # Open team menu and click "Create a Team" - cy.uiOpenTeamMenu('Create a Team'); + cy.uiOpenTeamMenu('Create a team'); // # Input passed in team name cy.get('#teamNameInput').should('be.visible').type(teamURL); diff --git a/e2e-tests/cypress/tests/integration/channels/team_settings/helpers.js b/e2e-tests/cypress/tests/integration/channels/team_settings/helpers.js index 8af3462cfe..fe1435c1cb 100644 --- a/e2e-tests/cypress/tests/integration/channels/team_settings/helpers.js +++ b/e2e-tests/cypress/tests/integration/channels/team_settings/helpers.js @@ -10,7 +10,7 @@ import { export const allowOnlyUserFromSpecificDomain = (domain) => { // # Open 'Team Settings' modal - cy.uiOpenTeamMenu('Team Settings'); + cy.uiOpenTeamMenu('Team settings'); // * Check that the 'Team Settings' modal was opened cy.get('#teamSettingsModal').should('exist').within(() => { @@ -31,7 +31,7 @@ export const allowOnlyUserFromSpecificDomain = (domain) => { export const inviteUserByEmail = (email) => { // # Open team menu and click 'Invite People' - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // # Wait half a second to ensure that the modal has been fully loaded cy.wait(TIMEOUTS.HALF_SEC); diff --git a/e2e-tests/cypress/tests/integration/channels/team_settings/invite_members_backdrop_spec.js b/e2e-tests/cypress/tests/integration/channels/team_settings/invite_members_backdrop_spec.js index 1360c0f227..b73b8bf1a0 100644 --- a/e2e-tests/cypress/tests/integration/channels/team_settings/invite_members_backdrop_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/team_settings/invite_members_backdrop_spec.js @@ -32,7 +32,7 @@ describe('Invite Members', () => { cy.visit('/'); // # Open and select invite menu item - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); }); it('allows user to exit when there are no inputs', () => { diff --git a/e2e-tests/cypress/tests/integration/channels/team_settings/invite_members_spec.js b/e2e-tests/cypress/tests/integration/channels/team_settings/invite_members_spec.js index 9f4571d7f1..7412d1527d 100644 --- a/e2e-tests/cypress/tests/integration/channels/team_settings/invite_members_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/team_settings/invite_members_spec.js @@ -111,7 +111,7 @@ describe('Invite Members', () => { cy.visit(`/${testTeam.name}/channels/town-square`); // # Open and select invite menu item - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // * Users emails input is focused by default cy.get('.users-emails-input__control--is-focused').should('be.visible'); @@ -181,7 +181,7 @@ function inviteUserToTeamAsMember(testUser, testTeam, user) { cy.visit(`/${testTeam.name}/channels/town-square`); // # Open and select invite menu item - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // * Verify Invite Members verifyInviteMembersModal(testTeam); @@ -196,7 +196,7 @@ function inviteUserToTeamAsSysadmin(testTeam, user) { cy.visit(`/${testTeam.name}/channels/off-topic`); // # Open and select invite menu item - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // * Verify Invite Members verifyInviteMembersModal(testTeam); diff --git a/e2e-tests/cypress/tests/integration/channels/team_settings/invite_user_to_closed_team_spec.js b/e2e-tests/cypress/tests/integration/channels/team_settings/invite_user_to_closed_team_spec.js index 9b629e9a9f..6e61eae7c3 100644 --- a/e2e-tests/cypress/tests/integration/channels/team_settings/invite_user_to_closed_team_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/team_settings/invite_user_to_closed_team_spec.js @@ -34,7 +34,7 @@ describe('Team Settings', () => { const inviteFailedMessage = `The following email addresses do not belong to an accepted domain: ${invalidEmail}. Please contact your System Administrator for details.`; // # Open team menu and click 'Team Settings' - cy.uiOpenTeamMenu('Team Settings'); + cy.uiOpenTeamMenu('Team settings'); // * Check that the 'Team Settings' modal was opened cy.get('#teamSettingsModal').should('exist').within(() => { @@ -55,7 +55,7 @@ describe('Team Settings', () => { }); // # Open team menu and click 'Invite People' - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // # Invite user with valid email domain that is not in the team inviteNewMemberToTeam(newUser.email); diff --git a/e2e-tests/cypress/tests/integration/channels/team_settings/join_closed_team_with_not_allowed_email_spec.js b/e2e-tests/cypress/tests/integration/channels/team_settings/join_closed_team_with_not_allowed_email_spec.js index 4371ca2ffa..609b2d4da6 100644 --- a/e2e-tests/cypress/tests/integration/channels/team_settings/join_closed_team_with_not_allowed_email_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/team_settings/join_closed_team_with_not_allowed_email_spec.js @@ -42,7 +42,7 @@ describe('Team Settings', () => { stubClipboard().as('clipboard'); // # Open team menu and click 'Team Settings' - cy.uiOpenTeamMenu('Team Settings'); + cy.uiOpenTeamMenu('Team settings'); // * Check that the 'Team Settings' modal was opened cy.get('#teamSettingsModal').should('exist').within(() => { @@ -63,7 +63,7 @@ describe('Team Settings', () => { cy.uiClose(); // # Open team menu and click 'Invite People' - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // # Get the invite URL cy.findByTestId('InviteView__copyInviteLink').should('be.visible').click(); @@ -94,7 +94,7 @@ describe('Team Settings', () => { it('MM-T2341 Cannot add a user to a team if the user\'s email is not from the correct domain', () => { // # Open team menu and click 'Team Settings' - cy.uiOpenTeamMenu('Team Settings'); + cy.uiOpenTeamMenu('Team settings'); // * Check that the 'Team Settings' modal was opened cy.get('#teamSettingsModal').should('exist').within(() => { @@ -136,7 +136,7 @@ describe('Team Settings', () => { cy.visit(`/${otherTeam.name}/channels/town-square`); // # Open team menu and click 'Join Another Team' - cy.uiOpenTeamMenu('Join Another Team'); + cy.uiOpenTeamMenu('Join another team'); // # Try to join the existing team cy.get('.signup-team-dir').find(`#${testTeam.display_name.replace(' ', '_')}`).scrollIntoView().click(); diff --git a/e2e-tests/cypress/tests/integration/channels/team_settings/manage_members_spec.js b/e2e-tests/cypress/tests/integration/channels/team_settings/manage_members_spec.js index 5de377c63c..a8445a38ac 100644 --- a/e2e-tests/cypress/tests/integration/channels/team_settings/manage_members_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/team_settings/manage_members_spec.js @@ -34,7 +34,7 @@ describe('Manage Members', () => { cy.visit(`/${testTeam.name}/channels/town-square`); // # Open team menu and click 'Manage Members' - cy.uiOpenTeamMenu('Manage Members'); + cy.uiOpenTeamMenu('Manage members'); // # Open member dropdown cy.get(`#teamMembersDropdown_${testUser.username}`).should('be.visible').click(); @@ -61,7 +61,7 @@ describe('Manage Members', () => { cy.visit(`/${testTeam.name}/channels/town-square`); // # Open team menu and click 'Manage Members' - cy.uiOpenTeamMenu('Manage Members'); + cy.uiOpenTeamMenu('Manage members'); // # Open member dropdown cy.get(`#teamMembersDropdown_${user.username}`).should('be.visible').click(); @@ -94,7 +94,7 @@ describe('Manage Members', () => { cy.visit(`/${testTeam.name}/channels/town-square`); // # Open team menu and click 'Manage Members' - cy.uiOpenTeamMenu('Manage Members'); + cy.uiOpenTeamMenu('Manage members'); // # Open member dropdown cy.get(`#teamMembersDropdown_${user.username}`).should('be.visible').click(); @@ -139,7 +139,7 @@ describe('Manage Members', () => { cy.visit(`/${testTeam.name}/channels/town-square`); // # Open team menu and click 'Manage Members' - cy.uiOpenTeamMenu('Manage Members'); + cy.uiOpenTeamMenu('Manage members'); // # Open member dropdown cy.get(`#teamMembersDropdown_${user.username}`).should('be.visible').click(); diff --git a/e2e-tests/cypress/tests/integration/channels/team_settings/remove_team_icon_spec.js b/e2e-tests/cypress/tests/integration/channels/team_settings/remove_team_icon_spec.js index ca9fb8547e..11158ca620 100644 --- a/e2e-tests/cypress/tests/integration/channels/team_settings/remove_team_icon_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/team_settings/remove_team_icon_spec.js @@ -61,7 +61,7 @@ describe('Teams Settings', () => { function openTeamSettingsDialog() { // # Open team menu and click 'Team Settings' - cy.uiOpenTeamMenu('Team Settings'); + cy.uiOpenTeamMenu('Team settings'); // * Verify the team settings dialog is open cy.get('#teamSettingsModalLabel').should('be.visible').and('contain', 'Team Settings'); diff --git a/e2e-tests/cypress/tests/integration/channels/team_settings/teammates_pagination_spec.js b/e2e-tests/cypress/tests/integration/channels/team_settings/teammates_pagination_spec.js index 1f8683d579..6e1ddeb024 100755 --- a/e2e-tests/cypress/tests/integration/channels/team_settings/teammates_pagination_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/team_settings/teammates_pagination_spec.js @@ -50,7 +50,7 @@ describe('Teams Suite', () => { it('MM-T384 Team Admin can use Next button to page through list in Manage Members', () => { // # Open team menu and click "Manage Members" - cy.uiOpenTeamMenu('Manage Members'); + cy.uiOpenTeamMenu('Manage members'); // * Check Manage Members modal dialog cy.get('#teamMemberModalLabel').should('be.visible'); diff --git a/e2e-tests/cypress/tests/integration/channels/team_settings/teams_spec.js b/e2e-tests/cypress/tests/integration/channels/team_settings/teams_spec.js index 84f1228476..46c43be0fd 100644 --- a/e2e-tests/cypress/tests/integration/channels/team_settings/teams_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/team_settings/teams_spec.js @@ -41,7 +41,7 @@ describe('Teams Suite', () => { cy.url().should('include', `/${testTeam.name}/channels/town-square`); // # Open team menu and click "Manage Members" - cy.uiOpenTeamMenu('Leave Team'); + cy.uiOpenTeamMenu('Leave team'); // * Check that the "leave team modal" opened up cy.get('#leaveTeamModal').should('be.visible'); @@ -75,8 +75,8 @@ describe('Teams Suite', () => { // # Open team menu and click "Invite People" cy.uiOpenTeamMenu(); - cy.uiGetLHSTeamMenu().findByText('Add people to the team'); - cy.uiGetLHSTeamMenu().findByText('Invite People').click().wait(TIMEOUTS.HALF_SEC); + cy.uiGetLHSTeamMenu().find('div.label-elements > span:nth-child(2)').should('have.text', 'Add or invite people to the team'); + cy.uiGetLHSTeamMenu().find("li span:contains('Invite people')").should('have.text', 'Invite people').click().wait(TIMEOUTS.HALF_SEC); // * Check that the Invitation Modal opened up cy.findByTestId('invitationModal', {timeout: TIMEOUTS.HALF_SEC}).should('be.visible'); @@ -184,7 +184,7 @@ describe('Teams Suite', () => { cy.visit(`/${testTeam.name}/channels/town-square`); // # Open team menu and click "Team Settings" - cy.uiOpenTeamMenu('Team Settings'); + cy.uiOpenTeamMenu('Team settings'); // # Go to Access section cy.get('#accessButton').click(); @@ -208,7 +208,7 @@ describe('Teams Suite', () => { cy.visit(`/${testTeam.name}/channels/town-square`); // # Open team menu and click "Team Settings" - cy.uiOpenTeamMenu('Team Settings'); + cy.uiOpenTeamMenu('Team settings'); // # Click on the team name menu item cy.findByText('Team Name').should('be.visible').click(); @@ -236,7 +236,7 @@ describe('Teams Suite', () => { cy.visit(`/${testTeam.name}/channels/town-square`); // # Open team menu and click "Team Settings" - cy.uiOpenTeamMenu('Team Settings'); + cy.uiOpenTeamMenu('Team settings'); // # Change team description in the input cy.get('#teamDescription').should('be.visible').clear().type(teamDescription); @@ -246,7 +246,7 @@ describe('Teams Suite', () => { cy.uiSaveAndClose(); // # Open team menu and click "Team Settings" - cy.uiOpenTeamMenu('Team Settings'); + cy.uiOpenTeamMenu('Team settings'); // * Verify team description is updated cy.get('#teamDescription').should('have.text', teamDescription); @@ -257,7 +257,7 @@ describe('Teams Suite', () => { cy.visit(`/${testTeam.name}/channels/town-square`); // # Open team menu and click "Team Settings" - cy.uiOpenTeamMenu('Team Settings'); + cy.uiOpenTeamMenu('Team settings'); // # Go to Access section cy.get('#accessButton').click(); @@ -296,7 +296,7 @@ describe('Teams Suite', () => { cy.visit(`/${testTeam.name}/channels/town-square`); // # Open team menu and click "Team Settings" - cy.uiOpenTeamMenu('Team Settings'); + cy.uiOpenTeamMenu('Team settings'); // # Go to Access section cy.get('#accessButton').click(); @@ -314,7 +314,7 @@ describe('Teams Suite', () => { cy.visit(`/${testTeam.name}/channels/town-square`); // # Open team menu and click "Join Another Team" - cy.uiOpenTeamMenu('Join Another Team'); + cy.uiOpenTeamMenu('Join another team'); // # Verify the original test team isn't on the list cy.get('.signup-team-dir').children().should('not.contain', `#${testTeam.name.charAt(0).toUpperCase() + testTeam.name.slice(1)}`); @@ -329,7 +329,7 @@ describe('Teams Suite', () => { cy.visit(`/${testTeam.name}/channels/town-square`); // # Open team menu and click "View Members" - cy.uiOpenTeamMenu('View Members'); + cy.uiOpenTeamMenu('View members'); cy.wait(TIMEOUTS.HALF_SEC); cy.get('#searchUsersInput').should('be.visible').type('sysadmin'); cy.get('.more-modal__list').should('be.visible').children().should('have.length', 1); @@ -341,7 +341,7 @@ function removeTeamMember(teamName, username) { cy.visit(`/${teamName}`); // # Open team menu and click "Manage Members" - cy.uiOpenTeamMenu('Manage Members'); + cy.uiOpenTeamMenu('Manage members'); cy.get(`#teamMembersDropdown_${username}`).should('be.visible').click(); cy.get('#removeFromTeam').should('be.visible').click(); diff --git a/e2e-tests/cypress/tests/support/saml_commands.ts b/e2e-tests/cypress/tests/support/saml_commands.ts index 17a4e2ec06..5143e96602 100644 --- a/e2e-tests/cypress/tests/support/saml_commands.ts +++ b/e2e-tests/cypress/tests/support/saml_commands.ts @@ -79,7 +79,7 @@ function getInvitePeopleLink(settings: TestSettings): ChainableT { cy.checkLeftSideBar(settings); // # Open team menu and click 'Invite People' - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); stubClipboard().as('clipboard'); cy.checkInvitePeoplePage(); diff --git a/e2e-tests/cypress/tests/support/ui/sidebar_left.ts b/e2e-tests/cypress/tests/support/ui/sidebar_left.ts index 1393478a87..eb1a4c77a9 100644 --- a/e2e-tests/cypress/tests/support/ui/sidebar_left.ts +++ b/e2e-tests/cypress/tests/support/ui/sidebar_left.ts @@ -9,7 +9,7 @@ Cypress.Commands.add('uiGetLHS', () => { Cypress.Commands.add('uiGetLHSHeader', () => { return cy.uiGetLHS(). - find('.SidebarHeaderMenuWrapper'). + find('#sidebarTeamMenuButton'). should('be.visible'); }); @@ -35,7 +35,7 @@ Cypress.Commands.add('uiGetLHSAddChannelButton', () => { }); Cypress.Commands.add('uiGetLHSTeamMenu', () => { - return cy.uiGetLHS().find('#sidebarDropdownMenu'); + return cy.get('#sidebarTeamMenu'); }); function uiOpenSystemConsoleMenu(item = ''): ChainableT { diff --git a/e2e-tests/cypress/tests/support/ui/team.ts b/e2e-tests/cypress/tests/support/ui/team.ts index 59be09e3d1..eef2e08db6 100644 --- a/e2e-tests/cypress/tests/support/ui/team.ts +++ b/e2e-tests/cypress/tests/support/ui/team.ts @@ -7,7 +7,7 @@ */ function uiInviteMemberToCurrentTeam(username: string) { // # Open member invite screen - cy.uiOpenTeamMenu('Invite People'); + cy.uiOpenTeamMenu('Invite people'); // # Open members section if licensed for guest accounts cy.findByTestId('invitationModal'). diff --git a/e2e-tests/cypress/tests/support/ui_commands.ts b/e2e-tests/cypress/tests/support/ui_commands.ts index 34ea4b0eb4..5a6cce74cd 100644 --- a/e2e-tests/cypress/tests/support/ui_commands.ts +++ b/e2e-tests/cypress/tests/support/ui_commands.ts @@ -393,8 +393,8 @@ function getCurrentTeamURL(siteURL: string): ChainableT { Cypress.Commands.add('getCurrentTeamURL', getCurrentTeamURL); function leaveTeam() { - // # Open team menu and click "Leave Team" - cy.uiOpenTeamMenu('Leave Team'); + // # Open team menu and click "Leave team" + cy.uiOpenTeamMenu('Leave team'); // * Check that the "leave team modal" opened up cy.get('#leaveTeamModal').should('be.visible'); diff --git a/e2e-tests/playwright/lib/src/test_action.ts b/e2e-tests/playwright/lib/src/test_action.ts index 54fb471f22..d9ca7e4d9c 100644 --- a/e2e-tests/playwright/lib/src/test_action.ts +++ b/e2e-tests/playwright/lib/src/test_action.ts @@ -5,7 +5,7 @@ import {Locator, Page} from '@playwright/test'; export {waitUntil} from 'async-wait-until'; const visibilityHidden = 'visibility: hidden !important;'; -const hideTeamHeader = `.test-team-header {${visibilityHidden}} `; +const hideTeamHeader = `#sidebarTeamMenuButton {${visibilityHidden}} `; const hidePostHeaderTime = `.post__time {${visibilityHidden}} `; const hidePostProfileIcon = `.profile-icon {${visibilityHidden}} `; diff --git a/webapp/channels/package.json b/webapp/channels/package.json index 5c77165c36..9f0a24cf76 100644 --- a/webapp/channels/package.json +++ b/webapp/channels/package.json @@ -12,7 +12,6 @@ "@guyplusplus/turndown-plugin-gfm": "1.0.7", "@mattermost/client": "*", "@mattermost/compass-components": "^0.2.12", - "@mattermost/compass-icons": "0.1.39", "@mattermost/desktop-api": "5.10.0-2", "@mattermost/types": "*", "@mui/base": "5.0.0-alpha.127", diff --git a/webapp/channels/src/components/add_groups_to_team_modal/add_groups_to_team_modal.tsx b/webapp/channels/src/components/add_groups_to_team_modal/add_groups_to_team_modal.tsx index 0b9eb53257..ca1770a9f1 100644 --- a/webapp/channels/src/components/add_groups_to_team_modal/add_groups_to_team_modal.tsx +++ b/webapp/channels/src/components/add_groups_to_team_modal/add_groups_to_team_modal.tsx @@ -17,6 +17,7 @@ import MultiSelect from 'components/multiselect/multiselect'; import type {Value} from 'components/multiselect/multiselect'; import groupsAvatar from 'images/groups-avatar.png'; +import {focusElement} from 'utils/a11y_utils'; import Constants from 'utils/constants'; const GROUPS_PER_PAGE = 50; @@ -30,6 +31,7 @@ type Props = { intl: IntlShape; searchTerm: string; groups: Group[]; + focusOriginElement?: string; // used in tandem with 'skipCommit' to allow using this component without performing actual linking excludeGroups?: Group[]; @@ -114,6 +116,9 @@ export class AddGroupsToTeamModal extends React.PureComponent { // public for tests public handleExit = (): void => { + if (this.props.focusOriginElement) { + focusElement(this.props.focusOriginElement, true); + } this.props.onExited(); }; diff --git a/webapp/channels/src/components/advanced_text_editor/use_post_box_indicator.test.tsx b/webapp/channels/src/components/advanced_text_editor/use_post_box_indicator.test.tsx index 6db93503c0..610269b334 100644 --- a/webapp/channels/src/components/advanced_text_editor/use_post_box_indicator.test.tsx +++ b/webapp/channels/src/components/advanced_text_editor/use_post_box_indicator.test.tsx @@ -1,6 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import {DateTime} from 'luxon'; import React from 'react'; import type {DeepPartial} from '@mattermost/types/utilities'; @@ -22,12 +23,6 @@ function getBaseState(): DeepPartial { type: 'D', name: 'current_user_id__teammate_user_id', }, - dm_near_timezone: { - id: 'dm_same_timezone', - teammate_id: 'teammate_near_timezone_id', - type: 'D', - name: 'current_user_id__teammate_near_timezone_id', - }, bot_dm_channel_id: { id: 'bot_dm_channel_id', teammate_id: 'bot_user_id', @@ -57,18 +52,6 @@ function getBaseState(): DeepPartial { manualTimezone: '', }, }, - teammate_near_timezone_id: { - id: 'teammate_near_timezone_id', - username: 'teammate_near_timezone_username', - nickname: 'teammate_near_timezone_nickname', - first_name: 'teammate_near_timezone_first_name', - last_name: 'teammate_near_timezone_last_name', - timezone: { - useAutomaticTimezone: 'false', - automaticTimezone: '', - manualTimezone: 'CET', - }, - }, bot_user_id: { id: 'bot_user_id', username: 'bot_username', @@ -109,12 +92,13 @@ function getBaseState(): DeepPartial { } describe('useTimePostBoxIndicator', () => { - beforeAll(() => { - jest.useFakeTimers(); - jest.setSystemTime(new Date('2021-01-01T18:00:00Z').getTime()); - }); - it('should pass base case', () => { + const fakeLocal = DateTime.local(2025, 1, 1, 3, { + zone: 'Asia/Kolkata', + }); + + DateTime.local = jest.fn(() => fakeLocal); + const {result: {current}} = renderHookWithContext(() => useTimePostBoxIndicator('dm_channel_id'), getBaseState()); expect(current.isDM).toBe(true); @@ -127,20 +111,12 @@ describe('useTimePostBoxIndicator', () => { expect(current.teammateTimezone.automaticTimezone).toBe('IST'); }); - it('should not show if within working hours', () => { - const {result: {current}} = renderHookWithContext(() => useTimePostBoxIndicator('dm_near_timezone'), getBaseState()); - - expect(current.isDM).toBe(true); - expect(current.showDndWarning).toBe(false); - expect(current.isSelfDM).toBe(false); - expect(current.isBot).toBe(false); - expect(current.showRemoteUserHour).toBe(false); - expect(current.isScheduledPostEnabled).toBe(true); - expect(current.teammateTimezone.useAutomaticTimezone).toBe(false); - expect(current.teammateTimezone.manualTimezone).toBe('CET'); - }); - it('should work for DM with bots', () => { + const fakeLocal = DateTime.local(2025, 1, 1, 3, { + zone: 'Asia/Kolkata', + }); + + DateTime.local = jest.fn(() => fakeLocal); const {result: {current}} = renderHookWithContext(() => useTimePostBoxIndicator('bot_dm_channel_id'), getBaseState()); expect(current.isDM).toBe(true); @@ -155,6 +131,12 @@ describe('useTimePostBoxIndicator', () => { }); it('should handle teammate not loaded', () => { + const fakeLocal = DateTime.local(2025, 1, 1, 1, { + zone: 'Asia/Kolkata', + }); + + DateTime.local = jest.fn(() => fakeLocal); + const {result: {current}} = renderHookWithContext(() => useTimePostBoxIndicator('unknown_dm_channel_id'), getBaseState()); expect(current.isDM).toBe(true); diff --git a/webapp/channels/src/components/channel_layout/center_channel/center_channel.tsx b/webapp/channels/src/components/channel_layout/center_channel/center_channel.tsx index 3b80ef7986..a3f02326b0 100644 --- a/webapp/channels/src/components/channel_layout/center_channel/center_channel.tsx +++ b/webapp/channels/src/components/channel_layout/center_channel/center_channel.tsx @@ -14,7 +14,7 @@ import {IDENTIFIER_PATH_PATTERN, ID_PATH_PATTERN, TEAM_NAME_PATH_PATTERN} from ' import type {OwnProps, PropsFromRedux} from './index'; -const ChannelHeaderMobile = makeAsyncComponent('ChannelHeaderMobile', lazy(() => import('components/channel_header_mobile'))); +const MobileChannelHeader = makeAsyncComponent('MobileChannelHeader', lazy(() => import('components/mobile_channel_header'))); const GlobalThreads = makeAsyncComponent('GlobalThreads', lazy(() => import('components/threading/global_threads')), (
@@ -76,15 +76,7 @@ export default class CenterChannel extends React.PureComponent { 'move--left-small': this.props.rhsMenuOpen, })} > - {isMobileView && ( - <> -
- -
- - )} + {isMobileView && }
({ ...jest.requireActual('mattermost-redux/selectors/entities/general') as typeof import('mattermost-redux/selectors/entities/general'), })); +jest.mock('selectors/views/browser', () => ({ + getIsMobileView: () => false, +})); + describe('ChannelController', () => { beforeEach(() => { mockState = { diff --git a/webapp/channels/src/components/channel_layout/channel_controller.tsx b/webapp/channels/src/components/channel_layout/channel_controller.tsx index 0e962e0ce9..0de9624415 100644 --- a/webapp/channels/src/components/channel_layout/channel_controller.tsx +++ b/webapp/channels/src/components/channel_layout/channel_controller.tsx @@ -9,6 +9,7 @@ import {cleanUpStatusAndProfileFetchingPoll} from 'mattermost-redux/actions/stat import {getIsUserStatusesConfigEnabled} from 'mattermost-redux/selectors/entities/common'; import {addVisibleUsersInCurrentChannelAndSelfToStatusPoll} from 'actions/status_actions'; +import {getIsMobileView} from 'selectors/views/browser'; import {makeAsyncComponent} from 'components/async_load'; import CenterChannel from 'components/channel_layout/center_channel'; @@ -24,6 +25,7 @@ import {isInternetExplorer, isEdge} from 'utils/user_agent'; const ProductNoticesModal = makeAsyncComponent('ProductNoticesModal', lazy(() => import('components/product_notices_modal'))); const ResetStatusModal = makeAsyncComponent('ResetStatusModal', lazy(() => import('components/reset_status_modal'))); +const MobileSidebarRight = makeAsyncComponent('MobileSidebarRight', lazy(() => import('components/mobile_sidebar_right'))); const BODY_CLASS_FOR_CHANNEL = ['app__body', 'channel-view']; @@ -32,6 +34,7 @@ type Props = { } export default function ChannelController(props: Props) { + const isMobileView = useSelector(getIsMobileView); const enabledUserStatuses = useSelector(getIsUserStatusesConfigEnabled); const dispatch = useDispatch(); @@ -85,6 +88,7 @@ export default function ChannelController(props: Props) {
+ {isMobileView && } ); } diff --git a/webapp/channels/src/components/invitation_modal/invitation_modal.tsx b/webapp/channels/src/components/invitation_modal/invitation_modal.tsx index 45656c39c0..e877b9d957 100644 --- a/webapp/channels/src/components/invitation_modal/invitation_modal.tsx +++ b/webapp/channels/src/components/invitation_modal/invitation_modal.tsx @@ -83,7 +83,7 @@ export type Props = { initialValue?: string; inviteAsGuest?: boolean; roleForTrackFlow: {started_by_role: string}; - focusOriginElement: string; + focusOriginElement?: string; } export const View = { @@ -132,7 +132,9 @@ export default class InvitationModal extends React.PureComponent { }; handleExit = () => { - focusElement(this.props.focusOriginElement, true); + if (this.props.focusOriginElement) { + focusElement(this.props.focusOriginElement, true); + } this.props.onExited?.(); }; diff --git a/webapp/channels/src/components/main_menu/__snapshots__/main_menu.test.tsx.snap b/webapp/channels/src/components/main_menu/__snapshots__/main_menu.test.tsx.snap deleted file mode 100644 index b9c8161bc0..0000000000 --- a/webapp/channels/src/components/main_menu/__snapshots__/main_menu.test.tsx.snap +++ /dev/null @@ -1,2393 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`components/Menu should match snapshot with cloud free and team limit reached 1`] = ` - - } - text="Create a Team" - to="/create_team" -/> -`; - -exports[`components/Menu should match snapshot with cloud free trial 1`] = ` - - } - text="Create a Team" - to="/create_team" -/> -`; - -exports[`components/Menu should match snapshot with guest access disabled and no team invite permission 1`] = ` - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
- -
-`; - -exports[`components/Menu should match snapshot with id 1`] = ` - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
- -
-`; - -exports[`components/Menu should match snapshot with most of the thing disabled 1`] = ` - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
- -
-`; - -exports[`components/Menu should match snapshot with most of the thing disabled in mobile 1`] = ` - - - - - - - - - - - - - - @ - - } - id="recentMentions" - onClick={[Function]} - show={true} - text="Recent Mentions" - /> - - } - id="flaggedPosts" - onClick={[Function]} - show={true} - text="Saved messages" - /> - - - - } - id="profileSettings" - modalId="user_settings" - show={true} - text="Profile" - /> - - } - id="accountSettings" - modalId="user_settings" - show={true} - text="Settings" - /> - - - - - } - id="addGroupsToTeam" - modalId="add_groups_to_team" - show={false} - text="Add Groups to Team" - /> - - - - } - id="invitePeople" - modalId="invitation" - onClick={[Function]} - show={true} - text="Invite People" - /> - - - - - - } - id="teamSettings" - modalId="team_settings" - show={true} - text="Team Settings" - /> - - - - } - id="manageGroups" - modalId="manage_team_groups" - show={false} - text="Manage Groups" - /> - - - - } - id="manageMembers" - modalId="team_members" - show={true} - text="Manage Members" - /> - - - - } - id="viewMembers" - modalId="team_members" - show={true} - text="View Members" - /> - - - - - - } - id="createTeam" - show={true} - text="Create a Team" - to="/create_team" - /> - - - } - id="joinTeam" - show={false} - text="Join Another Team" - to="/select_team" - /> - } - id="leaveTeam" - modalId="leave_team" - show={true} - text="Leave Team" - /> - - - - - - - - } - id="helpLink" - show={false} - text="Help" - /> - - } - id="reportLink" - show={false} - text="Report a Problem" - /> - - } - id="nativeAppLink" - show={true} - text="Download Apps" - url="" - /> - - } - id="about" - modalId="about" - show={true} - text="About Mattermost" - /> - - - - } - id="logout" - onClick={[Function]} - show={true} - text="Log Out" - /> - - -`; - -exports[`components/Menu should match snapshot with most of the thing enabled 1`] = ` - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
- -
-`; - -exports[`components/Menu should match snapshot with most of the thing enabled in mobile 1`] = ` - - - - - - - - - - - - - - @ - - } - id="recentMentions" - onClick={[Function]} - show={true} - text="Recent Mentions" - /> - - } - id="flaggedPosts" - onClick={[Function]} - show={true} - text="Saved messages" - /> - - - - } - id="profileSettings" - modalId="user_settings" - show={true} - text="Profile" - /> - - } - id="accountSettings" - modalId="user_settings" - show={true} - text="Settings" - /> - - - - - } - id="addGroupsToTeam" - modalId="add_groups_to_team" - show={false} - text="Add Groups to Team" - /> - - - - } - id="invitePeople" - modalId="invitation" - onClick={[Function]} - show={true} - text="Invite People" - /> - - - - - - } - id="teamSettings" - modalId="team_settings" - show={true} - text="Team Settings" - /> - - - - } - id="manageGroups" - modalId="manage_team_groups" - show={false} - text="Manage Groups" - /> - - - - } - id="manageMembers" - modalId="team_members" - show={true} - text="Manage Members" - /> - - - - } - id="viewMembers" - modalId="team_members" - show={true} - text="View Members" - /> - - - - - - } - id="createTeam" - show={true} - text="Create a Team" - to="/create_team" - /> - - - } - id="joinTeam" - show={false} - text="Join Another Team" - to="/select_team" - /> - } - id="leaveTeam" - modalId="leave_team" - show={true} - text="Leave Team" - /> - - - - - - - - } - id="helpLink" - show={true} - text="Help" - url="test-link-help" - /> - - } - id="reportLink" - show={true} - text="Report a Problem" - url="test-report-link" - /> - - } - id="nativeAppLink" - show={true} - text="Download Apps" - url="test" - /> - - } - id="about" - modalId="about" - show={true} - text="About Mattermost" - /> - - - - } - id="logout" - onClick={[Function]} - show={true} - text="Log Out" - /> - - -`; - -exports[`components/Menu should match snapshot with plugins 1`] = ` - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
- - - - -
-`; - -exports[`components/Menu should match snapshot with plugins in mobile 1`] = ` - - - - - - - - - - - - - - @ - - } - id="recentMentions" - onClick={[Function]} - show={true} - text="Recent Mentions" - /> - - } - id="flaggedPosts" - onClick={[Function]} - show={true} - text="Saved messages" - /> - - - - } - id="profileSettings" - modalId="user_settings" - show={true} - text="Profile" - /> - - } - id="accountSettings" - modalId="user_settings" - show={true} - text="Settings" - /> - - - - - } - id="addGroupsToTeam" - modalId="add_groups_to_team" - show={false} - text="Add Groups to Team" - /> - - - - } - id="invitePeople" - modalId="invitation" - onClick={[Function]} - show={true} - text="Invite People" - /> - - - - - - } - id="teamSettings" - modalId="team_settings" - show={true} - text="Team Settings" - /> - - - - } - id="manageGroups" - modalId="manage_team_groups" - show={false} - text="Manage Groups" - /> - - - - } - id="manageMembers" - modalId="team_members" - show={true} - text="Manage Members" - /> - - - - } - id="viewMembers" - modalId="team_members" - show={true} - text="View Members" - /> - - - - - - } - id="createTeam" - show={true} - text="Create a Team" - to="/create_team" - /> - - - } - id="joinTeam" - show={false} - text="Join Another Team" - to="/select_team" - /> - } - id="leaveTeam" - modalId="leave_team" - show={true} - text="Leave Team" - /> - - - - } - id="plugin-id-1_pluginmenuitem" - key="plugin-id-1_pluginmenuitem" - onClick={[Function]} - show={true} - text="some text" - /> - - } - id="plugind-id-2_pluginmenuitem" - key="plugind-id-2_pluginmenuitem" - onClick={[Function]} - show={true} - text="some text" - /> - - - - - - - } - id="helpLink" - show={false} - text="Help" - /> - - } - id="reportLink" - show={false} - text="Report a Problem" - /> - - } - id="nativeAppLink" - show={true} - text="Download Apps" - url="" - /> - - } - id="about" - modalId="about" - show={true} - text="About Mattermost" - /> - - - - } - id="logout" - onClick={[Function]} - show={true} - text="Log Out" - /> - - -`; diff --git a/webapp/channels/src/components/main_menu/learn_about_teams_link.scss b/webapp/channels/src/components/main_menu/learn_about_teams_link.scss deleted file mode 100644 index c3c6e1563a..0000000000 --- a/webapp/channels/src/components/main_menu/learn_about_teams_link.scss +++ /dev/null @@ -1,40 +0,0 @@ -.LearnAboutTeamsLink { - >span { - width: 100%; - - a { - display: flex; - align-items: center; - padding: 6px 18px; - column-gap: 10px; - - &:hover, - &:focus, - &:active { - text-decoration: none; - } - - >i { - display: inline-flex !important; - padding: 3px; - border-radius: 50%; - background-color: hsla(221, 77%, 48%, 0.08); - font-size: 14px !important; - - &::before { - margin: 0; - } - } - - span { - font-size: 12px; - font-weight: 600; - line-height: 16px; - - &:hover { - text-decoration: underline; - } - } - } - } -} diff --git a/webapp/channels/src/components/main_menu/learn_about_teams_link.tsx b/webapp/channels/src/components/main_menu/learn_about_teams_link.tsx deleted file mode 100644 index 8a02e8bd35..0000000000 --- a/webapp/channels/src/components/main_menu/learn_about_teams_link.tsx +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import React from 'react'; -import {FormattedMessage} from 'react-intl'; - -import ExternalLink from 'components/external_link'; -import './learn_about_teams_link.scss'; - -const LearnAboutTeamsLink = () => { - return ( -
- ( - - - {chunks} - - ), - }} - /> -
- ); -}; -export default LearnAboutTeamsLink; diff --git a/webapp/channels/src/components/main_menu/main_menu.scss b/webapp/channels/src/components/main_menu/main_menu.scss deleted file mode 100644 index 5d3b7facdf..0000000000 --- a/webapp/channels/src/components/main_menu/main_menu.scss +++ /dev/null @@ -1,13 +0,0 @@ -.MainMenu_dropdown-link { - a { - &:focus, - &:hover, - &:active { - background-color: rgba(63, 67, 80, 0.1); - } - - i + span:hover { - text-decoration: none; - } - } -} diff --git a/webapp/channels/src/components/main_menu/main_menu.test.tsx b/webapp/channels/src/components/main_menu/main_menu.test.tsx deleted file mode 100644 index 395a0556ae..0000000000 --- a/webapp/channels/src/components/main_menu/main_menu.test.tsx +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import {shallow} from 'enzyme'; -import type {ComponentProps} from 'react'; -import React from 'react'; -import {createIntl} from 'react-intl'; -import {Provider} from 'react-redux'; - -import {Permissions} from 'mattermost-redux/constants'; - -import Menu from 'components/widgets/menu/menu'; - -import {mountWithIntl} from 'tests/helpers/intl-test-helper'; -import mockStore from 'tests/test_store'; -import {TestHelper} from 'utils/test_helper'; - -import {MainMenu} from './main_menu'; -import type {Props} from './main_menu'; - -describe('components/Menu', () => { - // Neccessary for components enhanced by HOCs due to issue with enzyme. - // See https://github.com/enzymejs/enzyme/issues/539 - const getMainMenuWrapper = (props: Props) => { - return shallow(); - - // const wrapper = shallowWithIntl(); - // return wrapper.find('MainMenu').shallow(); - }; - - const defaultProps: ComponentProps = { - mobile: false, - teamId: 'team-id', - teamName: 'team_name', - currentUser: TestHelper.getUserMock(), - appDownloadLink: undefined, - enableCommands: false, - enableIncomingWebhooks: false, - enableOAuthServiceProvider: false, - enableOutgoingWebhooks: false, - canManageSystemBots: false, - canManageIntegrations: true, - experimentalPrimaryTeam: undefined, - helpLink: undefined, - reportAProblemLink: undefined, - moreTeamsToJoin: false, - pluginMenuItems: [], - isMentionSearch: false, - intl: createIntl({locale: 'en', defaultLocale: 'en', timeZone: 'Etc/UTC', textComponent: 'span'}), - guestAccessEnabled: true, - canInviteTeamMember: true, - actions: { - openModal: jest.fn(), - showMentions: jest.fn(), - showFlaggedPosts: jest.fn(), - closeRightHandSide: jest.fn(), - closeRhsMenu: jest.fn(), - }, - teamIsGroupConstrained: false, - isCloud: false, - isStarterFree: false, - isFreeTrial: false, - usageDeltaTeams: 1, - }; - - const defaultState = { - entities: { - channels: { - myMembers: {}, - }, - general: { - config: {}, - license: { - Cloud: 'false', - }, - }, - teams: { - currentTeamId: 'team-id', - myMembers: { - 'team-id': { - team_id: 'team-id', - user_id: 'test-user-id', - roles: 'team_user', - scheme_user: true, - }, - }, - }, - users: { - currentUserId: 'test-user-id', - profiles: { - 'test-user-id': { - id: 'test-user-id', - roles: 'system_user system_manager', - }, - }, - }, - roles: { - roles: { - system_manager: { - permissions: [ - Permissions.SYSCONSOLE_WRITE_PLUGINS, - ], - }, - }, - }, - }, - }; - - test('should match snapshot with id', () => { - const props = {...defaultProps, id: 'test-id'}; - const wrapper = getMainMenuWrapper(props); - expect(wrapper).toMatchSnapshot(); - }); - - test('should match snapshot with most of the thing disabled', () => { - const wrapper = getMainMenuWrapper(defaultProps); - expect(wrapper).toMatchSnapshot(); - }); - - test('should match snapshot with most of the thing disabled in mobile', () => { - const props = {...defaultProps, mobile: true}; - const wrapper = getMainMenuWrapper(props); - expect(wrapper).toMatchSnapshot(); - }); - - test('should match snapshot with most of the thing enabled', () => { - const props = { - ...defaultProps, - appDownloadLink: 'test', - enableCommands: true, - enableCustomEmoji: true, - canCreateOrDeleteCustomEmoji: true, - enableIncomingWebhooks: true, - enableOAuthServiceProvider: true, - enableOutgoingWebhooks: true, - enableUserCreation: true, - enableEmailInvitations: true, - enablePluginMarketplace: true, - experimentalPrimaryTeam: 'test', - helpLink: 'test-link-help', - reportAProblemLink: 'test-report-link', - moreTeamsToJoin: true, - }; - const wrapper = getMainMenuWrapper(props); - expect(wrapper).toMatchSnapshot(); - }); - - test('should match snapshot with most of the thing enabled in mobile', () => { - const props = { - ...defaultProps, - mobile: true, - appDownloadLink: 'test', - enableCommands: true, - enableCustomEmoji: true, - canCreateOrDeleteCustomEmoji: true, - enableIncomingWebhooks: true, - enableOAuthServiceProvider: true, - enableOutgoingWebhooks: true, - enableUserCreation: true, - enableEmailInvitations: true, - enablePluginMarketplace: true, - experimentalPrimaryTeam: 'test', - helpLink: 'test-link-help', - reportAProblemLink: 'test-report-link', - moreTeamsToJoin: true, - }; - const wrapper = getMainMenuWrapper(props); - expect(wrapper).toMatchSnapshot(); - }); - - test('should match snapshot with plugins', () => { - const props: ComponentProps = { - ...defaultProps, - pluginMenuItems: [{ - id: 'plugin-id-1', - pluginId: 'plugin-1', - mobileIcon: , - action: jest.fn, - text: 'some text', - }, - { - id: 'plugind-id-2', - pluginId: 'plugin-2', - mobileIcon: , - action: jest.fn, - text: 'some text', - }, - ], - }; - const wrapper = getMainMenuWrapper(props); - expect(wrapper).toMatchSnapshot(); - }); - - test('should match snapshot with plugins in mobile', () => { - const props: ComponentProps = { - ...defaultProps, - mobile: true, - pluginMenuItems: [{ - id: 'plugin-id-1', - pluginId: 'plugin-1', - mobileIcon: , - action: jest.fn, - text: 'some text', - }, - { - id: 'plugind-id-2', - pluginId: 'plugin-2', - mobileIcon: , - action: jest.fn, - text: 'some text', - }, - ], - }; - const wrapper = getMainMenuWrapper(props); - expect(wrapper).toMatchSnapshot(); - }); - - test('should show leave team option when primary team is not set', () => { - const props = {...defaultProps, teamIsGroupConstrained: false, experimentalPrimaryTeam: undefined}; - const wrapper = getMainMenuWrapper(props); - - // show leave team option when experimentalPrimaryTeam is not set - expect(wrapper.find('#leaveTeam')).toHaveLength(1); - expect(wrapper.find('#leaveTeam').find(Menu.ItemToggleModalRedux).props().show).toEqual(true); - }); - - test('should hide leave team option when experimentalPrimaryTeam is same as current team', () => { - const props = {...defaultProps, teamIsGroupConstrained: false}; - const wrapper = getMainMenuWrapper(props); - expect(wrapper.find('#leaveTeam')).toHaveLength(1); - expect(wrapper.find('#leaveTeam').find(Menu.ItemToggleModalRedux).props().show).toEqual(true); - }); - - test('should hide leave team option when experimentalPrimaryTeam is same as current team', () => { - const props = {...defaultProps, teamIsGroupConstrained: false, experimentalPrimaryTeam: 'other-team'}; - const wrapper = getMainMenuWrapper(props); - expect(wrapper.find('#leaveTeam')).toHaveLength(1); - expect(wrapper.find('#leaveTeam').find(Menu.ItemToggleModalRedux).props().show).toEqual(true); - }); - - test('mobile view should hide the subscribe now button when does not have permissions', () => { - const noPermissionsState = {...defaultState}; - noPermissionsState.entities.roles.roles.system_manager.permissions = []; - const store = mockStore(noPermissionsState); - - const wrapper = mountWithIntl( - - - , - ); - - expect(wrapper.find('UpgradeLink')).toHaveLength(0); - }); - - test('mobile view should hide start trial menu item because user state does not have permission to write license', () => { - const store = mockStore(defaultState); - - const wrapper = mountWithIntl( - - - , - ); - - expect(wrapper.find('#startTrial')).toHaveLength(0); - }); - - test('should match snapshot with guest access disabled and no team invite permission', () => { - const props = { - ...defaultProps, - guestAccessEnabled: false, - canInviteTeamMember: false, - }; - const wrapper = getMainMenuWrapper(props); - expect(wrapper).toMatchSnapshot(); - }); - - test('should match snapshot with cloud free trial', () => { - const props = { - ...defaultProps, - isCloud: true, - isStarterFree: false, - isFreeTrial: true, - usageDeltaTeams: -1, - }; - const wrapper = getMainMenuWrapper(props); - expect(wrapper.find('#createTeam')).toMatchSnapshot(); - }); - - test('should match snapshot with cloud free and team limit reached', () => { - const props = { - ...defaultProps, - isCloud: true, - isStarterFree: true, - isFreeTrial: false, - usageDeltaTeams: 0, - }; - const wrapper = getMainMenuWrapper(props); - expect(wrapper.find('#createTeam')).toMatchSnapshot(); - }); -}); diff --git a/webapp/channels/src/components/main_menu/main_menu.tsx b/webapp/channels/src/components/main_menu/main_menu.tsx deleted file mode 100644 index f2a338ba79..0000000000 --- a/webapp/channels/src/components/main_menu/main_menu.tsx +++ /dev/null @@ -1,538 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import React from 'react'; -import {injectIntl} from 'react-intl'; -import type {IntlShape} from 'react-intl'; - -import type {UserProfile} from '@mattermost/types/users'; - -import {Permissions} from 'mattermost-redux/constants'; - -import * as GlobalActions from 'actions/global_actions'; -import {trackEvent} from 'actions/telemetry_actions'; - -import AboutBuildModal from 'components/about_build_modal'; -import AddGroupsToTeamModal from 'components/add_groups_to_team_modal'; -import InvitationModal from 'components/invitation_modal'; -import LeaveTeamModal from 'components/leave_team_modal'; -import SystemPermissionGate from 'components/permissions_gates/system_permission_gate'; -import TeamPermissionGate from 'components/permissions_gates/team_permission_gate'; -import TeamGroupsManageModal from 'components/team_groups_manage_modal'; -import TeamMembersModal from 'components/team_members_modal'; -import TeamSettingsModal from 'components/team_settings_modal'; -import UserSettingsModal from 'components/user_settings/modal'; -import LeaveTeamIcon from 'components/widgets/icons/leave_team_icon'; -import Menu from 'components/widgets/menu/menu'; -import RestrictedIndicator from 'components/widgets/menu/menu_items/restricted_indicator'; - -import {FREEMIUM_TO_ENTERPRISE_TRIAL_LENGTH_DAYS} from 'utils/cloud_utils'; -import {Constants, LicenseSkus, ModalIdentifiers, MattermostFeatures} from 'utils/constants'; -import {cmdOrCtrlPressed, isKeyPressed} from 'utils/keyboard'; -import {makeUrlSafe} from 'utils/url'; -import * as UserAgent from 'utils/user_agent'; - -import type {ModalData} from 'types/actions'; -import type {MainMenuAction} from 'types/store/plugins'; - -import LearnAboutTeamsLink from './learn_about_teams_link'; -import './main_menu.scss'; - -export type Props = { - mobile: boolean; - id?: string; - teamId?: string; - teamName?: string; - siteName?: string; - currentUser?: UserProfile; - appDownloadLink?: string; - enableCommands: boolean; - enableIncomingWebhooks: boolean; - enableOAuthServiceProvider: boolean; - enableOutgoingWebhooks: boolean; - canManageSystemBots: boolean; - canManageIntegrations: boolean; - experimentalPrimaryTeam?: string; - helpLink?: string; - reportAProblemLink?: string; - moreTeamsToJoin: boolean; - pluginMenuItems?: MainMenuAction[]; - isMentionSearch?: boolean; - teamIsGroupConstrained: boolean; - isLicensedForLDAPGroups?: boolean; - intl: IntlShape; - isCloud: boolean; - isStarterFree: boolean; - isFreeTrial: boolean; - usageDeltaTeams: number; - guestAccessEnabled: boolean; - canInviteTeamMember: boolean; - actions: { - openModal:

(modalData: ModalData

) => void; - showMentions: () => void; - showFlaggedPosts: () => void; - closeRightHandSide: () => void; - closeRhsMenu: () => void; - }; - -}; -export class MainMenu extends React.PureComponent { - static defaultProps = { - teamType: '', - mobile: false, - pluginMenuItems: [], - }; - - async componentDidMount(): Promise { - document.addEventListener('keydown', this.handleKeyDown); - } - - componentWillUnmount(): void { - document.removeEventListener('keydown', this.handleKeyDown); - } - - handleKeyDown = (e: KeyboardEvent): void => { - if (cmdOrCtrlPressed(e) && e.shiftKey && isKeyPressed(e, Constants.KeyCodes.A)) { - e.preventDefault(); - this.props.actions.openModal({modalId: ModalIdentifiers.USER_SETTINGS, dialogType: UserSettingsModal, dialogProps: {isContentProductSettings: true, focusOriginElement: 'userAccountMenuButton'}}); - } - }; - - handleEmitUserLoggedOutEvent = (): void => { - GlobalActions.emitUserLoggedOutEvent(); - }; - - getFlagged = (e: Event): void => { - e.preventDefault(); - this.props.actions.showFlaggedPosts(); - this.props.actions.closeRhsMenu(); - }; - - searchMentions = (e: Event): void => { - e.preventDefault(); - - if (this.props.isMentionSearch) { - this.props.actions.closeRightHandSide(); - } else { - this.props.actions.closeRhsMenu(); - this.props.actions.showMentions(); - } - }; - - render() { - const { - appDownloadLink, - currentUser, - teamIsGroupConstrained, - isLicensedForLDAPGroups, - teamId = '', - guestAccessEnabled, - canInviteTeamMember, - } = this.props; - - const safeAppDownloadLink = makeUrlSafe(appDownloadLink || ''); - - if (!currentUser) { - return null; - } - - const pluginItems = this.props.pluginMenuItems?.map((item) => ( - { - if (item.action) { - item.action(); - } - }} - text={item.text} - icon={this.props.mobile && item.mobileIcon} - /> - )); - - const someIntegrationEnabled = this.props.enableIncomingWebhooks || this.props.enableOutgoingWebhooks || this.props.enableCommands || this.props.enableOAuthServiceProvider || this.props.canManageSystemBots; - const showIntegrations = !this.props.mobile && someIntegrationEnabled && this.props.canManageIntegrations; - const teamsLimitReached = this.props.isStarterFree && !this.props.isFreeTrial && this.props.usageDeltaTeams >= 0; - const createTeamRestricted = this.props.isCloud && (this.props.isFreeTrial || teamsLimitReached); - - const {formatMessage} = this.props.intl; - - let invitePeopleModal = null; - if (guestAccessEnabled || canInviteTeamMember) { - invitePeopleModal = ( - } - onClick={() => trackEvent('ui', 'click_sidebar_team_dropdown_invite_people')} - dialogProps={{focusOriginElement: 'sidebarDropdownMenuButton'}} - /> - ); - } - - return this.props.mobile ? ( -

- - - - - - - - - - - - {'@'}} - text={formatMessage({id: 'sidebar_right_menu.recentMentions', defaultMessage: 'Recent Mentions'})} - /> - } - text={formatMessage({id: 'sidebar_right_menu.flagged', defaultMessage: 'Saved messages'})} - /> - - - } - /> - } - /> - - - - } - /> - - - {invitePeopleModal} - - - - - } - /> - - - } - /> - - - } - /> - - - } - /> - - - - - } - /> - - } - /> - } - /> - - - {pluginItems} - - - - - - } - /> - } - /> - } - /> - } - /> - - - } - /> - - - ) : ( - - - - - - - {invitePeopleModal} - - - - - - - - - - - - - - - - - - - - )} - /> - - -
- -
-
-
- - {pluginItems} - -
- ); - } -} - -export default injectIntl(MainMenu); diff --git a/webapp/channels/src/components/menu/menu.scss b/webapp/channels/src/components/menu/menu.scss index 6ce7ca7813..905955e42d 100644 --- a/webapp/channels/src/components/menu/menu.scss +++ b/webapp/channels/src/components/menu/menu.scss @@ -10,6 +10,7 @@ padding: 4px 0; background-color: var(--center-channel-bg); box-shadow: var(--elevation-4), 0 0 0 1px rgba(var(--center-channel-color-rgb), 0.12) inset; + margin-block-start: 4px; } &.AsSubMenu { pointer-events: none; diff --git a/webapp/channels/src/components/channel_header_mobile/channel_info_button/__snapshots__/channel_info_button.test.tsx.snap b/webapp/channels/src/components/mobile_channel_header/channel_info_button/__snapshots__/channel_info_button.test.tsx.snap similarity index 100% rename from webapp/channels/src/components/channel_header_mobile/channel_info_button/__snapshots__/channel_info_button.test.tsx.snap rename to webapp/channels/src/components/mobile_channel_header/channel_info_button/__snapshots__/channel_info_button.test.tsx.snap diff --git a/webapp/channels/src/components/channel_header_mobile/channel_info_button/channel_info_button.test.tsx b/webapp/channels/src/components/mobile_channel_header/channel_info_button/channel_info_button.test.tsx similarity index 100% rename from webapp/channels/src/components/channel_header_mobile/channel_info_button/channel_info_button.test.tsx rename to webapp/channels/src/components/mobile_channel_header/channel_info_button/channel_info_button.test.tsx diff --git a/webapp/channels/src/components/channel_header_mobile/channel_info_button/channel_info_button.tsx b/webapp/channels/src/components/mobile_channel_header/channel_info_button/channel_info_button.tsx similarity index 100% rename from webapp/channels/src/components/channel_header_mobile/channel_info_button/channel_info_button.tsx rename to webapp/channels/src/components/mobile_channel_header/channel_info_button/channel_info_button.tsx diff --git a/webapp/channels/src/components/channel_header_mobile/channel_info_button/index.ts b/webapp/channels/src/components/mobile_channel_header/channel_info_button/index.ts similarity index 100% rename from webapp/channels/src/components/channel_header_mobile/channel_info_button/index.ts rename to webapp/channels/src/components/mobile_channel_header/channel_info_button/index.ts diff --git a/webapp/channels/src/components/channel_header_mobile/collapse_lhs_button/collapse_lhs_button.tsx b/webapp/channels/src/components/mobile_channel_header/collapse_lhs_button/collapse_lhs_button.tsx similarity index 100% rename from webapp/channels/src/components/channel_header_mobile/collapse_lhs_button/collapse_lhs_button.tsx rename to webapp/channels/src/components/mobile_channel_header/collapse_lhs_button/collapse_lhs_button.tsx diff --git a/webapp/channels/src/components/channel_header_mobile/collapse_lhs_button/index.ts b/webapp/channels/src/components/mobile_channel_header/collapse_lhs_button/index.ts similarity index 100% rename from webapp/channels/src/components/channel_header_mobile/collapse_lhs_button/index.ts rename to webapp/channels/src/components/mobile_channel_header/collapse_lhs_button/index.ts diff --git a/webapp/channels/src/components/channel_header_mobile/collapse_rhs_button/collapse_rhs_button.tsx b/webapp/channels/src/components/mobile_channel_header/collapse_rhs_button/collapse_rhs_button.tsx similarity index 100% rename from webapp/channels/src/components/channel_header_mobile/collapse_rhs_button/collapse_rhs_button.tsx rename to webapp/channels/src/components/mobile_channel_header/collapse_rhs_button/collapse_rhs_button.tsx diff --git a/webapp/channels/src/components/channel_header_mobile/collapse_rhs_button/index.ts b/webapp/channels/src/components/mobile_channel_header/collapse_rhs_button/index.ts similarity index 100% rename from webapp/channels/src/components/channel_header_mobile/collapse_rhs_button/index.ts rename to webapp/channels/src/components/mobile_channel_header/collapse_rhs_button/index.ts diff --git a/webapp/channels/src/components/channel_header_mobile/index.ts b/webapp/channels/src/components/mobile_channel_header/index.ts similarity index 95% rename from webapp/channels/src/components/channel_header_mobile/index.ts rename to webapp/channels/src/components/mobile_channel_header/index.ts index 9b177d734e..6a3342335a 100644 --- a/webapp/channels/src/components/channel_header_mobile/index.ts +++ b/webapp/channels/src/components/mobile_channel_header/index.ts @@ -24,7 +24,7 @@ import {getIsMobileView} from 'selectors/views/browser'; import type {GlobalState} from 'types/store'; -import ChannelHeaderMobile from './channel_header_mobile'; +import MobileChannelHeader from './mobile_channel_header'; const isCurrentChannelMuted = createSelector( 'isCurrentChannelMuted', @@ -53,4 +53,4 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ }, dispatch), }); -export default withRouter(connect(mapStateToProps, mapDispatchToProps)(ChannelHeaderMobile)); +export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MobileChannelHeader)); diff --git a/webapp/channels/src/components/channel_header_mobile/channel_header_mobile.test.tsx b/webapp/channels/src/components/mobile_channel_header/mobile_channel_header.test.tsx similarity index 98% rename from webapp/channels/src/components/channel_header_mobile/channel_header_mobile.test.tsx rename to webapp/channels/src/components/mobile_channel_header/mobile_channel_header.test.tsx index 5d0d2927ba..7d2bd40259 100644 --- a/webapp/channels/src/components/channel_header_mobile/channel_header_mobile.test.tsx +++ b/webapp/channels/src/components/mobile_channel_header/mobile_channel_header.test.tsx @@ -6,7 +6,7 @@ import React from 'react'; import {renderWithContext, screen} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; -import ChannelHeaderMobile from './channel_header_mobile'; +import ChannelHeaderMobile from './mobile_channel_header'; describe('components/ChannelHeaderMobile/ChannelHeaderMobile', () => { global.document.querySelector = jest.fn().mockReturnValue({ diff --git a/webapp/channels/src/components/channel_header_mobile/channel_header_mobile.tsx b/webapp/channels/src/components/mobile_channel_header/mobile_channel_header.tsx similarity index 98% rename from webapp/channels/src/components/channel_header_mobile/channel_header_mobile.tsx rename to webapp/channels/src/components/mobile_channel_header/mobile_channel_header.tsx index 0f647f0a38..01366d8b92 100644 --- a/webapp/channels/src/components/channel_header_mobile/channel_header_mobile.tsx +++ b/webapp/channels/src/components/mobile_channel_header/mobile_channel_header.tsx @@ -33,7 +33,7 @@ type Props = { }; } -export default class ChannelHeaderMobile extends React.PureComponent { +export default class MobileChannelHeader extends React.PureComponent { componentDidMount() { document.querySelector('.inner-wrap')?.addEventListener('click', this.hideSidebars); } diff --git a/webapp/channels/src/components/channel_header_mobile/show_search_button/index.ts b/webapp/channels/src/components/mobile_channel_header/show_search_button/index.ts similarity index 100% rename from webapp/channels/src/components/channel_header_mobile/show_search_button/index.ts rename to webapp/channels/src/components/mobile_channel_header/show_search_button/index.ts diff --git a/webapp/channels/src/components/channel_header_mobile/show_search_button/show_search_button.tsx b/webapp/channels/src/components/mobile_channel_header/show_search_button/show_search_button.tsx similarity index 100% rename from webapp/channels/src/components/channel_header_mobile/show_search_button/show_search_button.tsx rename to webapp/channels/src/components/mobile_channel_header/show_search_button/show_search_button.tsx diff --git a/webapp/channels/src/components/channel_header_mobile/unmute_channel_button/__snapshots__/unmute_channel_button.test.tsx.snap b/webapp/channels/src/components/mobile_channel_header/unmute_channel_button/__snapshots__/unmute_channel_button.test.tsx.snap similarity index 100% rename from webapp/channels/src/components/channel_header_mobile/unmute_channel_button/__snapshots__/unmute_channel_button.test.tsx.snap rename to webapp/channels/src/components/mobile_channel_header/unmute_channel_button/__snapshots__/unmute_channel_button.test.tsx.snap diff --git a/webapp/channels/src/components/channel_header_mobile/unmute_channel_button/index.ts b/webapp/channels/src/components/mobile_channel_header/unmute_channel_button/index.ts similarity index 100% rename from webapp/channels/src/components/channel_header_mobile/unmute_channel_button/index.ts rename to webapp/channels/src/components/mobile_channel_header/unmute_channel_button/index.ts diff --git a/webapp/channels/src/components/channel_header_mobile/unmute_channel_button/unmute_channel_button.test.tsx b/webapp/channels/src/components/mobile_channel_header/unmute_channel_button/unmute_channel_button.test.tsx similarity index 100% rename from webapp/channels/src/components/channel_header_mobile/unmute_channel_button/unmute_channel_button.test.tsx rename to webapp/channels/src/components/mobile_channel_header/unmute_channel_button/unmute_channel_button.test.tsx diff --git a/webapp/channels/src/components/channel_header_mobile/unmute_channel_button/unmute_channel_button.tsx b/webapp/channels/src/components/mobile_channel_header/unmute_channel_button/unmute_channel_button.tsx similarity index 100% rename from webapp/channels/src/components/channel_header_mobile/unmute_channel_button/unmute_channel_button.tsx rename to webapp/channels/src/components/mobile_channel_header/unmute_channel_button/unmute_channel_button.tsx diff --git a/webapp/channels/src/components/mobile_sidebar_right/index.ts b/webapp/channels/src/components/mobile_sidebar_right/index.ts new file mode 100644 index 0000000000..16f68edcca --- /dev/null +++ b/webapp/channels/src/components/mobile_sidebar_right/index.ts @@ -0,0 +1,26 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import type {ConnectedProps} from 'react-redux'; +import {connect} from 'react-redux'; + +import {getCurrentUser} from 'mattermost-redux/selectors/entities/users'; + +import {getIsRhsMenuOpen} from 'selectors/rhs'; + +import type {GlobalState} from 'types/store'; + +import MobileSidebarRight from './mobile_sidebar_right'; + +function mapStateToProps(state: GlobalState) { + return { + currentUser: getCurrentUser(state), + isOpen: getIsRhsMenuOpen(state), + }; +} + +const connector = connect(mapStateToProps); + +export type PropsFromRedux = ConnectedProps; + +export default connect(mapStateToProps)(MobileSidebarRight); diff --git a/webapp/channels/src/sass/layout/_sidebar-menu.scss b/webapp/channels/src/components/mobile_sidebar_right/mobile_sidebar_right.scss similarity index 58% rename from webapp/channels/src/sass/layout/_sidebar-menu.scss rename to webapp/channels/src/components/mobile_sidebar_right/mobile_sidebar_right.scss index eb2dc2ced8..86a52f0911 100644 --- a/webapp/channels/src/sass/layout/_sidebar-menu.scss +++ b/webapp/channels/src/components/mobile_sidebar_right/mobile_sidebar_right.scss @@ -4,15 +4,29 @@ .sidebar--menu { position: absolute; right: 0; - display: none; overflow: auto; - width: 220px; + width: 290px; height: 100%; padding: 0 0 2em; border-right: variables.$border-gray; background: var(--sidebar-bg); color: rgba(var(--sidebar-text-rgb), 0.8); -webkit-overflow-scrolling: touch; + transform: translate3d(290px, 0, 0); + transition: transform 0.3s linear; + + &.move--left { + transform: translate3d(0, 0, 0); + } + + .mentions { + font-size: 17px; + font-weight: bold; + } + + .Menu .MenuItem.MenuItem--with-icon .MenuItem__help-text { + padding-left: 0; + } .icon { display: inline-block; @@ -31,53 +45,6 @@ padding-top: 5px; } - .team__header { - @include mixins.pie-clearfix; - - display: none; - padding: 0 15px; - background: var(--sidebar-header-bg); - - a { - color: variables.$white; - } - - .navbar-right { - margin: 16px -5px 0; - font-size: 0.85em; - - .dropdown-toggle { - padding: 0 10px; - } - - .dropdown-menu { - li a { - padding: 3 20px; - color: #555; - } - } - - .dropdown__icon { - display: inline-block; - width: 4px; - height: 16px; - background-size: 100% 100%; - } - } - - .team__name { - overflow: hidden; - max-width: 80%; - float: left; - font-size: 1.2em; - font-weight: 600; - line-height: 50px; - text-decoration: none; - text-overflow: ellipsis; - white-space: nowrap; - } - } - .nav { > li { > a, diff --git a/webapp/channels/src/components/mobile_sidebar_right/mobile_sidebar_right.tsx b/webapp/channels/src/components/mobile_sidebar_right/mobile_sidebar_right.tsx new file mode 100644 index 0000000000..52bbd6f4c7 --- /dev/null +++ b/webapp/channels/src/components/mobile_sidebar_right/mobile_sidebar_right.tsx @@ -0,0 +1,52 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import classNames from 'classnames'; +import React, {memo} from 'react'; +import {CSSTransition} from 'react-transition-group'; + +import useGetUsageDeltas from 'components/common/hooks/useGetUsageDeltas'; + +import MobileRightDrawerItems from './mobile_sidebar_right_items'; + +import type {PropsFromRedux} from './index'; +import './mobile_sidebar_right.scss'; + +const TRANSITION_TIMEOUT = 300; // in ms + +type Props = PropsFromRedux; + +const MobileRightDrawer = ({ + isOpen, + currentUser, +}: Props) => { + const usageDeltas = useGetUsageDeltas(); + + if (!currentUser) { + return null; + } + + return ( + + ); +}; + +export default memo(MobileRightDrawer); diff --git a/webapp/channels/src/components/main_menu/index.tsx b/webapp/channels/src/components/mobile_sidebar_right/mobile_sidebar_right_items/index.tsx similarity index 60% rename from webapp/channels/src/components/main_menu/index.tsx rename to webapp/channels/src/components/mobile_sidebar_right/mobile_sidebar_right_items/index.tsx index 3236a53bab..cd66511dc8 100644 --- a/webapp/channels/src/components/main_menu/index.tsx +++ b/webapp/channels/src/components/mobile_sidebar_right/mobile_sidebar_right_items/index.tsx @@ -1,25 +1,21 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import type {ConnectedProps} from 'react-redux'; import {connect} from 'react-redux'; -import {withRouter} from 'react-router-dom'; import {bindActionCreators} from 'redux'; import type {Dispatch} from 'redux'; -import {Permissions} from 'mattermost-redux/constants'; import {getCloudSubscription as selectCloudSubscription, getSubscriptionProduct} from 'mattermost-redux/selectors/entities/cloud'; import { getConfig, getLicense, } from 'mattermost-redux/selectors/entities/general'; -import {haveICurrentTeamPermission, haveISystemPermission} from 'mattermost-redux/selectors/entities/roles'; import { getJoinableTeamIds, getCurrentTeam, } from 'mattermost-redux/selectors/entities/teams'; -import {getCurrentUser} from 'mattermost-redux/selectors/entities/users'; -import {openModal} from 'actions/views/modals'; import {showMentions, showFlaggedPosts, closeRightHandSide, closeMenu as closeRhsMenu} from 'actions/views/rhs'; import {getRhsState} from 'selectors/rhs'; @@ -28,28 +24,18 @@ import {isCloudLicense} from 'utils/license_utils'; import type {GlobalState} from 'types/store'; -import MainMenu from './main_menu'; +import MobileSidebarRightItems from './mobile_sidebar_right_items'; function mapStateToProps(state: GlobalState) { const config = getConfig(state); const currentTeam = getCurrentTeam(state); - const currentUser = getCurrentUser(state); const appDownloadLink = config.AppDownloadLink; - const enableCommands = config.EnableCommands === 'true'; const siteName = config.SiteName; - const enableIncomingWebhooks = config.EnableIncomingWebhooks === 'true'; - const enableOAuthServiceProvider = config.EnableOAuthServiceProvider === 'true'; - const enableOutgoingWebhooks = config.EnableOutgoingWebhooks === 'true'; const experimentalPrimaryTeam = config.ExperimentalPrimaryTeam; const helpLink = config.HelpLink; const reportAProblemLink = config.ReportAProblemLink; - const canManageTeamIntegrations = (haveICurrentTeamPermission(state, Permissions.MANAGE_SLASH_COMMANDS) || haveICurrentTeamPermission(state, Permissions.MANAGE_OAUTH) || haveICurrentTeamPermission(state, Permissions.MANAGE_INCOMING_WEBHOOKS) || haveICurrentTeamPermission(state, Permissions.MANAGE_OUTGOING_WEBHOOKS)); - const canManageSystemBots = (haveISystemPermission(state, {permission: Permissions.MANAGE_BOTS}) || haveISystemPermission(state, {permission: Permissions.MANAGE_OTHERS_BOTS})); - const canManageIntegrations = canManageTeamIntegrations || canManageSystemBots; - const canInviteTeamMember = haveICurrentTeamPermission(state, Permissions.ADD_USER_TO_TEAM); - const joinableTeams = getJoinableTeamIds(state); const moreTeamsToJoin = joinableTeams && joinableTeams.length > 0; const rhsState = getRhsState(state); @@ -64,12 +50,6 @@ function mapStateToProps(state: GlobalState) { return { appDownloadLink, - enableCommands, - canManageIntegrations, - enableIncomingWebhooks, - enableOAuthServiceProvider, - enableOutgoingWebhooks, - canManageSystemBots, experimentalPrimaryTeam, helpLink, reportAProblemLink, @@ -78,13 +58,10 @@ function mapStateToProps(state: GlobalState) { siteName, teamId: currentTeam?.id, teamName: currentTeam?.name, - currentUser, isMentionSearch: rhsState === RHSStates.MENTION, teamIsGroupConstrained: Boolean(currentTeam?.group_constrained), isLicensedForLDAPGroups: state.entities.general.license.LDAPGroups === 'true', guestAccessEnabled: config.EnableGuestAccounts === 'true', - canInviteTeamMember, - isCloud, isStarterFree, isFreeTrial, }; @@ -93,7 +70,6 @@ function mapStateToProps(state: GlobalState) { function mapDispatchToProps(dispatch: Dispatch) { return { actions: bindActionCreators({ - openModal, showMentions, showFlaggedPosts, closeRightHandSide, @@ -102,4 +78,8 @@ function mapDispatchToProps(dispatch: Dispatch) { }; } -export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MainMenu)); +const connector = connect(mapStateToProps, mapDispatchToProps); + +export type PropsFromRedux = ConnectedProps; + +export default connector(MobileSidebarRightItems); diff --git a/webapp/channels/src/components/mobile_sidebar_right/mobile_sidebar_right_items/mobile_sidebar_right_items.test.tsx b/webapp/channels/src/components/mobile_sidebar_right/mobile_sidebar_right_items/mobile_sidebar_right_items.test.tsx new file mode 100644 index 0000000000..1cd472efea --- /dev/null +++ b/webapp/channels/src/components/mobile_sidebar_right/mobile_sidebar_right_items/mobile_sidebar_right_items.test.tsx @@ -0,0 +1,164 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; + +import {Permissions} from 'mattermost-redux/constants'; + +import type {MockIntl} from 'tests/helpers/intl-test-helper'; +import {renderWithContext, screen} from 'tests/react_testing_utils'; + +import {MobileSidebarRightItems} from './mobile_sidebar_right_items'; +import type {Props} from './mobile_sidebar_right_items'; + +describe('MobileSidebarRightItems', () => { + const defaultProps: Props = { + teamId: 'team-id', + teamName: 'team_name', + appDownloadLink: undefined, + experimentalPrimaryTeam: undefined, + helpLink: undefined, + reportAProblemLink: undefined, + moreTeamsToJoin: false, + pluginMenuItems: [], + isMentionSearch: false, + usageDeltaTeams: 0, + siteName: 'site-name', + isLicensedForLDAPGroups: false, + guestAccessEnabled: true, + actions: { + showMentions: jest.fn(), + showFlaggedPosts: jest.fn(), + closeRightHandSide: jest.fn(), + closeRhsMenu: jest.fn(), + }, + teamIsGroupConstrained: false, + isStarterFree: false, + isFreeTrial: false, + intl: { + formatMessage: ({defaultMessage}) => defaultMessage, + } as MockIntl, + }; + + const defaultState = { + entities: { + channels: { + myMembers: {}, + }, + general: { + config: {}, + license: { + Cloud: 'false', + }, + }, + teams: { + currentTeamId: 'team-id', + myMembers: { + 'team-id': { + team_id: 'team-id', + user_id: 'test-user-id', + roles: 'team_user', + scheme_user: true, + }, + }, + }, + users: { + currentUserId: 'test-user-id', + profiles: { + 'test-user-id': { + id: 'test-user-id', + roles: 'system_user system_admin', + }, + }, + }, + roles: { + roles: { + system_admin: { + permissions: [ + Permissions.CREATE_TEAM, + Permissions.SYSCONSOLE_WRITE_PLUGINS, + ], + }, + }, + }, + }, + }; + + test('should render basic menu items', () => { + renderWithContext(, defaultState); + expect(screen.getByText('Recent Mentions')).toBeInTheDocument(); + expect(screen.getByText('Saved messages')).toBeInTheDocument(); + expect(screen.getByText('Profile')).toBeInTheDocument(); + expect(screen.getByText('Settings')).toBeInTheDocument(); + }); + + test('should show leave team option when primary team is not set', () => { + renderWithContext( + , + defaultState, + ); + expect(screen.getByText('Leave Team')).toBeInTheDocument(); + }); + + test('should hide leave team option when team is group constrained', () => { + renderWithContext( + , + defaultState, + ); + expect(screen.queryByText('Leave Team')).not.toBeInTheDocument(); + }); + + test('should show create team option with proper permissions', () => { + renderWithContext(, defaultState); + expect(screen.getByText('Create a Team')).toBeInTheDocument(); + }); + + test('should show plugins when provided', () => { + const pluginMenuItems = [ + { + id: 'plugin-1', + pluginId: 'plugin-1', + mobileIcon: , + action: jest.fn(), + text: 'Plugin Item 1', + }, + ]; + renderWithContext( + , + defaultState, + ); + expect(screen.getByText('Plugin Item 1')).toBeInTheDocument(); + }); + + test('should show help link when provided', () => { + renderWithContext( + , + defaultState, + ); + expect(screen.getByText('Help')).toBeInTheDocument(); + }); + + test('should show report link when provided', () => { + renderWithContext( + , + defaultState, + ); + expect(screen.getByText('Report a Problem')).toBeInTheDocument(); + }); +}); diff --git a/webapp/channels/src/components/mobile_sidebar_right/mobile_sidebar_right_items/mobile_sidebar_right_items.tsx b/webapp/channels/src/components/mobile_sidebar_right/mobile_sidebar_right_items/mobile_sidebar_right_items.tsx new file mode 100644 index 0000000000..815d177319 --- /dev/null +++ b/webapp/channels/src/components/mobile_sidebar_right/mobile_sidebar_right_items/mobile_sidebar_right_items.tsx @@ -0,0 +1,299 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {injectIntl} from 'react-intl'; +import type {WrappedComponentProps} from 'react-intl'; + +import {Permissions} from 'mattermost-redux/constants'; + +import {emitUserLoggedOutEvent} from 'actions/global_actions'; +import {trackEvent} from 'actions/telemetry_actions'; + +import AboutBuildModal from 'components/about_build_modal'; +import AddGroupsToTeamModal from 'components/add_groups_to_team_modal'; +import InvitationModal from 'components/invitation_modal'; +import LeaveTeamModal from 'components/leave_team_modal'; +import SystemPermissionGate from 'components/permissions_gates/system_permission_gate'; +import TeamPermissionGate from 'components/permissions_gates/team_permission_gate'; +import TeamGroupsManageModal from 'components/team_groups_manage_modal'; +import TeamMembersModal from 'components/team_members_modal'; +import TeamSettingsModal from 'components/team_settings_modal'; +import UserSettingsModal from 'components/user_settings/modal'; +import LeaveTeamIcon from 'components/widgets/icons/leave_team_icon'; +import Menu from 'components/widgets/menu/menu'; + +import {ModalIdentifiers} from 'utils/constants'; +import {makeUrlSafe} from 'utils/url'; + +import type {PropsFromRedux} from './index'; + +export interface Props extends PropsFromRedux, WrappedComponentProps { + usageDeltaTeams: number; +} + +export class MobileSidebarRightItems extends React.PureComponent { + static defaultProps = { + pluginMenuItems: [], + }; + + onRecentMentionItemClick = (e: Event): void => { + e.preventDefault(); + + if (this.props.isMentionSearch) { + this.props.actions.closeRightHandSide(); + } else { + this.props.actions.closeRhsMenu(); + this.props.actions.showMentions(); + } + }; + + onShowFlaggedPostItemClick = (e: Event): void => { + e.preventDefault(); + this.props.actions.showFlaggedPosts(); + this.props.actions.closeRhsMenu(); + }; + + onLogoutItemClick = (): void => { + emitUserLoggedOutEvent(); + }; + + render() { + const {formatMessage} = this.props.intl; + + const safeAppDownloadLink = makeUrlSafe(this.props.appDownloadLink || ''); + const teamsLimitReached = this.props.isStarterFree && !this.props.isFreeTrial && this.props.usageDeltaTeams >= 0; + + const pluginItems = this.props.pluginMenuItems.map((item) => ( + { + if (item.action) { + item.action(); + } + }} + text={item.text} + icon={item.mobileIcon} + /> + )); + + return ( + + + + + + + + + + + + + {'@'}} + text={formatMessage({id: 'sidebar_right_menu.recentMentions', defaultMessage: 'Recent Mentions'})} + /> + } + text={formatMessage({id: 'sidebar_right_menu.flagged', defaultMessage: 'Saved messages'})} + /> + + + } + /> + } + /> + + + + } + /> + + {this.props.guestAccessEnabled && ( + + } + onClick={() => trackEvent('ui', 'click_sidebar_team_dropdown_invite_people')} + /> + + )} + + + + } + /> + + + } + /> + + + } + /> + + + } + /> + + + + + } + /> + + } + /> + } + /> + + + {pluginItems} + + + } + /> + } + /> + } + /> + } + /> + + + } + /> + + + ); + } +} + +export default injectIntl(MobileSidebarRightItems); diff --git a/webapp/channels/src/components/more_direct_channels/more_direct_channels.tsx b/webapp/channels/src/components/more_direct_channels/more_direct_channels.tsx index 7271348749..0afc27c8b0 100644 --- a/webapp/channels/src/components/more_direct_channels/more_direct_channels.tsx +++ b/webapp/channels/src/components/more_direct_channels/more_direct_channels.tsx @@ -164,7 +164,7 @@ export default class MoreDirectChannels extends React.PureComponent { focusElement(this.props.focusOriginElement, true); }, 0); 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 4ede9f1b21..89c58234c1 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 @@ -10,10 +10,12 @@ import {TourTip, useMeasurePunchouts} from '@mattermost/components'; import {setShowOnboardingCompleteProfileTour} from 'actions/views/onboarding_tasks'; import {isShowOnboardingCompleteProfileTour} from 'selectors/views/onboarding_tasks'; +import {ELEMENT_ID_FOR_USER_ACCOUNT_MENU} from 'components/user_account_menu/user_account_menu'; + import {OnboardingTasksName, TaskNameMapToSteps} from './constants'; import {useHandleOnBoardingTaskData} from './onboarding_tasks_manager'; -const translate = {x: 1, y: -9}; +const translate = {x: 1.5, y: -9}; export const CompleteYourProfileTour = () => { const dispatch = useDispatch(); @@ -43,7 +45,7 @@ export const CompleteYourProfileTour = () => {

); - const overlayPunchOut = useMeasurePunchouts(['userAccountMenu'], [], {x: -2.5, y: -2.5, width: 5, height: 5}); + const overlayPunchOut = useMeasurePunchouts([ELEMENT_ID_FOR_USER_ACCOUNT_MENU], [], {x: -2.5, y: -2.5, width: 5, height: 5}); const onDismiss = (e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); diff --git a/webapp/channels/src/components/root/root.tsx b/webapp/channels/src/components/root/root.tsx index be9d4cefc3..150b3ee301 100644 --- a/webapp/channels/src/components/root/root.tsx +++ b/webapp/channels/src/components/root/root.tsx @@ -26,7 +26,6 @@ import LoggedIn from 'components/logged_in'; import LoggedInRoute from 'components/logged_in_route'; import {LAUNCHING_WORKSPACE_FULLSCREEN_Z_INDEX} from 'components/preparing_workspace/launching_workspace'; import {Animations} from 'components/preparing_workspace/steps'; -import SidebarMobileRightMenu from 'components/sidebar_mobile_right_menu'; import webSocketClient from 'client/web_websocket_client'; import {initializePlugins} from 'plugins'; @@ -584,7 +583,6 @@ export default class Root extends React.PureComponent { - diff --git a/webapp/channels/src/components/select_team/__snapshots__/select_team.test.tsx.snap b/webapp/channels/src/components/select_team/__snapshots__/select_team.test.tsx.snap index f2465ed41d..e60263d541 100644 --- a/webapp/channels/src/components/select_team/__snapshots__/select_team.test.tsx.snap +++ b/webapp/channels/src/components/select_team/__snapshots__/select_team.test.tsx.snap @@ -33,7 +33,20 @@ exports[`components/select_team/SelectTeam should match snapshot 1`] = ` id="signup_team.join_open" /> - + + + + - + + + + - + + + + - + + + + h4 { + > h4 { margin: 0; span { @@ -15,4 +15,39 @@ line-height: 28px; } } + + a.LearnAboutTeamsLink { + display: flex; + align-items: center; + padding: 6px 18px; + column-gap: 10px; + + &:hover, + &:focus, + &:active { + text-decoration: none; + } + + > i { + display: inline-flex; + padding: 3px; + border-radius: 50%; + background-color: rgba(var(--denim-button-bg-rgb), 0.08); + font-size: 14px; + + &::before { + margin: 0; + } + } + + span { + font-size: 12px; + font-weight: 600; + line-height: 16px; + + &:hover { + text-decoration: underline; + } + } + } } diff --git a/webapp/channels/src/components/select_team/select_team.tsx b/webapp/channels/src/components/select_team/select_team.tsx index 877273c44b..7282509878 100644 --- a/webapp/channels/src/components/select_team/select_team.tsx +++ b/webapp/channels/src/components/select_team/select_team.tsx @@ -18,8 +18,8 @@ import AnnouncementBar from 'components/announcement_bar'; import BackButton from 'components/common/back_button'; import InfiniteScroll from 'components/common/infinite_scroll'; import SiteNameAndDescription from 'components/common/site_name_and_description'; +import ExternalLink from 'components/external_link'; import LoadingScreen from 'components/loading_screen'; -import LearnAboutTeamsLink from 'components/main_menu/learn_about_teams_link'; import SystemPermissionGate from 'components/permissions_gates/system_permission_gate'; import LogoutIcon from 'components/widgets/icons/fa_logout_icon'; @@ -28,10 +28,12 @@ import Constants from 'utils/constants'; import * as UserAgent from 'utils/user_agent'; import SelectTeamItem from './components/select_team_item'; + import './select_team.scss'; export const TEAMS_PER_PAGE = 30; const TEAM_MEMBERSHIP_DENIAL_ERROR_ID = 'api.team.add_members.user_denied'; +const MATTERMOST_ACADEMY_TEAM_TRAINING_LINK = 'https://mattermost.com/pl/mattermost-academy-team-training'; type Actions = { getTeams: (page?: number, perPage?: number, includeTotalCount?: boolean) => any; @@ -289,7 +291,20 @@ export default class SelectTeam extends React.PureComponent { defaultMessage='Teams you can join: ' /> - + + + + `; - -exports[`components/sidebar should match snapshot 1`] = ` - - -
- -
-
- -
- - -
-`; - -exports[`components/sidebar should match snapshot when direct channels modal is open 1`] = ` - - -
- -
-
- -
- - - -
-`; - -exports[`components/sidebar should match snapshot when more channels modal is open 1`] = ` - - -
- -
-
- -
- - -
-`; diff --git a/webapp/channels/src/components/sidebar/mobile_sidebar_header/contents/contents.tsx b/webapp/channels/src/components/sidebar/mobile_sidebar_header/contents/contents.tsx deleted file mode 100644 index cf1b9261b6..0000000000 --- a/webapp/channels/src/components/sidebar/mobile_sidebar_header/contents/contents.tsx +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import React from 'react'; -import styled from 'styled-components'; - -import type {UserProfile} from '@mattermost/types/users'; - -import CustomStatusEmoji from 'components/custom_status/custom_status_emoji'; -import CustomStatusModal from 'components/custom_status/custom_status_modal'; -import WithTooltip from 'components/with_tooltip'; - -import {ModalIdentifiers} from 'utils/constants'; - -import type {ModalData} from 'types/actions'; - -const HeaderLine = styled.div` - display: flex; - padding: 2px 16px 0 0; - flex-grow: 1; - user-select: none; - color: var(--sidebar-header-text-color); -`; - -const VerticalStack = styled.div` - display: flex; - flex-direction: column; - flex-grow: 1; -`; - -type Props = { - teamDescription?: string; - teamId?: string; - currentUser: UserProfile; - teamDisplayName?: string; - actions: Actions; -}; - -type Actions = { - openModal:

(modalData: ModalData

) => void; -}; - -export default class Contents extends React.PureComponent { - handleCustomStatusEmojiClick = (event: React.MouseEvent) => { - event.stopPropagation(); - const customStatusInputModalData = { - modalId: ModalIdentifiers.CUSTOM_STATUS, - dialogType: CustomStatusModal, - }; - this.props.actions.openModal(customStatusInputModalData); - }; - - render() { - if (!this.props.currentUser || !this.props.teamId) { - return null; - } - - let teamNameWithToolTip = ( -

- {this.props.teamDisplayName} -

- ); - - if (this.props.teamDescription) { - teamNameWithToolTip = ( - - {teamNameWithToolTip} - - ); - } - - return ( -
- - - {teamNameWithToolTip} -
-
- {'@' + this.props.currentUser.username} -
- void} - /> -
-
-
-
- ); - } -} diff --git a/webapp/channels/src/components/sidebar/mobile_sidebar_header/contents/index.ts b/webapp/channels/src/components/sidebar/mobile_sidebar_header/contents/index.ts deleted file mode 100644 index 7fa4eca19b..0000000000 --- a/webapp/channels/src/components/sidebar/mobile_sidebar_header/contents/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import {connect} from 'react-redux'; -import {bindActionCreators} from 'redux'; -import type {Dispatch} from 'redux'; - -import {getCurrentTeam} from 'mattermost-redux/selectors/entities/teams'; -import {getCurrentUser} from 'mattermost-redux/selectors/entities/users'; - -import {openModal} from 'actions/views/modals'; - -import type {GlobalState} from 'types/store'; - -import Contents from './contents'; - -function mapStateToProps(state: GlobalState) { - const currentTeam = getCurrentTeam(state); - const currentUser = getCurrentUser(state); - - return { - currentUser, - teamDescription: currentTeam?.description, - teamDisplayName: currentTeam?.display_name, - teamId: currentTeam?.id, - }; -} - -function mapDispatchToProps(dispatch: Dispatch) { - return { - actions: bindActionCreators({ - openModal, - }, dispatch), - }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(Contents); diff --git a/webapp/channels/src/components/sidebar/mobile_sidebar_header/index.ts b/webapp/channels/src/components/sidebar/mobile_sidebar_header/index.ts index cba11e4ba4..d273a91b88 100644 --- a/webapp/channels/src/components/sidebar/mobile_sidebar_header/index.ts +++ b/webapp/channels/src/components/sidebar/mobile_sidebar_header/index.ts @@ -1,5 +1,34 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import type {ConnectedProps} from 'react-redux'; +import {connect} from 'react-redux'; + +import {getCurrentTeam} from 'mattermost-redux/selectors/entities/teams'; +import {getCurrentUser} from 'mattermost-redux/selectors/entities/users'; + +import {openModal} from 'actions/views/modals'; + +import type {GlobalState} from 'types/store'; + import MobileSidebarHeader from './mobile_sidebar_header'; -export default MobileSidebarHeader; + +function mapStateToProps(state: GlobalState) { + const currentTeam = getCurrentTeam(state); + const currentUser = getCurrentUser(state); + + return { + username: currentUser?.username, + teamDisplayName: currentTeam?.display_name ?? '', + }; +} + +const mapDispatchToProps = { + openModal, +}; + +const connector = connect(mapStateToProps, mapDispatchToProps); + +export type PropsFromRedux = ConnectedProps; + +export default connector(MobileSidebarHeader); diff --git a/webapp/channels/src/components/sidebar/mobile_sidebar_header/mobile_sidebar_header.scss b/webapp/channels/src/components/sidebar/mobile_sidebar_header/mobile_sidebar_header.scss new file mode 100644 index 0000000000..539e596861 --- /dev/null +++ b/webapp/channels/src/components/sidebar/mobile_sidebar_header/mobile_sidebar_header.scss @@ -0,0 +1,35 @@ +.mobileSidebarHeader { + display: flex; + flex-direction: column; + padding: 15px; + gap: 4px; + + h1 { + overflow: hidden; + margin: 0; + color: var(--sidebar-header-text-color); + font-family: Metropolis, sans-serif; + font-size: 16px; + font-weight: 600; + line-height: 22px; + text-overflow: ellipsis; + white-space: nowrap; + } + + &__username { + display: flex; + overflow: hidden; + flex-direction: row; + color: rgba(var(--sidebar-header-text-color-rgb), 0.75); + font-family: "Open Sans", sans-serif; + font-size: 14px; + gap: 2px; + text-overflow: ellipsis; + white-space: nowrap; + + span:first-child { + overflow: hidden; + text-overflow: ellipsis; + } + } +} diff --git a/webapp/channels/src/components/sidebar/mobile_sidebar_header/mobile_sidebar_header.tsx b/webapp/channels/src/components/sidebar/mobile_sidebar_header/mobile_sidebar_header.tsx index 7876a56e76..aa937e35cb 100644 --- a/webapp/channels/src/components/sidebar/mobile_sidebar_header/mobile_sidebar_header.tsx +++ b/webapp/channels/src/components/sidebar/mobile_sidebar_header/mobile_sidebar_header.tsx @@ -2,27 +2,45 @@ // See LICENSE.txt for license information. import React from 'react'; -import {useIntl} from 'react-intl'; +import type {MouseEvent, KeyboardEvent} from 'react'; -import Contents from './contents'; +import CustomStatusEmoji from 'components/custom_status/custom_status_emoji'; +import CustomStatusModal from 'components/custom_status/custom_status_modal'; -export default function MobileSidebarHeader() { - const intl = useIntl(); - const ariaLabel = intl.formatMessage({id: 'accessibility.sections.lhsHeader', defaultMessage: 'team menu region'}); +import {ModalIdentifiers} from 'utils/constants'; + +import type {PropsFromRedux} from './index'; +import './mobile_sidebar_header.scss'; + +type Props = PropsFromRedux; + +export default function MobileSidebarHeader(props: Props) { + if (!props.username) { + return null; + } + + function handleCustomStatusEmojiClick(event: MouseEvent | KeyboardEvent) { + event.stopPropagation(); + + const customStatusInputModalData = { + modalId: ModalIdentifiers.CUSTOM_STATUS, + dialogType: CustomStatusModal, + }; + props.openModal(customStatusInputModalData); + } return ( -
-
- +
+

{props.teamDisplayName}

+
+ {'@' + props.username} +
); diff --git a/webapp/channels/src/components/sidebar/sidebar.test.tsx b/webapp/channels/src/components/sidebar/sidebar.test.tsx index 69692ff742..2810fda21e 100644 --- a/webapp/channels/src/components/sidebar/sidebar.test.tsx +++ b/webapp/channels/src/components/sidebar/sidebar.test.tsx @@ -1,15 +1,14 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {shallow} from 'enzyme'; import React from 'react'; import type {DeepPartial} from '@mattermost/types/utilities'; -import {Preferences} from 'mattermost-redux/constants'; +import {Permissions, Preferences} from 'mattermost-redux/constants'; import mergeObjects from 'packages/mattermost-redux/test/merge_objects'; -import {renderWithContext, screen, waitFor} from 'tests/react_testing_utils'; +import {fireEvent, renderWithContext, screen, waitFor} from 'tests/react_testing_utils'; import Constants, {ModalIdentifiers} from 'utils/constants'; import {TestHelper} from 'utils/test_helper'; @@ -18,14 +17,61 @@ import type {GlobalState} from 'types/store'; import Sidebar from './sidebar'; describe('components/sidebar', () => { - const currentTeamId = 'fake_team_id'; + const currentTeam = TestHelper.getTeamMock({ + id: 'current_team_id', + display_name: 'Current Test Team', + }); + + const initialState: DeepPartial = { + entities: { + teams: { + currentTeamId: currentTeam.id, + teams: { + [currentTeam.id]: currentTeam, + }, + myMembers: { + [currentTeam.id]: { + roles: 'team_user', + }, + }, + }, + users: { + currentUserId: 'current_user_id', + profiles: { + current_user_id: { + id: 'current_user_id', + roles: 'system_user system_admin', + }, + }, + }, + roles: { + roles: { + system_admin: { + permissions: [Permissions.MANAGE_TEAM], + }, + system_user: { + permissions: [], + }, + team_user: { + permissions: [], + }, + }, + }, + preferences: { + myPreferences: {}, + }, + general: { + config: {}, + }, + }, + }; const baseProps = { canCreatePublicChannel: true, canCreatePrivateChannel: true, canJoinPublicChannel: true, isOpen: false, - teamId: currentTeamId, + teamId: currentTeam.id, hasSeenModal: true, isCloud: false, unreadFilterEnabled: false, @@ -43,94 +89,36 @@ describe('components/sidebar', () => { }, }; - test('should match snapshot', () => { - const wrapper = shallow( + test('should render the sidebar components correctly', () => { + renderWithContext( , + initialState, ); - expect(wrapper).toMatchSnapshot(); + // Check for SidebarContainer that is the parent of the sidebar + expect(document.getElementById('SidebarContainer')).toBeInTheDocument(); + + expect(screen.getByRole('application', {name: /channel sidebar region/i})).toBeInTheDocument(); }); - test('should match snapshot when direct channels modal is open', () => { - const wrapper = shallow( - , - ); - - wrapper.instance().setState({showDirectChannelsModal: true}); - expect(wrapper).toMatchSnapshot(); - }); - - test('should match snapshot when more channels modal is open', () => { - const wrapper = shallow( - , - ); - - wrapper.instance().setState({showMoreChannelsModal: true}); - expect(wrapper).toMatchSnapshot(); - }); - - test('Should call Shortcut modal on FORWARD_SLASH+ctrl/meta', () => { - const wrapper = shallow( - , - ); - const instance = wrapper.instance(); - - let key = Constants.KeyCodes.BACK_SLASH[0] as string; - let keyCode = Constants.KeyCodes.BACK_SLASH[1] as number; - instance.handleKeyDownEvent({ctrlKey: true, preventDefault: jest.fn(), key, keyCode} as any); - expect(wrapper.instance().props.actions.openModal).not.toHaveBeenCalled(); - - key = 'ù'; - keyCode = Constants.KeyCodes.FORWARD_SLASH[1] as number; - instance.handleKeyDownEvent({ctrlKey: true, preventDefault: jest.fn(), key, keyCode} as any); - expect(wrapper.instance().props.actions.openModal).toHaveBeenCalledWith(expect.objectContaining({modalId: ModalIdentifiers.KEYBOARD_SHORTCUTS_MODAL})); - - key = '/'; - keyCode = Constants.KeyCodes.SEVEN[1] as number; - instance.handleKeyDownEvent({ctrlKey: true, preventDefault: jest.fn(), key, keyCode} as any); - expect(wrapper.instance().props.actions.openModal).toHaveBeenCalledWith(expect.objectContaining({modalId: ModalIdentifiers.KEYBOARD_SHORTCUTS_MODAL})); - - key = Constants.KeyCodes.FORWARD_SLASH[0] as string; - keyCode = Constants.KeyCodes.FORWARD_SLASH[1] as number; - instance.handleKeyDownEvent({ctrlKey: true, preventDefault: jest.fn(), key, keyCode} as any); - expect(wrapper.instance().props.actions.openModal).toHaveBeenCalledWith(expect.objectContaining({modalId: ModalIdentifiers.KEYBOARD_SHORTCUTS_MODAL})); - }); - - test('should toggle direct messages modal correctly', () => { - const wrapper = shallow( - , - ); - const instance = wrapper.instance(); - const mockEvent: Partial = {preventDefault: jest.fn()}; - - instance.hideMoreDirectChannelsModal = jest.fn(); - instance.showMoreDirectChannelsModal = jest.fn(); - - instance.handleOpenMoreDirectChannelsModal(mockEvent as any); - expect(instance.showMoreDirectChannelsModal).toHaveBeenCalled(); - - instance.setState({showDirectChannelsModal: true}); - instance.handleOpenMoreDirectChannelsModal(mockEvent as any); - expect(instance.hideMoreDirectChannelsModal).toHaveBeenCalled(); - }); - - test('should match empty div snapshot when teamId is missing', () => { + test('should not rendering anything when teamId is missing', () => { const props = { ...baseProps, teamId: '', }; - const wrapper = shallow( + renderWithContext( , + initialState, ); - expect(wrapper).toMatchSnapshot(); + expect(screen.queryByRole('application', {name: /channel sidebar region/i})).toBeNull(); }); describe('unreads category', () => { const currentUserId = 'current_user_id'; - const channel1 = TestHelper.getChannelMock({id: 'channel1', team_id: currentTeamId}); - const channel2 = TestHelper.getChannelMock({id: 'channel2', team_id: currentTeamId}); + const channel1 = TestHelper.getChannelMock({id: 'channel1', team_id: currentTeam.id}); + const channel2 = TestHelper.getChannelMock({id: 'channel2', team_id: currentTeam.id}); const baseState: DeepPartial = { entities: { @@ -141,7 +129,7 @@ describe('components/sidebar', () => { channel2, }, channelsInTeam: { - [currentTeamId]: new Set([channel1.id, channel2.id]), + [currentTeam.id]: new Set([channel1.id, channel2.id]), }, messageCounts: { channel1: {total: 10}, @@ -153,13 +141,40 @@ describe('components/sidebar', () => { }, }, teams: { - currentTeamId, + currentTeamId: currentTeam.id, teams: { - [currentTeamId]: TestHelper.getTeamMock({id: currentTeamId}), + [currentTeam.id]: currentTeam, + }, + myMembers: { + [currentTeam.id]: { + roles: 'team_user', + }, }, }, users: { currentUserId, + profiles: { + [currentUserId]: { + id: currentUserId, + roles: 'system_user system_admin', + }, + }, + }, + roles: { + roles: { + system_admin: { + permissions: [Permissions.MANAGE_TEAM], + }, + system_user: { + permissions: [], + }, + team_user: { + permissions: [], + }, + }, + }, + general: { + config: {}, }, }, }; @@ -263,4 +278,111 @@ describe('components/sidebar', () => { }); }); }); + + describe('modals', () => { + test('should call Shortcut modal on FORWARD_SLASH+ctrl/meta', () => { + const openModalSpy = jest.fn(); + const closeModalSpy = jest.fn(); + + const props = { + ...baseProps, + isKeyBoardShortcutModalOpen: false, + actions: { + ...baseProps.actions, + openModal: openModalSpy, + closeModal: closeModalSpy, + }, + }; + + renderWithContext( + , + initialState, + ); + expect(document.getElementById('SidebarContainer')).toBeInTheDocument(); + + // Test with backslash key (should not trigger the modal) + fireEvent.keyDown(document, { + key: '\\', + code: 'Backslash', + keyCode: Constants.KeyCodes.BACK_SLASH[1], + ctrlKey: true, + }); + expect(openModalSpy).not.toHaveBeenCalled(); + + // Test with 'ù' key but with forward slash keyCode (should trigger the modal) + fireEvent.keyDown(document, { + key: 'ù', + code: 'Slash', + keyCode: Constants.KeyCodes.FORWARD_SLASH[1], + ctrlKey: true, + }); + expect(openModalSpy).toHaveBeenCalledWith(expect.objectContaining({ + modalId: ModalIdentifiers.KEYBOARD_SHORTCUTS_MODAL, + })); + + // Reset the spy + openModalSpy.mockClear(); + + // Test with '/' key but with seven keyCode (should trigger the modal) + fireEvent.keyDown(document, { + key: '/', + code: 'Digit7', + keyCode: Constants.KeyCodes.SEVEN[1], + ctrlKey: true, + }); + expect(openModalSpy).toHaveBeenCalledWith(expect.objectContaining({ + modalId: ModalIdentifiers.KEYBOARD_SHORTCUTS_MODAL, + })); + + // Reset the spy + openModalSpy.mockClear(); + + // Test with forward slash key (should trigger the modal) + fireEvent.keyDown(document, { + key: '/', + code: 'Slash', + keyCode: Constants.KeyCodes.FORWARD_SLASH[1], + ctrlKey: true, + }); + expect(openModalSpy).toHaveBeenCalledWith(expect.objectContaining({ + modalId: ModalIdentifiers.KEYBOARD_SHORTCUTS_MODAL, + })); + }); + + test('should close Shortcut modal on FORWARD_SLASH+ctrl/meta when already open', () => { + const openModalSpy = jest.fn(); + const closeModalSpy = jest.fn(); + + const props = { + ...baseProps, + isKeyBoardShortcutModalOpen: true, // Modal is already open + actions: { + ...baseProps.actions, + openModal: openModalSpy, + closeModal: closeModalSpy, + }, + }; + + renderWithContext( + , + initialState, + ); + + expect(document.getElementById('SidebarContainer')).toBeInTheDocument(); + + // Test with forward slash key (should close the modal since it's already open) + fireEvent.keyDown(document, { + key: '/', + code: 'Slash', + keyCode: Constants.KeyCodes.FORWARD_SLASH[1], + ctrlKey: true, + }); + + // Should call closeModal with the keyboard shortcuts modal ID + expect(closeModalSpy).toHaveBeenCalledWith(ModalIdentifiers.KEYBOARD_SHORTCUTS_MODAL); + + // Should not call openModal + expect(openModalSpy).not.toHaveBeenCalled(); + }); + }); }); 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 index 5f8f389358..fcb03fb549 100644 --- 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 @@ -17,7 +17,8 @@ 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'; +export const ELEMENT_ID_FOR_BROWSE_OR_ADD_CHANNEL_MENU = 'browserOrAddChannelMenu'; +export const ELEMENT_ID_FOR_BROWSE_OR_ADD_CHANNEL_MENU_BUTTON = 'browseOrAddChannelMenuButton'; type Props = { canCreateChannel: boolean; @@ -52,6 +53,7 @@ export default function SidebarBrowserOrAddChannelMenu(props: Props) { /> )} trailingElements={showCreateAndJoinChannelsTutorialTip && } + aria-haspopup='true' /> ); } @@ -69,6 +71,7 @@ export default function SidebarBrowserOrAddChannelMenu(props: Props) { defaultMessage='Browse channels' /> )} + aria-haspopup='true' /> ); } @@ -84,6 +87,7 @@ export default function SidebarBrowserOrAddChannelMenu(props: Props) { defaultMessage='Open a direct message' /> )} + aria-haspopup='true' /> ); @@ -100,6 +104,7 @@ export default function SidebarBrowserOrAddChannelMenu(props: Props) { defaultMessage='Create new user group' /> )} + aria-haspopup='true' /> ); } @@ -117,6 +122,7 @@ export default function SidebarBrowserOrAddChannelMenu(props: Props) { defaultMessage='Create new category' /> )} + aria-haspopup='true' /> ); } @@ -139,13 +145,14 @@ export default function SidebarBrowserOrAddChannelMenu(props: Props) { )} trailingElements={showInvitePeopleTutorialTip && } + aria-haspopup='true' /> ); return ( {createNewChannelMenuItem} diff --git a/webapp/channels/src/components/sidebar/sidebar_header/sidebar_header.scss b/webapp/channels/src/components/sidebar/sidebar_header/sidebar_header.scss index a86f2bb00c..1b85e24192 100644 --- a/webapp/channels/src/components/sidebar/sidebar_header/sidebar_header.scss +++ b/webapp/channels/src/components/sidebar/sidebar_header/sidebar_header.scss @@ -6,31 +6,31 @@ align-items: center; justify-content: space-between; padding: 0 16px; -} -.sidebarHeader { - display: flex; - margin: 0; - color: var(--sidebar-text); - cursor: pointer; - font-family: Metropolis, sans-serif; - font-size: 16px; - font-weight: 600; - line-height: 24px; - - .title { - display: inline-block; + button#sidebarTeamMenuButton { overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - .icon-chevron-down { - margin-right: -1px; - margin-left: -3px; - } - - #SidebarContainer & { + padding: 2px 6px; font-family: Metropolis, sans-serif; + font-size: 16px; + gap: 1px; + + & > span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + & > .icon { + display: flex; + justify-content: center; + } + } +} + +#sidebarTeamMenu { + .learnAboutTeamsMenuItem { + .leading-element,.label-elements>span { + color: var(--link-color); + } } } diff --git a/webapp/channels/src/components/sidebar/sidebar_header/sidebar_header.test.tsx b/webapp/channels/src/components/sidebar/sidebar_header/sidebar_header.test.tsx index e09aacc7a8..2ede374a42 100644 --- a/webapp/channels/src/components/sidebar/sidebar_header/sidebar_header.test.tsx +++ b/webapp/channels/src/components/sidebar/sidebar_header/sidebar_header.test.tsx @@ -1,141 +1,156 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {shallow} from 'enzyme'; import React from 'react'; -import IconButton from '@mattermost/compass-components/components/icon-button'; // eslint-disable-line no-restricted-imports +import {Permissions} from 'mattermost-redux/constants'; +import {renderWithContext, screen} from 'tests/react_testing_utils'; import {CloudProducts} from 'utils/constants'; import {FileSizes} from 'utils/file_utils'; +import {TestHelper} from 'utils/test_helper'; -import BrowserOrAddChannelMenu from './sidebar_browse_or_add_channel_menu'; import SidebarHeader from './sidebar_header'; import type {Props} from './sidebar_header'; -let props: Props; +describe('SidebarHeader', () => { + const defaultProps: Props = { + showNewChannelModal: jest.fn(), + showMoreChannelsModal: jest.fn(), + invitePeopleModal: jest.fn(), + showCreateCategoryModal: jest.fn(), + canCreateChannel: true, + canJoinPublicChannel: true, + handleOpenDirectMessagesModal: jest.fn(), + unreadFilterEnabled: true, + showCreateUserGroupModal: jest.fn(), + canCreateCustomGroups: true, + }; -const mockDispatch = jest.fn(); -let mockState: any; + const team = TestHelper.getTeamMock({ + display_name: 'Steadfast', + }); -jest.mock('react-redux', () => ({ - ...jest.requireActual('react-redux') as typeof import('react-redux'), - useSelector: (selector: (state: typeof mockState) => unknown) => selector(mockState), - useDispatch: () => mockDispatch, -})); - -describe('Components/SidebarHeader', () => { - beforeEach(() => { - props = { - showNewChannelModal: jest.fn(), - showMoreChannelsModal: jest.fn(), - invitePeopleModal: jest.fn(), - showCreateCategoryModal: jest.fn(), - canCreateChannel: true, - canJoinPublicChannel: true, - handleOpenDirectMessagesModal: jest.fn(), - unreadFilterEnabled: true, - showCreateUserGroupModal: jest.fn(), - canCreateCustomGroups: true, - }; - - mockState = { - entities: { - general: { - config: {}, + const initialState = { + entities: { + general: { + config: {}, + }, + preferences: { + myPreferences: {}, + }, + teams: { + currentTeamId: team.id, + teams: { + [team.id]: team, }, - preferences: { - myPreferences: {}, + myMembers: { + [team.id]: { + roles: 'team_user', + }, + }, + }, + users: { + profiles: { + uid: { + id: 'uid', + roles: 'system_user system_admin', + }, + }, + currentUserId: 'uid', + }, + roles: { + roles: { + system_admin: { + permissions: [Permissions.MANAGE_TEAM], + }, + system_user: { + permissions: [], + }, + team_user: { + permissions: [], + }, + }, + }, + usage: { + integrations: { + enabled: 11, + enabledLoaded: true, + }, + messages: { + history: 10000, + historyLoaded: true, + }, + files: { + totalStorage: FileSizes.Gigabyte, + totalStorageLoaded: true, }, teams: { - currentTeamId: 'currentteam', - teams: { - currentteam: { - id: 'currentteam', - description: 'et iste illum reprehenderit aliquid in rem itaque in maxime eius.', - }, + active: 1, + teamsLoaded: true, + }, + boards: { + cards: 500, + cardsLoaded: true, + }, + }, + cloud: { + subscription: { + product_id: 'test_prod_1', + trial_end_at: 1652807380, + is_free_trial: 'false', + }, + products: { + test_prod_1: { + id: 'test_prod_1', + sku: CloudProducts.STARTER, + price_per_seat: 0, }, }, - users: { - profiles: { - uid: {}, - }, - currentUserId: 'uid', - }, - usage: { - integrations: { - enabled: 11, - enabledLoaded: true, - }, - messages: { - history: 10000, - historyLoaded: true, - }, - files: { - totalStorage: FileSizes.Gigabyte, - totalStorageLoaded: true, - }, - teams: { - active: 1, - teamsLoaded: true, - }, - boards: { - cards: 500, - cardsLoaded: true, - }, - }, - cloud: { - subscription: { - product_id: 'test_prod_1', - trial_end_at: 1652807380, - is_free_trial: 'false', - }, - products: { - test_prod_1: { - id: 'test_prod_1', - sku: CloudProducts.STARTER, - price_per_seat: 0, - }, - }, + limits: { + limitsLoaded: true, limits: { - limitsLoaded: true, - limits: { - integrations: { - enabled: 10, - }, - messages: { - history: 10000, - }, - files: { - total_storage: FileSizes.Gigabyte, - }, - teams: { - active: 1, - }, - boards: { - cards: 500, - views: 5, - }, + integrations: { + enabled: 10, + }, + messages: { + history: 10000, + }, + files: { + total_storage: FileSizes.Gigabyte, + }, + teams: { + active: 1, + }, + boards: { + cards: 500, + views: 5, }, }, }, }, - views: { - addChannelDropdown: { - isOpen: false, - }, - }, - }; + }, + }; + + test('should render the team menu button', () => { + renderWithContext(, initialState); + + expect(screen.getByText('Steadfast')).toBeInTheDocument(); + expect(screen.getByRole('button', {name: team.display_name})).toBeInTheDocument(); }); - it('should show BrowserOrAddChannelMenu', () => { - const wrapper = shallow(); - expect(wrapper.find(BrowserOrAddChannelMenu).length).toBe(1); + test('should render the \'Browse or create channels\' menu button', () => { + renderWithContext(, initialState); + + expect(screen.getByRole('button', {name: /Browse or create channels/i})).toBeInTheDocument(); }); - it('should embed teams menu dropdown into heading', () => { - const wrapper = shallow(); - expect(wrapper.find(IconButton).length).toBe(0); - expect(wrapper.find('i').prop('className')).toBe('icon icon-chevron-down'); + test('should not render anything when team is empty', () => { + const state = {...initialState}; + state.entities.teams.currentTeamId = ''; + renderWithContext(, state); + + expect(screen.queryByRole('button', {name: team.display_name})).toBeNull(); + expect(screen.queryByRole('button', {name: /Add Channel Dropdown/i})).toBeNull(); }); }); diff --git a/webapp/channels/src/components/sidebar/sidebar_header/sidebar_header.tsx b/webapp/channels/src/components/sidebar/sidebar_header/sidebar_header.tsx index 6ebbb6f862..f183e60a27 100644 --- a/webapp/channels/src/components/sidebar/sidebar_header/sidebar_header.tsx +++ b/webapp/channels/src/components/sidebar/sidebar_header/sidebar_header.tsx @@ -1,17 +1,13 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, {useState} from 'react'; +import React from 'react'; import {useSelector} from 'react-redux'; import {getCurrentTeam} from 'mattermost-redux/selectors/entities/teams'; -import useGetUsageDeltas from 'components/common/hooks/useGetUsageDeltas'; -import MainMenu from 'components/main_menu'; -import MenuWrapper from 'components/widgets/menu/menu_wrapper'; -import WithTooltip from 'components/with_tooltip'; - import SidebarBrowseOrAddChannelMenu from './sidebar_browse_or_add_channel_menu'; +import SidebarTeamMenu from './sidebar_team_menu'; import './sidebar_header.scss'; @@ -30,52 +26,14 @@ export type Props = { const SidebarHeader = (props: Props) => { const currentTeam = useSelector(getCurrentTeam); - const usageDeltas = useGetUsageDeltas(); - - const [menuToggled, setMenuToggled] = useState(false); - - const handleMenuToggle = () => { - setMenuToggled(!menuToggled); - }; if (!currentTeam) { return null; } return ( -