diff --git a/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_account_settings_spec.js b/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_account_settings_spec.js index fe7b0b9143..0257834020 100644 --- a/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_account_settings_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_account_settings_spec.js @@ -34,8 +34,8 @@ describe('Verify Accessibility Support in different sections in Settings and Pro {key: 'desktop', label: 'Desktop Notifications', type: 'radio'}, {key: 'email', label: 'Email Notifications', type: 'radio'}, {key: 'push', label: 'Mobile Push Notifications', type: 'radio'}, - {key: 'keysWithNotification', label: 'Keywords that trigger Notifications', type: 'checkbox'}, - {key: 'comments', label: 'Reply notifications', type: 'radio'}, + {key: 'keysWithNotification', label: 'Keywords That Trigger Notifications', type: 'checkbox'}, + {key: 'comments', label: 'Reply Notifications', type: 'radio'}, ], display: [ {key: 'theme', label: 'Theme', type: 'radio'}, 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 4e7f24bf34..9f4c0d33f9 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 @@ -188,6 +188,8 @@ function mapFeatureIdToId(id: string) { return 'All Professional features'; case 'mattermost.feature.all_enterprise': return 'All Enterprise features'; + case 'mattermost.feature.highlight_without_notification': + return 'Keywords Highlight Without Notification'; default: return ''; } diff --git a/e2e-tests/cypress/tests/integration/channels/messaging/message_auto_response_spec.js b/e2e-tests/cypress/tests/integration/channels/messaging/message_auto_response_spec.js index 42db600700..007b04dc74 100644 --- a/e2e-tests/cypress/tests/integration/channels/messaging/message_auto_response_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/messaging/message_auto_response_spec.js @@ -48,7 +48,7 @@ describe('Auto Response In DMs', () => { // # Open 'Settings' modal and view 'Notifications' cy.uiOpenSettingsModal().within(() => { // # Click on 'Edit' for 'Automatic Direct Message Replies - cy.get('#auto-responderEdit').should('be.visible').click(); + cy.get('#auto-responderEdit').should('exist').scrollIntoView().and('be.visible').click(); // # Click on 'Enabled' checkbox cy.get('#autoResponderActive').should('be.visible').click(); diff --git a/e2e-tests/cypress/tests/integration/channels/notifications/at_icon_still_shows_mentions_list_with_deactivated_triggers_spec.js b/e2e-tests/cypress/tests/integration/channels/notifications/at_icon_still_shows_mentions_list_with_deactivated_triggers_spec.js index c50a58c998..521d3f9133 100644 --- a/e2e-tests/cypress/tests/integration/channels/notifications/at_icon_still_shows_mentions_list_with_deactivated_triggers_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/notifications/at_icon_still_shows_mentions_list_with_deactivated_triggers_spec.js @@ -23,8 +23,8 @@ describe('Notifications', () => { // # Open 'Settings' modal cy.uiOpenSettingsModal().within(() => { - // # Open 'Keywords that trigger Notifications' setting and uncheck all the checkboxes - cy.findByRole('heading', {name: 'Keywords that trigger Notifications'}).should('be.visible').click(); + // # Open 'Keywords That Trigger Notifications' setting and uncheck all the checkboxes + cy.findByRole('heading', {name: 'Keywords That Trigger Notifications'}).should('be.visible').click(); cy.findByRole('checkbox', {name: `Your case-sensitive first name "${otherUser.first_name}"`}).should('not.be.checked'); cy.findByRole('checkbox', {name: `Your non case-sensitive username "${otherUser.username}"`}).should('not.be.checked'); cy.findByRole('checkbox', {name: 'Channel-wide mentions "@channel", "@all", "@here"'}).click().should('not.be.checked'); diff --git a/e2e-tests/cypress/tests/integration/channels/notifications/at_mentions_spec.js b/e2e-tests/cypress/tests/integration/channels/notifications/at_mentions_spec.js index 716c0319a4..4982846810 100644 --- a/e2e-tests/cypress/tests/integration/channels/notifications/at_mentions_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/notifications/at_mentions_spec.js @@ -252,7 +252,7 @@ function setNotificationSettings(desiredSettings = {first: true, username: true, cy.findAllByText('Notifications').should('be.visible'); // Open up 'Words that trigger mentions' sub-section - cy.findByText('Keywords that trigger Notifications'). + cy.findByText('Keywords That Trigger Notifications'). scrollIntoView(). click(); diff --git a/e2e-tests/cypress/tests/integration/channels/notifications/deselect_username_mention_trigger_spec.js b/e2e-tests/cypress/tests/integration/channels/notifications/deselect_username_mention_trigger_spec.js index 93bd1e2355..10bb5465fc 100644 --- a/e2e-tests/cypress/tests/integration/channels/notifications/deselect_username_mention_trigger_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/notifications/deselect_username_mention_trigger_spec.js @@ -29,8 +29,8 @@ describe('Notifications', () => { // # Open 'Settings' modal cy.uiOpenSettingsModal().within(() => { - // # Open 'Keywords that trigger Notifications' setting - cy.findByRole('heading', {name: 'Keywords that trigger Notifications'}).should('be.visible').click(); + // # Open 'Keywords That Trigger Notifications' setting + cy.findByRole('heading', {name: 'Keywords That Trigger Notifications'}).should('be.visible').click(); // * As otherUser, ensure that 'Your non-case sensitive username' is not checked cy.findByRole('checkbox', {name: `Your non case-sensitive username "${otherUser.username}"`}).should('not.be.checked'); diff --git a/e2e-tests/playwright/support/ui/components/channels/delete_post_modal.ts b/e2e-tests/playwright/support/ui/components/channels/delete_post_modal.ts index 041f5a5afa..af92689ae1 100644 --- a/e2e-tests/playwright/support/ui/components/channels/delete_post_modal.ts +++ b/e2e-tests/playwright/support/ui/components/channels/delete_post_modal.ts @@ -9,7 +9,7 @@ export default class DeletePostModal { constructor(container: Locator) { this.container = container; - this.confirmButton = this.container.locator('#deletePostModalButton'); + this.confirmButton = container.locator('#deletePostModalButton'); } async toBeVisible() { diff --git a/e2e-tests/playwright/support/ui/components/channels/settings/notification_settings.ts b/e2e-tests/playwright/support/ui/components/channels/settings/notification_settings.ts new file mode 100644 index 0000000000..2482dcaaa9 --- /dev/null +++ b/e2e-tests/playwright/support/ui/components/channels/settings/notification_settings.ts @@ -0,0 +1,54 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {expect, Locator} from '@playwright/test'; + +type NotificationSettingsSection = 'keysWithHighlight' | 'keysWithNotification'; + +export default class NotificationsSettings { + readonly container: Locator; + + constructor(container: Locator) { + this.container = container; + } + + async toBeVisible() { + await expect(this.container).toBeVisible(); + } + + async expandSection(section: NotificationSettingsSection) { + if (section === 'keysWithHighlight') { + await this.container.getByText('Keywords That Get Highlighted (without notifications)').click(); + await this.verifySectionIsExpanded('keysWithHighlight'); + } + } + + async verifySectionIsExpanded(section: NotificationSettingsSection) { + await expect(this.container.locator(`#${section}Edit`)).not.toBeVisible(); + + if (section === 'keysWithHighlight') { + await expect( + this.container.getByText( + 'Enter non case-sensitive keywords, press Tab or use commas to separate them:', + ), + ).toBeVisible(); + await expect( + this.container.getByText( + 'These keywords will be shown to you with a highlight when anyone sends a message that includes them.', + ), + ).toBeVisible(); + } + } + + async getKeywordsInput() { + await expect(this.container.locator('input')).toBeVisible(); + return this.container.locator('input'); + } + + async save() { + await expect(this.container.getByText('Save')).toBeVisible(); + await this.container.getByText('Save').click(); + } +} + +export {NotificationsSettings}; diff --git a/e2e-tests/playwright/support/ui/components/channels/settings/settings_modal.ts b/e2e-tests/playwright/support/ui/components/channels/settings/settings_modal.ts new file mode 100644 index 0000000000..2b3e08cc76 --- /dev/null +++ b/e2e-tests/playwright/support/ui/components/channels/settings/settings_modal.ts @@ -0,0 +1,39 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {expect, Locator} from '@playwright/test'; + +import {NotificationsSettings} from './notification_settings'; + +export default class SettingsModal { + readonly container: Locator; + + readonly notificationsSettingsTab; + readonly notificationsSettings; + + constructor(container: Locator) { + this.container = container; + + this.notificationsSettingsTab = container.locator('#notificationsButton'); + this.notificationsSettings = new NotificationsSettings(container.locator('#notificationSettings')); + } + + async toBeVisible() { + await expect(this.container).toBeVisible(); + } + + async openNotificationsTab() { + await expect(this.notificationsSettingsTab).toBeVisible(); + await this.notificationsSettingsTab.click(); + + await this.notificationsSettings.toBeVisible(); + } + + async closeModal() { + await this.container.getByLabel('Close').click(); + + await expect(this.container).not.toBeVisible(); + } +} + +export {SettingsModal}; diff --git a/e2e-tests/playwright/support/ui/components/global_header.ts b/e2e-tests/playwright/support/ui/components/global_header.ts index 8ab49d6307..a17a62a6fa 100644 --- a/e2e-tests/playwright/support/ui/components/global_header.ts +++ b/e2e-tests/playwright/support/ui/components/global_header.ts @@ -7,11 +7,19 @@ export default class GlobalHeader { readonly container: Locator; readonly productSwitchMenu; + readonly recentMentionsButton; + readonly settingsButton; constructor(container: Locator) { this.container = container; this.productSwitchMenu = container.getByRole('button', {name: 'Product switch menu'}); + this.recentMentionsButton = container.getByRole('button', {name: 'Recent mentions'}); + this.settingsButton = container.getByRole('button', {name: 'Settings'}); + } + + async toBeVisible(name: string) { + await expect(this.container.getByRole('heading', {name})).toBeVisible(); } async switchProduct(name: string) { @@ -19,8 +27,14 @@ export default class GlobalHeader { await this.container.getByRole('link', {name}).click(); } - async toBeVisible(name: string) { - await expect(this.container.getByRole('heading', {name})).toBeVisible(); + async openSettings() { + await expect(this.settingsButton).toBeVisible(); + await this.settingsButton.click(); + } + + async openRecentMentions() { + await expect(this.recentMentionsButton).toBeVisible(); + await this.recentMentionsButton.click(); } } diff --git a/e2e-tests/playwright/support/ui/components/index.ts b/e2e-tests/playwright/support/ui/components/index.ts index d05d1705b9..a129f32c1c 100644 --- a/e2e-tests/playwright/support/ui/components/index.ts +++ b/e2e-tests/playwright/support/ui/components/index.ts @@ -10,6 +10,7 @@ import {ChannelsSidebarLeft} from './channels/sidebar_left'; import {ChannelsSidebarRight} from './channels/sidebar_right'; import {DeletePostModal} from './channels/delete_post_modal'; import {FindChannelsModal} from './channels/find_channels_modal'; +import {SettingsModal} from './channels/settings/settings_modal'; import {Footer} from './footer'; import {GlobalHeader} from './global_header'; import {MainHeader} from './main_header'; @@ -30,6 +31,7 @@ const components = { ChannelsPost, FindChannelsModal, DeletePostModal, + SettingsModal, PostDotMenu, PostMenu, ThreadFooter, diff --git a/e2e-tests/playwright/support/ui/pages/channels.ts b/e2e-tests/playwright/support/ui/pages/channels.ts index 1031c51e12..27e231b16e 100644 --- a/e2e-tests/playwright/support/ui/pages/channels.ts +++ b/e2e-tests/playwright/support/ui/pages/channels.ts @@ -18,6 +18,7 @@ export default class ChannelsPage { readonly findChannelsModal; readonly deletePostModal; + readonly settingsModal; readonly postDotMenu; readonly postReminderMenu; @@ -37,6 +38,7 @@ export default class ChannelsPage { // Modals this.findChannelsModal = new components.FindChannelsModal(page.getByRole('dialog', {name: 'Find Channels'})); this.deletePostModal = new components.DeletePostModal(page.locator('#deletePostModal')); + this.settingsModal = new components.SettingsModal(page.getByRole('dialog', {name: 'Settings'})); // Menus this.postDotMenu = new components.PostDotMenu(page.getByRole('menu', {name: 'Post extra options'})); diff --git a/e2e-tests/playwright/tests/functional/channels/settings/notifications/highlight_without_notification.spec.ts b/e2e-tests/playwright/tests/functional/channels/settings/notifications/highlight_without_notification.spec.ts new file mode 100644 index 0000000000..c59dbb6eac --- /dev/null +++ b/e2e-tests/playwright/tests/functional/channels/settings/notifications/highlight_without_notification.spec.ts @@ -0,0 +1,271 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {expect} from '@playwright/test'; + +import {test} from '@e2e-support/test_fixture'; +import {getRandomId} from '@e2e-support/util'; +import {createRandomPost} from '@e2e-support/server/post'; + +const keywords = [`AB${getRandomId()}`, `CD${getRandomId()}`, `EF${getRandomId()}`, `Highlight me ${getRandomId()}`]; + +const highlightWithoutNotificationClass = 'non-notification-highlight'; + +test('MM-T5465-1 Should add the keyword when enter, comma or tab is pressed on the textbox', async ({pw, pages}) => { + const {user} = await pw.initSetup(); + + // # Log in as a user in new browser context + const {page} = await pw.testBrowser.login(user); + + // # Visit default channel page + const channelPage = new pages.ChannelsPage(page); + await channelPage.goto(); + await channelPage.toBeVisible(); + + await channelPage.centerView.postCreate.postMessage('Hello World'); + + // # Open settings modal + await channelPage.globalHeader.openSettings(); + await channelPage.settingsModal.toBeVisible(); + + // # Open notifications tab + await channelPage.settingsModal.openNotificationsTab(); + + // # Open keywords that get highlighted section + await channelPage.settingsModal.notificationsSettings.expandSection('keysWithHighlight'); + + const keywordsInput = await channelPage.settingsModal.notificationsSettings.getKeywordsInput(); + + // # Enter keyword 1 + await keywordsInput.type(keywords[0]); + + // # Press Comma on the textbox + await keywordsInput.press(','); + + // # Enter keyword 2 + await keywordsInput.type(keywords[1]); + + // # Press Tab on the textbox + await keywordsInput.press('Tab'); + + // # Enter keyword 3 + await keywordsInput.type(keywords[2]); + + // # Press Enter on the textbox + await keywordsInput.press('Enter'); + + // * Verify that the keywords have been added to the collapsed description + await expect(channelPage.settingsModal.notificationsSettings.container.getByText(keywords[0])).toBeVisible(); + await expect(channelPage.settingsModal.notificationsSettings.container.getByText(keywords[1])).toBeVisible(); + await expect(channelPage.settingsModal.notificationsSettings.container.getByText(keywords[2])).toBeVisible(); +}); + +test('MM-T5465-2 Should highlight the keywords when a message is sent with the keyword in center', async ({ + pw, + pages, +}) => { + const {user} = await pw.initSetup(); + + // # Log in as a user in new browser context + const {page} = await pw.testBrowser.login(user); + + // # Visit default channel page + const channelPage = new pages.ChannelsPage(page); + await channelPage.goto(); + await channelPage.toBeVisible(); + + // # Open settings modal + await channelPage.globalHeader.openSettings(); + await channelPage.settingsModal.toBeVisible(); + + // # Open notifications tab + await channelPage.settingsModal.openNotificationsTab(); + + // # Open keywords that get highlighted section + await channelPage.settingsModal.notificationsSettings.expandSection('keysWithHighlight'); + + // # Enter the keyword + const keywordsInput = await channelPage.settingsModal.notificationsSettings.getKeywordsInput(); + await keywordsInput.type(keywords[3]); + await keywordsInput.press('Tab'); + + // # Save the keyword + await channelPage.settingsModal.notificationsSettings.save(); + + // # Close the settings modal + await channelPage.settingsModal.closeModal(); + + // # Post a message without the keyword + const messageWithoutKeyword = 'This message does not contain the keyword'; + await channelPage.centerView.postCreate.postMessage(messageWithoutKeyword); + const lastPostWithoutHighlight = await channelPage.centerView.getLastPost(); + + // * Verify that the keywords are not highlighted + await expect(lastPostWithoutHighlight.container.getByText(messageWithoutKeyword)).toBeVisible(); + await expect(lastPostWithoutHighlight.container.getByText(messageWithoutKeyword)).not.toHaveClass( + highlightWithoutNotificationClass, + ); + + // # Post a message with the keyword + const messageWithKeyword = `This message contains the keyword ${keywords[3]}`; + await channelPage.centerView.postCreate.postMessage(messageWithKeyword); + const lastPostWithHighlight = await channelPage.centerView.getLastPost(); + + // * Verify that the keywords are highlighted + await expect(lastPostWithHighlight.container.getByText(messageWithKeyword)).toBeVisible(); + await expect(lastPostWithHighlight.container.getByText(keywords[3])).toHaveClass(highlightWithoutNotificationClass); +}); + +test('MM-T5465-3 Should highlight the keywords when a message is sent with the keyword in rhs', async ({pw, pages}) => { + const {user} = await pw.initSetup(); + + // # Log in as a user in new browser context + const {page} = await pw.testBrowser.login(user); + + // # Visit default channel page + const channelPage = new pages.ChannelsPage(page); + await channelPage.goto(); + await channelPage.toBeVisible(); + + // # Open settings modal + await channelPage.globalHeader.openSettings(); + await channelPage.settingsModal.toBeVisible(); + + // # Open notifications tab + await channelPage.settingsModal.openNotificationsTab(); + + // # Open keywords that get highlighted section + await channelPage.settingsModal.notificationsSettings.expandSection('keysWithHighlight'); + + // # Enter the keyword + const keywordsInput = await channelPage.settingsModal.notificationsSettings.getKeywordsInput(); + await keywordsInput.type(keywords[3]); + await keywordsInput.press('Tab'); + + // # Save the keyword + await channelPage.settingsModal.notificationsSettings.save(); + + // # Close the settings modal + await channelPage.settingsModal.closeModal(); + + // # Post a message without the keyword + const messageWithoutKeyword = 'This message does not contain the keyword'; + await channelPage.centerView.postCreate.postMessage(messageWithoutKeyword); + const lastPostWithoutHighlight = await channelPage.centerView.getLastPost(); + + // # Open the message in the RHS + await lastPostWithoutHighlight.hover(); + await lastPostWithoutHighlight.postMenu.toBeVisible(); + await lastPostWithoutHighlight.postMenu.reply(); + await channelPage.sidebarRight.toBeVisible(); + + // # Post a message with the keyword in the RHS + const messageWithKeyword = `This message contains the keyword ${keywords[3]}`; + await channelPage.sidebarRight.postCreate.postMessage(messageWithKeyword); + + // * Verify that the keywords are highlighted + const lastPostWithHighlightInRHS = await channelPage.sidebarRight.getLastPost(); + await expect(lastPostWithHighlightInRHS.container.getByText(messageWithKeyword)).toBeVisible(); + await expect(lastPostWithHighlightInRHS.container.getByText(keywords[3])).toHaveClass( + highlightWithoutNotificationClass, + ); +}); + +test('MM-T5465-4 Highlighted keywords should not appear in the Recent Mentions', async ({pw, pages}) => { + const {user} = await pw.initSetup(); + + // # Log in as a user in new browser context + const {page} = await pw.testBrowser.login(user); + + // # Visit default channel page + const channelPage = new pages.ChannelsPage(page); + await channelPage.goto(); + await channelPage.toBeVisible(); + + // # Open settings modal + await channelPage.globalHeader.openSettings(); + await channelPage.settingsModal.toBeVisible(); + + // # Open notifications tab + await channelPage.settingsModal.openNotificationsTab(); + + // # Open keywords that get highlighted section + await channelPage.settingsModal.notificationsSettings.expandSection('keysWithHighlight'); + + // # Enter the keyword + const keywordsInput = await channelPage.settingsModal.notificationsSettings.getKeywordsInput(); + await keywordsInput.type(keywords[0]); + await keywordsInput.press('Tab'); + + // # Save the keyword + await channelPage.settingsModal.notificationsSettings.save(); + + // # Close the settings modal + await channelPage.settingsModal.closeModal(); + + // # Open the recent mentions + await channelPage.globalHeader.openRecentMentions(); + + // * Verify recent mentions is empty + await channelPage.sidebarRight.toBeVisible(); + await expect(channelPage.sidebarRight.container.getByText('No mentions yet')).toBeVisible(); +}); + +test('MM-T5465-5 Should highlight keywords in message sent from another user', async ({pw, pages}) => { + const {adminClient, team, adminUser, user} = await pw.initSetup(); + + if (!adminUser) { + throw new Error('Failed to create admin user'); + } + + // # Get the default channel of the team for getting the channel id + const channel = await adminClient.getChannelByName(team.id, 'town-square'); + + const highlightKeyword = keywords[0]; + const messageWithKeyword = `This recieved message contains the ${highlightKeyword} keyword `; + + // # Create a post containing the keyword in the channel by admin + await adminClient.createPost( + createRandomPost({ + message: messageWithKeyword, + channel_id: channel.id, + user_id: adminUser.id, + }), + ); + + // # Now log in as a user in new browser context + const {page} = await pw.testBrowser.login(user); + + // # Visit default channel page + const channelPage = new pages.ChannelsPage(page); + await channelPage.goto(); + await channelPage.toBeVisible(); + + // # Open settings modal + await channelPage.globalHeader.openSettings(); + await channelPage.settingsModal.toBeVisible(); + + // # Open notifications tab + await channelPage.settingsModal.openNotificationsTab(); + + // # Open keywords that get highlighted section + await channelPage.settingsModal.notificationsSettings.expandSection('keysWithHighlight'); + + // # Enter the keyword + const keywordsInput = await channelPage.settingsModal.notificationsSettings.getKeywordsInput(); + await keywordsInput.type(keywords[0]); + await keywordsInput.press('Tab'); + + // # Save the keyword + await channelPage.settingsModal.notificationsSettings.save(); + + // # Close the settings modal + await channelPage.settingsModal.closeModal(); + + // * Verify that the keywords are highlighted in the last message recieved + const lastPostWithHighlight = await channelPage.centerView.getLastPost(); + await expect(lastPostWithHighlight.container.getByText(messageWithKeyword)).toBeVisible(); + await expect(lastPostWithHighlight.container.getByText(highlightKeyword)).toHaveClass( + highlightWithoutNotificationClass, + ); +}); diff --git a/server/public/model/notify_admin.go b/server/public/model/notify_admin.go index 48eb238c92..6d496e5fcd 100644 --- a/server/public/model/notify_admin.go +++ b/server/public/model/notify_admin.go @@ -13,17 +13,18 @@ import ( type MattermostFeature string const ( - PaidFeatureGuestAccounts = MattermostFeature("mattermost.feature.guest_accounts") - PaidFeatureCustomUsergroups = MattermostFeature("mattermost.feature.custom_user_groups") - PaidFeatureCreateMultipleTeams = MattermostFeature("mattermost.feature.create_multiple_teams") - PaidFeatureStartcall = MattermostFeature("mattermost.feature.start_call") - PaidFeaturePlaybooksRetrospective = MattermostFeature("mattermost.feature.playbooks_retro") - PaidFeatureUnlimitedMessages = MattermostFeature("mattermost.feature.unlimited_messages") - PaidFeatureUnlimitedFileStorage = MattermostFeature("mattermost.feature.unlimited_file_storage") - PaidFeatureAllProfessionalfeatures = MattermostFeature("mattermost.feature.all_professional") - PaidFeatureAllEnterprisefeatures = MattermostFeature("mattermost.feature.all_enterprise") - UpgradeDowngradedWorkspace = MattermostFeature("mattermost.feature.upgrade_downgraded_workspace") - PluginFeature = MattermostFeature("mattermost.feature.plugin") + PaidFeatureGuestAccounts = MattermostFeature("mattermost.feature.guest_accounts") + PaidFeatureCustomUsergroups = MattermostFeature("mattermost.feature.custom_user_groups") + PaidFeatureCreateMultipleTeams = MattermostFeature("mattermost.feature.create_multiple_teams") + PaidFeatureStartcall = MattermostFeature("mattermost.feature.start_call") + PaidFeaturePlaybooksRetrospective = MattermostFeature("mattermost.feature.playbooks_retro") + PaidFeatureUnlimitedMessages = MattermostFeature("mattermost.feature.unlimited_messages") + PaidFeatureUnlimitedFileStorage = MattermostFeature("mattermost.feature.unlimited_file_storage") + PaidFeatureAllProfessionalfeatures = MattermostFeature("mattermost.feature.all_professional") + PaidFeatureAllEnterprisefeatures = MattermostFeature("mattermost.feature.all_enterprise") + UpgradeDowngradedWorkspace = MattermostFeature("mattermost.feature.upgrade_downgraded_workspace") + PluginFeature = MattermostFeature("mattermost.feature.plugin") + PaidFeatureHighlightWithoutNotification = MattermostFeature("mattermost.feature.highlight_without_notification") ) var validSKUs = map[string]struct{}{ @@ -33,16 +34,17 @@ var validSKUs = map[string]struct{}{ // These are the features a non admin would typically ping an admin about var paidFeatures = map[MattermostFeature]struct{}{ - PaidFeatureGuestAccounts: {}, - PaidFeatureCustomUsergroups: {}, - PaidFeatureCreateMultipleTeams: {}, - PaidFeatureStartcall: {}, - PaidFeaturePlaybooksRetrospective: {}, - PaidFeatureUnlimitedMessages: {}, - PaidFeatureUnlimitedFileStorage: {}, - PaidFeatureAllProfessionalfeatures: {}, - PaidFeatureAllEnterprisefeatures: {}, - UpgradeDowngradedWorkspace: {}, + PaidFeatureGuestAccounts: {}, + PaidFeatureCustomUsergroups: {}, + PaidFeatureCreateMultipleTeams: {}, + PaidFeatureStartcall: {}, + PaidFeaturePlaybooksRetrospective: {}, + PaidFeatureUnlimitedMessages: {}, + PaidFeatureUnlimitedFileStorage: {}, + PaidFeatureAllProfessionalfeatures: {}, + PaidFeatureAllEnterprisefeatures: {}, + UpgradeDowngradedWorkspace: {}, + PaidFeatureHighlightWithoutNotification: {}, } type NotifyAdminToUpgradeRequest struct { diff --git a/server/public/model/user.go b/server/public/model/user.go index fa1169c70d..ef42d5998c 100644 --- a/server/public/model/user.go +++ b/server/public/model/user.go @@ -36,6 +36,7 @@ const ( ChannelMentionsNotifyProp = "channel" CommentsNotifyProp = "comments" MentionKeysNotifyProp = "mention_keys" + HighlightsNotifyProp = "highlight_keys" CommentsNotifyNever = "never" CommentsNotifyRoot = "root" CommentsNotifyAny = "any" diff --git a/webapp/channels/src/components/__snapshots__/setting_item_min.test.tsx.snap b/webapp/channels/src/components/__snapshots__/setting_item_min.test.tsx.snap new file mode 100644 index 0000000000..7c79e274d1 --- /dev/null +++ b/webapp/channels/src/components/__snapshots__/setting_item_min.test.tsx.snap @@ -0,0 +1,75 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`components/SettingItemMin should match snapshot 1`] = ` +
+
+

+ title +

+ +
+
+ describe +
+
+`; + +exports[`components/SettingItemMin should match snapshot, on disableOpen to true 1`] = ` +
+
+

+ title +

+ +
+
+ describe +
+
+`; diff --git a/webapp/channels/src/components/__snapshots__/textbox.test.tsx.snap b/webapp/channels/src/components/__snapshots__/textbox.test.tsx.snap index 31aa7f6904..da8232ae7e 100644 --- a/webapp/channels/src/components/__snapshots__/textbox.test.tsx.snap +++ b/webapp/channels/src/components/__snapshots__/textbox.test.tsx.snap @@ -18,7 +18,6 @@ exports[`components/TextBox should match snapshot with additional, optional prop "hideUtilities": true, } } - mentionKeys={Array []} message="some test text" /> @@ -147,7 +146,6 @@ exports[`components/TextBox should match snapshot with required props 1`] = ` "hideUtilities": true, } } - mentionKeys={Array []} message="some test text" /> @@ -271,7 +269,6 @@ exports[`components/TextBox should throw error when new property is too long 1`] "hideUtilities": true, } } - mentionKeys={Array []} message="some test text that exceeds char limit" /> @@ -395,7 +392,6 @@ exports[`components/TextBox should throw error when value is too long 1`] = ` "hideUtilities": true, } } - mentionKeys={Array []} message="some test text that exceeds char limit" /> diff --git a/webapp/channels/src/components/toggle_modal_button/__snapshots__/toggle_modal_button.test.tsx.snap b/webapp/channels/src/components/__snapshots__/toggle_modal_button.test.tsx.snap similarity index 90% rename from webapp/channels/src/components/toggle_modal_button/__snapshots__/toggle_modal_button.test.tsx.snap rename to webapp/channels/src/components/__snapshots__/toggle_modal_button.test.tsx.snap index fbd01404d0..58c7b7f2be 100644 --- a/webapp/channels/src/components/toggle_modal_button/__snapshots__/toggle_modal_button.test.tsx.snap +++ b/webapp/channels/src/components/__snapshots__/toggle_modal_button.test.tsx.snap @@ -2,11 +2,6 @@ exports[`components/ToggleModalButton component should match snapshot 1`] = ` - + } className="" id="SystemRoleUsers" @@ -191,6 +193,7 @@ exports[`admin_console/system_role_users should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -236,6 +239,7 @@ exports[`admin_console/system_role_users should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -283,6 +287,7 @@ exports[`admin_console/system_role_users should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -328,6 +333,7 @@ exports[`admin_console/system_role_users should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -359,7 +365,7 @@ exports[`admin_console/system_role_users should match snapshot 1`] = ` exports[`admin_console/system_role_users should match snapshot with readOnly true 1`] = ` - + } className="" id="SystemRoleUsers" @@ -547,6 +555,7 @@ exports[`admin_console/system_role_users should match snapshot with readOnly tru "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -592,6 +601,7 @@ exports[`admin_console/system_role_users should match snapshot with readOnly tru "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -639,6 +649,7 @@ exports[`admin_console/system_role_users should match snapshot with readOnly tru "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -684,6 +695,7 @@ exports[`admin_console/system_role_users should match snapshot with readOnly tru "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", diff --git a/webapp/channels/src/components/admin_console/team_channel_settings/channel/details/__snapshots__/channel_groups.test.tsx.snap b/webapp/channels/src/components/admin_console/team_channel_settings/channel/details/__snapshots__/channel_groups.test.tsx.snap index ce09ad883c..afafa63097 100644 --- a/webapp/channels/src/components/admin_console/team_channel_settings/channel/details/__snapshots__/channel_groups.test.tsx.snap +++ b/webapp/channels/src/components/admin_console/team_channel_settings/channel/details/__snapshots__/channel_groups.test.tsx.snap @@ -3,7 +3,7 @@ exports[`admin_console/team_channel_settings/channel/ChannelGroups should match snapshot 1`] = ` - + } className="" id="channel_groups" diff --git a/webapp/channels/src/components/admin_console/team_channel_settings/channel/details/channel_members/__snapshots__/channel_members.test.tsx.snap b/webapp/channels/src/components/admin_console/team_channel_settings/channel/details/channel_members/__snapshots__/channel_members.test.tsx.snap index 5691dedfa9..d68b076ad6 100644 --- a/webapp/channels/src/components/admin_console/team_channel_settings/channel/details/channel_members/__snapshots__/channel_members.test.tsx.snap +++ b/webapp/channels/src/components/admin_console/team_channel_settings/channel/details/channel_members/__snapshots__/channel_members.test.tsx.snap @@ -3,7 +3,7 @@ exports[`admin_console/team_channel_settings/channel/ChannelMembers should match snapshot 1`] = ` - + } className="" id="channelMembers" @@ -215,6 +215,7 @@ exports[`admin_console/team_channel_settings/channel/ChannelMembers should match "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -253,6 +254,7 @@ exports[`admin_console/team_channel_settings/channel/ChannelMembers should match "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -291,6 +293,7 @@ exports[`admin_console/team_channel_settings/channel/ChannelMembers should match "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -314,7 +317,7 @@ exports[`admin_console/team_channel_settings/channel/ChannelMembers should match exports[`admin_console/team_channel_settings/channel/ChannelMembers should match snapshot loading no users 1`] = ` - + } className="" id="channelMembers" diff --git a/webapp/channels/src/components/admin_console/team_channel_settings/group/__snapshots__/group_row.test.tsx.snap b/webapp/channels/src/components/admin_console/team_channel_settings/group/__snapshots__/group_row.test.tsx.snap index fbba0f889c..66aabf7a88 100644 --- a/webapp/channels/src/components/admin_console/team_channel_settings/group/__snapshots__/group_row.test.tsx.snap +++ b/webapp/channels/src/components/admin_console/team_channel_settings/group/__snapshots__/group_row.test.tsx.snap @@ -17,7 +17,7 @@ exports[`admin_console/team_channel_settings/group/GroupRow should match snapsho - - +
- + } className="" id="team_groups" diff --git a/webapp/channels/src/components/admin_console/team_channel_settings/team/details/team_members/__snapshots__/team_members.test.tsx.snap b/webapp/channels/src/components/admin_console/team_channel_settings/team/details/team_members/__snapshots__/team_members.test.tsx.snap index a2bee43b1e..e69d8ee603 100644 --- a/webapp/channels/src/components/admin_console/team_channel_settings/team/details/team_members/__snapshots__/team_members.test.tsx.snap +++ b/webapp/channels/src/components/admin_console/team_channel_settings/team/details/team_members/__snapshots__/team_members.test.tsx.snap @@ -3,7 +3,7 @@ exports[`admin_console/team_channel_settings/team/TeamMembers should match snapshot 1`] = ` - + } className="" id="teamMembers" @@ -187,6 +187,7 @@ exports[`admin_console/team_channel_settings/team/TeamMembers should match snaps "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -225,6 +226,7 @@ exports[`admin_console/team_channel_settings/team/TeamMembers should match snaps "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -263,6 +265,7 @@ exports[`admin_console/team_channel_settings/team/TeamMembers should match snaps "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -286,7 +289,7 @@ exports[`admin_console/team_channel_settings/team/TeamMembers should match snaps exports[`admin_console/team_channel_settings/team/TeamMembers should match snapshot loading no users 1`] = ` - + } className="" id="teamMembers" diff --git a/webapp/channels/src/components/admin_console/user_grid/__snapshots__/user_grid.test.tsx.snap b/webapp/channels/src/components/admin_console/user_grid/__snapshots__/user_grid.test.tsx.snap index 7a0a2071a9..d1c11918ff 100644 --- a/webapp/channels/src/components/admin_console/user_grid/__snapshots__/user_grid.test.tsx.snap +++ b/webapp/channels/src/components/admin_console/user_grid/__snapshots__/user_grid.test.tsx.snap @@ -84,6 +84,7 @@ exports[`components/admin_console/user_grid/UserGrid should match snapshot with "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -128,6 +129,7 @@ exports[`components/admin_console/user_grid/UserGrid should match snapshot with "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -187,6 +189,7 @@ exports[`components/admin_console/user_grid/UserGrid should match snapshot with "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -234,6 +237,7 @@ exports[`components/admin_console/user_grid/UserGrid should match snapshot with "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -278,6 +282,7 @@ exports[`components/admin_console/user_grid/UserGrid should match snapshot with "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -337,6 +342,7 @@ exports[`components/admin_console/user_grid/UserGrid should match snapshot with "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -453,6 +459,7 @@ exports[`components/admin_console/user_grid/UserGrid should match snapshot with "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -505,6 +512,7 @@ exports[`components/admin_console/user_grid/UserGrid should match snapshot with "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -556,6 +564,7 @@ exports[`components/admin_console/user_grid/UserGrid should match snapshot with "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -603,6 +612,7 @@ exports[`components/admin_console/user_grid/UserGrid should match snapshot with "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -647,6 +657,7 @@ exports[`components/admin_console/user_grid/UserGrid should match snapshot with "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -706,6 +717,7 @@ exports[`components/admin_console/user_grid/UserGrid should match snapshot with "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -753,6 +765,7 @@ exports[`components/admin_console/user_grid/UserGrid should match snapshot with "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -797,6 +810,7 @@ exports[`components/admin_console/user_grid/UserGrid should match snapshot with "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -856,6 +870,7 @@ exports[`components/admin_console/user_grid/UserGrid should match snapshot with "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -972,6 +987,7 @@ exports[`components/admin_console/user_grid/UserGrid should match snapshot with "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -1016,6 +1032,7 @@ exports[`components/admin_console/user_grid/UserGrid should match snapshot with "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -1075,6 +1092,7 @@ exports[`components/admin_console/user_grid/UserGrid should match snapshot with "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", diff --git a/webapp/channels/src/components/channel_header_dropdown/__snapshots__/channel_header_dropdown.test.tsx.snap b/webapp/channels/src/components/channel_header_dropdown/__snapshots__/channel_header_dropdown.test.tsx.snap index 7017d5f504..b1c1727a7b 100644 --- a/webapp/channels/src/components/channel_header_dropdown/__snapshots__/channel_header_dropdown.test.tsx.snap +++ b/webapp/channels/src/components/channel_header_dropdown/__snapshots__/channel_header_dropdown.test.tsx.snap @@ -137,6 +137,7 @@ exports[`components/ChannelHeaderDropdown should match snapshot with no plugin i "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -213,6 +214,7 @@ exports[`components/ChannelHeaderDropdown should match snapshot with no plugin i "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -757,6 +759,7 @@ exports[`components/ChannelHeaderDropdown should match snapshot with no plugin i "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -966,6 +969,7 @@ exports[`components/ChannelHeaderDropdown should match snapshot with plugins 1`] "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -1042,6 +1046,7 @@ exports[`components/ChannelHeaderDropdown should match snapshot with plugins 1`] "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -1586,6 +1591,7 @@ exports[`components/ChannelHeaderDropdown should match snapshot with plugins 1`] "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", diff --git a/webapp/channels/src/components/channel_notifications_modal/components/__snapshots__/collapse_view.test.tsx.snap b/webapp/channels/src/components/channel_notifications_modal/components/__snapshots__/collapse_view.test.tsx.snap index e9522877c4..9e2879a20f 100644 --- a/webapp/channels/src/components/channel_notifications_modal/components/__snapshots__/collapse_view.test.tsx.snap +++ b/webapp/channels/src/components/channel_notifications_modal/components/__snapshots__/collapse_view.test.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`components/channel_notifications_modal/CollapseView should match snapshot, DESKTOP on collapsed view 1`] = ` - void}; diff --git a/webapp/channels/src/components/integrations/installed_outgoing_webhooks/__snapshots__/installed_outgoing_webhooks.test.tsx.snap b/webapp/channels/src/components/integrations/installed_outgoing_webhooks/__snapshots__/installed_outgoing_webhooks.test.tsx.snap index 6d606b18a4..fa6299d7d0 100644 --- a/webapp/channels/src/components/integrations/installed_outgoing_webhooks/__snapshots__/installed_outgoing_webhooks.test.tsx.snap +++ b/webapp/channels/src/components/integrations/installed_outgoing_webhooks/__snapshots__/installed_outgoing_webhooks.test.tsx.snap @@ -48,6 +48,7 @@ exports[`components/integrations/InstalledOutgoingWebhooks should match snapshot "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -155,6 +156,7 @@ exports[`components/integrations/InstalledOutgoingWebhooks should match snapshot "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", diff --git a/webapp/channels/src/components/markdown/index.ts b/webapp/channels/src/components/markdown/index.ts index ba562269f5..6fbfe1854d 100644 --- a/webapp/channels/src/components/markdown/index.ts +++ b/webapp/channels/src/components/markdown/index.ts @@ -24,7 +24,7 @@ function makeGetChannelNamesMap() { return createSelector( 'makeGetChannelNamesMap', getChannelNameToDisplayNameMap, - (state: GlobalState, props: OwnProps) => props && props.channelNamesMap, + (_: GlobalState, props: OwnProps) => props && props.channelNamesMap, (channelNamesMap, channelMentions) => { if (channelMentions) { return Object.assign({}, channelMentions, channelNamesMap); @@ -63,6 +63,7 @@ function makeMapStateToProps() { } const connector = connect(makeMapStateToProps); + export type PropsFromRedux = ConnectedProps; export default connector(Markdown); diff --git a/webapp/channels/src/components/markdown/markdown.tsx b/webapp/channels/src/components/markdown/markdown.tsx index 3ff9c62376..12c774f234 100644 --- a/webapp/channels/src/components/markdown/markdown.tsx +++ b/webapp/channels/src/components/markdown/markdown.tsx @@ -5,6 +5,8 @@ import React from 'react'; import type {PostImage, PostType} from '@mattermost/types/posts'; +import type {HighlightWithoutNotificationKey} from 'mattermost-redux/selectors/entities/users'; + import PostEditedIndicator from 'components/post_view/post_edited_indicator'; import type EmojiMap from 'utils/emoji_map'; @@ -53,6 +55,7 @@ export type OwnProps = { * An array of words that can be used to mention a user */ mentionKeys?: MentionKey[]; + highlightKeys?: HighlightWithoutNotificationKey[]; /** * Any extra props that should be passed into the image component @@ -92,6 +95,7 @@ function Markdown({ message = '', channelNamesMap, mentionKeys, + highlightKeys, imageProps, channelId, hasPluginTooltips, @@ -123,6 +127,7 @@ function Markdown({ autolinkedUrlSchemes, siteURL, mentionKeys, + highlightKeys, atMentions: true, channelNamesMap, proxyImages: hasImageProxy && proxyImages, diff --git a/webapp/channels/src/components/more_direct_channels/__snapshots__/more_direct_channels.test.tsx.snap b/webapp/channels/src/components/more_direct_channels/__snapshots__/more_direct_channels.test.tsx.snap index 1818a0f446..6073e4686f 100644 --- a/webapp/channels/src/components/more_direct_channels/__snapshots__/more_direct_channels.test.tsx.snap +++ b/webapp/channels/src/components/more_direct_channels/__snapshots__/more_direct_channels.test.tsx.snap @@ -95,6 +95,7 @@ exports[`components/MoreDirectChannels should exclude deleted users if there is "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -133,6 +134,7 @@ exports[`components/MoreDirectChannels should exclude deleted users if there is "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -171,6 +173,7 @@ exports[`components/MoreDirectChannels should exclude deleted users if there is "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -209,6 +212,7 @@ exports[`components/MoreDirectChannels should exclude deleted users if there is "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -247,6 +251,7 @@ exports[`components/MoreDirectChannels should exclude deleted users if there is "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -380,6 +385,7 @@ exports[`components/MoreDirectChannels should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -418,6 +424,7 @@ exports[`components/MoreDirectChannels should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -456,6 +463,7 @@ exports[`components/MoreDirectChannels should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -499,6 +507,7 @@ exports[`components/MoreDirectChannels should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -539,6 +548,7 @@ exports[`components/MoreDirectChannels should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", diff --git a/webapp/channels/src/components/post_markdown/index.ts b/webapp/channels/src/components/post_markdown/index.ts index f8f4894e8f..32345947c0 100644 --- a/webapp/channels/src/components/post_markdown/index.ts +++ b/webapp/channels/src/components/post_markdown/index.ts @@ -1,14 +1,15 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {connect} from 'react-redux'; +import {type ConnectedProps, connect} from 'react-redux'; import type {Channel} from '@mattermost/types/channels'; import type {Post} from '@mattermost/types/posts'; import {createSelector} from 'mattermost-redux/selectors/create_selector'; import {getChannel} from 'mattermost-redux/selectors/entities/channels'; -import {getConfig} from 'mattermost-redux/selectors/entities/general'; +import {getSubscriptionProduct} from 'mattermost-redux/selectors/entities/cloud'; +import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general'; import { getMyGroupMentionKeysForChannel, getMyGroupMentionKeys, @@ -16,15 +17,16 @@ import { import {getBool} from 'mattermost-redux/selectors/entities/preferences'; import {getCurrentTeam} from 'mattermost-redux/selectors/entities/teams'; import {getCurrentTimezone} from 'mattermost-redux/selectors/entities/timezone'; -import {getCurrentUserMentionKeys} from 'mattermost-redux/selectors/entities/users'; +import {getCurrentUserMentionKeys, getHighlightWithoutNotificationKeys} from 'mattermost-redux/selectors/entities/users'; import {canManageMembers} from 'utils/channel_utils'; import {Preferences} from 'utils/constants'; +import {isEnterpriseOrCloudOrSKUStarterFree} from 'utils/license_utils'; import type {MentionKey} from 'utils/text_formatting'; import type {GlobalState} from 'types/store'; -import PostMarkdown from './post_markdown'; +import PostMarkdown, {type OwnProps} from './post_markdown'; export function makeGetMentionKeysForPost(): ( state: GlobalState, @@ -54,18 +56,19 @@ export function makeGetMentionKeysForPost(): ( ); } -type OwnProps = { - channelId: string; - mentionKeys: MentionKey[]; - post?: Post; -}; - function makeMapStateToProps() { const getMentionKeysForPost = makeGetMentionKeysForPost(); return (state: GlobalState, ownProps: OwnProps) => { const channel = getChannel(state, ownProps.channelId); const currentTeam = getCurrentTeam(state) || {}; + + const license = getLicense(state); + const subscriptionProduct = getSubscriptionProduct(state); + + const config = getConfig(state); + const isEnterpriseReady = config.BuildEnterpriseReady === 'true'; + return { channel, currentTeam, @@ -73,11 +76,18 @@ function makeMapStateToProps() { hasPluginTooltips: Boolean(state.plugins.components.LinkTooltip), isUserCanManageMembers: channel && canManageMembers(state, channel), mentionKeys: getMentionKeysForPost(state, ownProps.post, channel), + highlightKeys: getHighlightWithoutNotificationKeys(state), isMilitaryTime: getBool(state, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.USE_MILITARY_TIME, false), timezone: getCurrentTimezone(state), hideGuestTags: getConfig(state).HideGuestTags === 'true', + isEnterpriseOrCloudOrSKUStarterFree: isEnterpriseOrCloudOrSKUStarterFree(license, subscriptionProduct, isEnterpriseReady), + isEnterpriseReady, }; }; } -export default connect(makeMapStateToProps)(PostMarkdown); +const connector = connect(makeMapStateToProps); + +export type PropsFromRedux = ConnectedProps; + +export default connector(PostMarkdown); diff --git a/webapp/channels/src/components/post_markdown/post_markdown.test.tsx b/webapp/channels/src/components/post_markdown/post_markdown.test.tsx index 33bf5d361b..ef617eb53c 100644 --- a/webapp/channels/src/components/post_markdown/post_markdown.test.tsx +++ b/webapp/channels/src/components/post_markdown/post_markdown.test.tsx @@ -10,11 +10,14 @@ import {Posts} from 'mattermost-redux/constants'; import {renderWithContext, screen} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; +import type {PluginComponent} from 'types/store/plugins'; + import PostMarkdown from './post_markdown'; describe('components/PostMarkdown', () => { const baseProps = { - imageProps: {}, + imageProps: {} as Record, + pluginHooks: [], message: 'message', post: TestHelper.getPostMock(), mentionKeys: [{key: 'a'}, {key: 'b'}, {key: 'c'}], @@ -22,6 +25,14 @@ describe('components/PostMarkdown', () => { channel: TestHelper.getChannelMock(), currentTeam: TestHelper.getTeamMock(), hideGuestTags: false, + isMilitaryTime: false, + timezone: '', + highlightKeys: [], + hasPluginTooltips: false, + isUserCanManageMembers: false, + isEnterpriseOrCloudOrSKUStarterFree: true, + isEnterpriseReady: false, + dispatch: jest.fn(), }; const state = {entities: { @@ -224,7 +235,7 @@ describe('components/PostMarkdown', () => { return updatedMessage + '!'; }, }, - ], + ] as PluginComponent[], }; renderWithContext(, state); expect(screen.queryByText('world', {exact: true})).not.toBeInTheDocument(); @@ -258,7 +269,7 @@ describe('components/PostMarkdown', () => { return post.message + '!'; }, }, - ], + ] as PluginComponent[], }; renderWithContext(, state); expect(screen.queryByText('world', {exact: true})).not.toBeInTheDocument(); diff --git a/webapp/channels/src/components/post_markdown/post_markdown.tsx b/webapp/channels/src/components/post_markdown/post_markdown.tsx index 2f9c5b97a8..ad77ddb43f 100644 --- a/webapp/channels/src/components/post_markdown/post_markdown.tsx +++ b/webapp/channels/src/components/post_markdown/post_markdown.tsx @@ -4,65 +4,45 @@ import memoize from 'memoize-one'; import React from 'react'; -import type {Channel} from '@mattermost/types/channels'; import type {Post} from '@mattermost/types/posts'; -import type {Team} from '@mattermost/types/teams'; import {Posts} from 'mattermost-redux/constants'; import Markdown from 'components/markdown'; -import type {MentionKey, TextFormattingOptions} from 'utils/text_formatting'; +import type {TextFormattingOptions} from 'utils/text_formatting'; import {renderReminderSystemBotMessage, renderSystemMessage} from './system_message_helpers'; -type Props = { +import {type PropsFromRedux} from './index'; - /* +export type OwnProps = { + + /** * Any extra props that should be passed into the image component */ - imageProps?: Record; + imageProps?: Record; - /* + /** * The post text to be rendered */ message: string; - /* + /** * The optional post for which this message is being rendered */ post?: Post; - - /* - * The id of the channel that this post is being rendered in - */ - channelId?: string; - channel: Channel; - currentTeam: Team; - options?: TextFormattingOptions; - pluginHooks?: Array>; - - /** - * Whether or not to place the LinkTooltip component inside links - */ - hasPluginTooltips?: boolean; - isUserCanManageMembers?: boolean; - mentionKeys: MentionKey[]; + channelId: string; /** * Whether or not to render the post edited indicator * @default true */ showPostEditedIndicator?: boolean; + options?: TextFormattingOptions; +}; - /** - * Whether the user prefers Military time - */ - isMilitaryTime?: boolean; - timezone?: string; - - hideGuestTags: boolean; -} +type Props = PropsFromRedux & OwnProps; export default class PostMarkdown extends React.PureComponent { static defaultProps = { @@ -82,11 +62,10 @@ export default class PostMarkdown extends React.PureComponent { }); render() { - let {message} = this.props; - const {post, mentionKeys} = this.props; + let message = this.props.message; - if (post) { - const renderedSystemMessage = renderSystemMessage(post, + if (this.props.post) { + const renderedSystemMessage = renderSystemMessage(this.props.post, this.props.currentTeam, this.props.channel, this.props.hideGuestTags, @@ -98,39 +77,45 @@ export default class PostMarkdown extends React.PureComponent { } } - if (post && post.type === Posts.POST_TYPES.REMINDER) { - const renderedSystemBotMessage = renderReminderSystemBotMessage(post, this.props.currentTeam); + if (this.props.post && this.props.post.type === Posts.POST_TYPES.REMINDER) { + const renderedSystemBotMessage = renderReminderSystemBotMessage(this.props.post, this.props.currentTeam); return
{renderedSystemBotMessage}
; } - // Proxy images if we have an image proxy and the server hasn't already rewritten the post's image URLs. - const proxyImages = !post || !post.message_source || post.message === post.message_source; - const channelNamesMap = post && post.props && post.props.channel_mentions; + // Proxy images if we have an image proxy and the server hasn't already rewritten the this.props.post's image URLs. + const proxyImages = !this.props.post || !this.props.post.message_source || this.props.post.message === this.props.post.message_source; + const channelNamesMap = this.props.post && this.props.post.props && this.props.post.props.channel_mentions; this.props.pluginHooks?.forEach((o) => { - if (o && o.hook && post) { - message = o.hook(post, message); + if (o && o.hook && this.props.post) { + message = o.hook(this.props.post, message); } }); let mentionHighlight = this.props.options?.mentionHighlight; - if (post && post.props) { - mentionHighlight = !post.props.mentionHighlightDisabled; + if (this.props.post && this.props.post.props) { + mentionHighlight = !this.props.post.props.mentionHighlightDisabled; } const options = this.getOptions( this.props.options, - post?.props?.disable_group_highlight === true, + this.props.post?.props?.disable_group_highlight === true, mentionHighlight, - post?.edit_at, + this.props.post?.edit_at, ); + let highlightKeys; + if (!this.props.isEnterpriseOrCloudOrSKUStarterFree && this.props.isEnterpriseReady) { + highlightKeys = this.props.highlightKeys; + } + return ( { options={options} post={post} channelId={post.channel_id} - mentionKeys={[]} showPostEditedIndicator={this.props.showPostEditedIndicator} />
diff --git a/webapp/channels/src/components/profile_popover/__snapshots__/profile_popover.test.tsx.snap b/webapp/channels/src/components/profile_popover/__snapshots__/profile_popover.test.tsx.snap index b7d3187bc8..38cbda81e8 100644 --- a/webapp/channels/src/components/profile_popover/__snapshots__/profile_popover.test.tsx.snap +++ b/webapp/channels/src/components/profile_popover/__snapshots__/profile_popover.test.tsx.snap @@ -139,6 +139,7 @@ exports[`components/ProfilePopover should disable start call button when user is "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -197,7 +198,7 @@ exports[`components/ProfilePopover should disable start call button when user is } >
- - +
- - +
- - +
- - +
@@ -1632,6 +1645,7 @@ exports[`components/ProfilePopover should match snapshot when calls are disabled "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -1802,6 +1816,7 @@ exports[`components/ProfilePopover should match snapshot with custom status 1`] "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -1898,7 +1913,7 @@ exports[`components/ProfilePopover should match snapshot with custom status 1`] } >
- - +
- - +
- - +
- - +
- - - - - + /> + + + + @@ -4430,6 +4399,7 @@ exports[`components/ProfilePopover should show the start call button when isCall "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -4511,6 +4481,7 @@ exports[`components/ProfilePopover should show the start call button when isCall "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", diff --git a/webapp/channels/src/components/setting_item.tsx b/webapp/channels/src/components/setting_item.tsx index 245702eae7..11bf69c480 100644 --- a/webapp/channels/src/components/setting_item.tsx +++ b/webapp/channels/src/components/setting_item.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type {ReactNode, RefObject} from 'react'; import SettingItemMin from 'components/setting_item_min'; -import type SettingItemMinComponent from 'components/setting_item_min/setting_item_min'; +import type SettingItemMinComponent from 'components/setting_item_min'; type Props = { @@ -27,36 +27,33 @@ type Props = { /** * The setting UI when it is maximized (open) */ - max: ReactNode | null; + max: ReactNode; // Props to pass through for SettingItemMin updateSection: (section: string) => void; title?: ReactNode; - disableOpen?: boolean; + isDisabled?: boolean; describe?: ReactNode; + + /** + * Replacement in place of edit button when the setting (in collapsed mode) is disabled + */ + collapsedEditButtonWhenDisabled?: ReactNode; } + export default class SettingItem extends React.PureComponent { minRef: RefObject; - static defaultProps = { - infoPosition: 'bottom', - saving: false, - section: '', - containerStyle: '', - }; - constructor(props: Props) { super(props); + this.minRef = React.createRef(); } - focusEditButton(): void { - this.minRef.current?.focus(); - } - componentDidUpdate(prevProps: Props) { - if (prevProps.active && !this.props.active && this.props.areAllSectionsInactive) { - this.focusEditButton(); + // We want to bring back focus to the edit button when the section is opened and then closed along with all sections are closed + if (!this.props.active && prevProps.active && this.props.areAllSectionsInactive) { + this.minRef.current?.focus(); } } @@ -67,12 +64,13 @@ export default class SettingItem extends React.PureComponent { return ( ); } diff --git a/webapp/channels/src/components/setting_item_min/setting_item_min.test.tsx b/webapp/channels/src/components/setting_item_min.test.tsx similarity index 82% rename from webapp/channels/src/components/setting_item_min/setting_item_min.test.tsx rename to webapp/channels/src/components/setting_item_min.test.tsx index de8a4a178a..29a627dc46 100644 --- a/webapp/channels/src/components/setting_item_min/setting_item_min.test.tsx +++ b/webapp/channels/src/components/setting_item_min.test.tsx @@ -36,26 +36,26 @@ describe('components/SettingItemMin', () => { expect(wrapper).toMatchSnapshot(); }); - test('should have called updateSection on handleUpdateSection with section', () => { + test('should have called updateSection on handleClick with section', () => { const updateSection = jest.fn(); const props = {...baseProps, updateSection}; const wrapper = shallow( , ); - wrapper.instance().handleUpdateSection({preventDefault: jest.fn()} as any); + wrapper.instance().handleClick({preventDefault: jest.fn()} as any); expect(updateSection).toHaveBeenCalled(); expect(updateSection).toHaveBeenCalledWith('section'); }); - test('should have called updateSection on handleUpdateSection with empty string', () => { + test('should have called updateSection on handleClick with empty string', () => { const updateSection = jest.fn(); const props = {...baseProps, updateSection, section: ''}; const wrapper = shallow( , ); - wrapper.instance().handleUpdateSection({preventDefault: jest.fn()} as any); + wrapper.instance().handleClick({preventDefault: jest.fn()} as any); expect(updateSection).toHaveBeenCalled(); expect(updateSection).toHaveBeenCalledWith(''); }); diff --git a/webapp/channels/src/components/setting_item_min.tsx b/webapp/channels/src/components/setting_item_min.tsx new file mode 100644 index 0000000000..ba3af422b5 --- /dev/null +++ b/webapp/channels/src/components/setting_item_min.tsx @@ -0,0 +1,118 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import classNames from 'classnames'; +import React, {type ReactNode, type MouseEvent} from 'react'; +import {FormattedMessage} from 'react-intl'; + +import EditIcon from 'components/widgets/icons/fa_edit_icon'; + +import {a11yFocus} from 'utils/utils'; + +interface Props { + + /** + * Settings title + */ + title: ReactNode; + + /** + * Option to disable opening the setting + */ + isDisabled?: boolean; + + /** + * Settings or tab section + */ + section: string; + + /** + * Function to update section + */ + updateSection: (section: string) => void; + + /** + * Settings description + */ + describe?: ReactNode; + + /** + * Replacement in place of edit button when the setting (in collapsed mode) is disabled + */ + collapsedEditButtonWhenDisabled?: ReactNode; +} + +export default class SettingItemMin extends React.PureComponent { + private edit: HTMLButtonElement | null = null; + + focus() { + a11yFocus(this.edit); + } + + private getEdit = (node: HTMLButtonElement) => { + this.edit = node; + }; + + handleClick = (e: MouseEvent) => { + if (this.props.isDisabled) { + return; + } + + e.preventDefault(); + this.props.updateSection(this.props.section); + }; + + render() { + let editButtonComponent: ReactNode; + + if (this.props.isDisabled) { + if (this.props.collapsedEditButtonWhenDisabled) { + editButtonComponent = this.props.collapsedEditButtonWhenDisabled; + } else { + editButtonComponent = null; + } + } else { + editButtonComponent = ( + + ); + } + + return ( +
+
+

+ {this.props.title} +

+ {editButtonComponent} +
+
+ {this.props.describe} +
+
+ ); + } +} diff --git a/webapp/channels/src/components/setting_item_min/__snapshots__/setting_item_min.test.tsx.snap b/webapp/channels/src/components/setting_item_min/__snapshots__/setting_item_min.test.tsx.snap deleted file mode 100644 index 6dcdb6d06b..0000000000 --- a/webapp/channels/src/components/setting_item_min/__snapshots__/setting_item_min.test.tsx.snap +++ /dev/null @@ -1,60 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`components/SettingItemMin should match snapshot 1`] = ` -
-
-

- title -

-
- -
-
-
- describe -
-
-`; - -exports[`components/SettingItemMin should match snapshot, on disableOpen to true 1`] = ` -
-
-

- title -

-
-
-`; diff --git a/webapp/channels/src/components/setting_item_min/index.ts b/webapp/channels/src/components/setting_item_min/index.ts deleted file mode 100644 index a0a9b0a247..0000000000 --- a/webapp/channels/src/components/setting_item_min/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import {connect} from 'react-redux'; - -import {getIsMobileView} from 'selectors/views/browser'; - -import type {GlobalState} from 'types/store'; - -import SettingItemMin from './setting_item_min'; - -function mapStateToProps(state: GlobalState) { - return { - isMobileView: getIsMobileView(state), - }; -} - -export default connect(mapStateToProps, null, null, {forwardRef: true})(SettingItemMin); diff --git a/webapp/channels/src/components/setting_item_min/setting_item_min.tsx b/webapp/channels/src/components/setting_item_min/setting_item_min.tsx deleted file mode 100644 index 201b264754..0000000000 --- a/webapp/channels/src/components/setting_item_min/setting_item_min.tsx +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import React from 'react'; -import type {ReactNode} from 'react'; -import {FormattedMessage} from 'react-intl'; - -import EditIcon from 'components/widgets/icons/fa_edit_icon'; - -import {a11yFocus} from 'utils/utils'; - -interface Props { - - /** - * Settings title - */ - title: ReactNode | string; - - /** - * Option to disable opening the setting - */ - disableOpen?: boolean; - - /** - * Settings or tab section - */ - section: string; - - /** - * Function to update section - */ - updateSection: (section: string) => void; - - /** - * Settings description - */ - describe?: ReactNode; - - isMobileView: boolean; -} - -export default class SettingItemMin extends React.PureComponent { - private edit: HTMLButtonElement | null = null; - - focus(): void { - a11yFocus(this.edit); - } - - private getEdit = (node: HTMLButtonElement) => { - this.edit = node; - }; - - handleUpdateSection = (e: React.MouseEvent) => { - e.preventDefault(); - this.props.updateSection(this.props.section); - }; - - render(): JSX.Element { - let editButton = null; - let describeSection = null; - - if (!this.props.disableOpen && this.props.isMobileView) { - editButton = ( -
- -
- ); - } else if (!this.props.disableOpen) { - editButton = ( -
- -
- ); - - describeSection = ( -
- {this.props.describe} -
- ); - } - - return ( -
-
-

- {this.props.title} -

- {editButton} -
- {describeSection} -
- ); - } -} diff --git a/webapp/channels/src/components/sidebar/__snapshots__/invite_members_button.test.tsx.snap b/webapp/channels/src/components/sidebar/__snapshots__/invite_members_button.test.tsx.snap index f62174b987..acdc673be5 100644 --- a/webapp/channels/src/components/sidebar/__snapshots__/invite_members_button.test.tsx.snap +++ b/webapp/channels/src/components/sidebar/__snapshots__/invite_members_button.test.tsx.snap @@ -37,7 +37,7 @@ exports[`components/sidebar/invite_members_button should match snapshot 1`] = ` } teamId="team_id2sss" > - - - - - + + Invite Members + + + + + diff --git a/webapp/channels/src/components/team_general_tab/__snapshots__/open_invite.test.tsx.snap b/webapp/channels/src/components/team_general_tab/__snapshots__/open_invite.test.tsx.snap index e2c2093bf7..de9e758967 100644 --- a/webapp/channels/src/components/team_general_tab/__snapshots__/open_invite.test.tsx.snap +++ b/webapp/channels/src/components/team_general_tab/__snapshots__/open_invite.test.tsx.snap @@ -102,7 +102,7 @@ exports[`components/TeamSettings/OpenInvite should match snapshot on active with `; exports[`components/TeamSettings/OpenInvite should match snapshot on non active allowing open invite 1`] = ` - - - - - - - - - - { > diff --git a/webapp/channels/src/components/toggle_modal_button/toggle_modal_button.test.tsx b/webapp/channels/src/components/toggle_modal_button.test.tsx similarity index 90% rename from webapp/channels/src/components/toggle_modal_button/toggle_modal_button.test.tsx rename to webapp/channels/src/components/toggle_modal_button.test.tsx index 2946f82307..cf487f6367 100644 --- a/webapp/channels/src/components/toggle_modal_button/toggle_modal_button.test.tsx +++ b/webapp/channels/src/components/toggle_modal_button.test.tsx @@ -10,6 +10,11 @@ import {ModalIdentifiers} from 'utils/constants'; import ToggleModalButton from './toggle_modal_button'; +jest.mock('react-redux', () => ({ + ...jest.requireActual('react-redux') as typeof import('react-redux'), + useDispatch: () => jest.fn(), +})); + class TestModal extends React.PureComponent { render() { return ( @@ -33,7 +38,6 @@ describe('components/ToggleModalButton', () => { role='menuitem' modalId={ModalIdentifiers.DELETE_CHANNEL} dialogType={TestModal} - actions={{openModal: () => true}} > (modalData: ModalData

) => void; - }; }; -const ToggleModalButton = ({ariaLabel, children, modalId, dialogType, dialogProps = {}, onClick, className = '', showUnread, disabled, id, actions, role}: Props) => { +const ToggleModalButton = ({ + ariaLabel, + children, + modalId, + dialogType, + dialogProps = {}, + onClick, + className = '', + showUnread, + disabled, + id, + role, +}: Props) => { const intl = useIntl(); + const dispatch = useDispatch(); + const show = (e: MouseEvent) => { if (e) { e.preventDefault(); @@ -38,7 +49,7 @@ const ToggleModalButton = ({ariaLabel, children, modalId, dialogType, dialogProp dialogType, }; - actions.openModal(modalData); + dispatch(openModal(modalData)); }; const ariaLabelElement = ariaLabel ? intl.formatMessage({ diff --git a/webapp/channels/src/components/toggle_modal_button/index.ts b/webapp/channels/src/components/toggle_modal_button/index.ts deleted file mode 100644 index 323edfa3ac..0000000000 --- a/webapp/channels/src/components/toggle_modal_button/index.ts +++ /dev/null @@ -1,20 +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 {openModal} from 'actions/views/modals'; - -import ToggleModalButton from './toggle_modal_button'; - -function mapDispatchToProps(dispatch: Dispatch) { - return { - actions: bindActionCreators({ - openModal, - }, dispatch), - }; -} - -export default connect(null, mapDispatchToProps)(ToggleModalButton); diff --git a/webapp/channels/src/components/user_group_popover/__snapshots__/user_group_popover.test.tsx.snap b/webapp/channels/src/components/user_group_popover/__snapshots__/user_group_popover.test.tsx.snap index f0ad2f9129..2db6ce2566 100644 --- a/webapp/channels/src/components/user_group_popover/__snapshots__/user_group_popover.test.tsx.snap +++ b/webapp/channels/src/components/user_group_popover/__snapshots__/user_group_popover.test.tsx.snap @@ -400,6 +400,7 @@ exports[`component/user_group_popover should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -441,6 +442,7 @@ exports[`component/user_group_popover should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -482,6 +484,7 @@ exports[`component/user_group_popover should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -523,6 +526,7 @@ exports[`component/user_group_popover should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -564,6 +568,7 @@ exports[`component/user_group_popover should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -605,6 +610,7 @@ exports[`component/user_group_popover should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -646,6 +652,7 @@ exports[`component/user_group_popover should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -687,6 +694,7 @@ exports[`component/user_group_popover should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -728,6 +736,7 @@ exports[`component/user_group_popover should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -769,6 +778,7 @@ exports[`component/user_group_popover should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -810,6 +820,7 @@ exports[`component/user_group_popover should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -851,6 +862,7 @@ exports[`component/user_group_popover should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -892,6 +904,7 @@ exports[`component/user_group_popover should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -933,6 +946,7 @@ exports[`component/user_group_popover should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -974,6 +988,7 @@ exports[`component/user_group_popover should match snapshot 1`] = ` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", diff --git a/webapp/channels/src/components/user_group_popover/group_member_list/__snapshots__/group_member_list.test.tsx.snap b/webapp/channels/src/components/user_group_popover/group_member_list/__snapshots__/group_member_list.test.tsx.snap index 60b65bee46..99103e0c26 100644 --- a/webapp/channels/src/components/user_group_popover/group_member_list/__snapshots__/group_member_list.test.tsx.snap +++ b/webapp/channels/src/components/user_group_popover/group_member_list/__snapshots__/group_member_list.test.tsx.snap @@ -91,6 +91,7 @@ exports[`component/user_group_popover/group_member_list should match snapshot 1` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -132,6 +133,7 @@ exports[`component/user_group_popover/group_member_list should match snapshot 1` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -173,6 +175,7 @@ exports[`component/user_group_popover/group_member_list should match snapshot 1` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -214,6 +217,7 @@ exports[`component/user_group_popover/group_member_list should match snapshot 1` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", @@ -255,6 +259,7 @@ exports[`component/user_group_popover/group_member_list should match snapshot 1` "desktop_sound": "false", "email": "false", "first_name": "false", + "highlight_keys": "", "mark_unread": "mention", "mention_keys": "", "push": "none", diff --git a/webapp/channels/src/components/user_settings/advanced/join_leave_section/__snapshots__/join_leave_section.test.tsx.snap b/webapp/channels/src/components/user_settings/advanced/join_leave_section/__snapshots__/join_leave_section.test.tsx.snap index 7cc88a05e0..b4d1a35345 100644 --- a/webapp/channels/src/components/user_settings/advanced/join_leave_section/__snapshots__/join_leave_section.test.tsx.snap +++ b/webapp/channels/src/components/user_settings/advanced/join_leave_section/__snapshots__/join_leave_section.test.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`components/user_settings/advanced/JoinLeaveSection should match snapshot 1`] = ` - } - infoPosition="bottom" max={null} - saving={false} section="name_format" title={ } - infoPosition="bottom" max={null} - saving={false} section="lastactive" title={ } - saving={false} section="channel_display_mode" title={ } - saving={false} section="languages" title={ } - infoPosition="bottom" max={null} - saving={false} section="name_format" title={ } - infoPosition="bottom" max={null} - saving={false} section="lastactive" title={ } - saving={false} section="click_to_reply" title={ } - saving={false} section="languages" title={ } - saving={false} section="clock" title={ } - infoPosition="bottom" max={null} - saving={false} section="name_format" title={ } - infoPosition="bottom" max={null} - saving={false} section="lastactive" title={ } - saving={false} section="languages" title={ } - infoPosition="bottom" max={null} - saving={false} section="name_format" title={ } - infoPosition="bottom" max={null} - saving={false} section="lastactive" title={ } - saving={false} section="collapse" title={ } - saving={false} section="languages" title={ } - infoPosition="bottom" max={null} - saving={false} section="name_format" title={ } - infoPosition="bottom" max={null} - saving={false} section="lastactive" title={ } - saving={false} section="languages" title={ } - infoPosition="bottom" max={null} - saving={false} section="name_format" title={ } - infoPosition="bottom" max={null} - saving={false} section="lastactive" title={ } - saving={false} section="languages" title={ } - infoPosition="bottom" max={null} - saving={false} section="name_format" title={ } - infoPosition="bottom" max={null} - saving={false} section="lastactive" title={ } - saving={false} section="linkpreview" title={ } - saving={false} section="languages" title={ } - infoPosition="bottom" max={null} - saving={false} section="name_format" title={ } - infoPosition="bottom" max={null} - saving={false} section="lastactive" title={ } - saving={false} section="message_display" title={ } - saving={false} section="languages" title={ } - infoPosition="bottom" max={null} - saving={false} section="name_format" title={ } - infoPosition="bottom" max={null} - saving={false} section="lastactive" title={ } - saving={false} section="languages" title={ } - infoPosition="bottom" max={null} - saving={false} section="name_format" title={ } - infoPosition="bottom" max={null} - saving={false} section="lastactive" title={ } - saving={false} section="languages" title={ } - infoPosition="bottom" max={null} - saving={false} section="name_format" title={ } - infoPosition="bottom" max={null} - saving={false} section="lastactive" title={ } - saving={false} section="languages" title={ } - infoPosition="bottom" max={null} - saving={false} section="name_format" title={ } - infoPosition="bottom" max={null} - saving={false} section="lastactive" title={ } - saving={false} section="languages" title={ } - infoPosition="bottom" max={null} - saving={false} section="name_format" title={ } - infoPosition="bottom" max={null} - saving={false} section="lastactive" title={ } - saving={false} section="languages" title={ } - infoPosition="bottom" max={null} - saving={false} section="name_format" title={ } - saving={false} section="languages" title={

Desktop Notifications

- + + Edit +

Email Notifications

- + + Edit +

Mobile Push Notifications

- + + Edit +

- Keywords that trigger Notifications + Keywords That Trigger Notifications

- +
+ "@some-user" +
+
+
+
+
+

+ Keywords That Get Highlighted (Without Notifications) +

+ +
+
+ None +
+
+
+
+
+
+
+ , + "container":
+
+ +