diff --git a/components/admin_console/custom_plugin_settings/custom_plugin_settings.jsx b/components/admin_console/custom_plugin_settings/custom_plugin_settings.jsx
index 1681a800b841..2567d2fb8f8b 100644
--- a/components/admin_console/custom_plugin_settings/custom_plugin_settings.jsx
+++ b/components/admin_console/custom_plugin_settings/custom_plugin_settings.jsx
@@ -10,7 +10,10 @@ export default class CustomPluginSettings extends SchemaAdminSettings {
}
componentWillReceiveProps(nextProps) {
- if (!this.props.schema && nextProps.schema) {
+ const id = this.props.schema ? this.props.schema.id : '';
+ const nextId = nextProps.schema ? nextProps.schema.id : '';
+
+ if ((!this.props.schema && nextProps.schema) || (id !== nextId)) {
this.setState(this.getStateFromConfig(nextProps.config, nextProps.schema));
}
}
diff --git a/components/announcement_bar/announcement_bar.jsx b/components/announcement_bar/announcement_bar.jsx
index 52bf4b2e1357..bba7ede9cb94 100644
--- a/components/announcement_bar/announcement_bar.jsx
+++ b/components/announcement_bar/announcement_bar.jsx
@@ -56,7 +56,17 @@ export default class AnnouncementBar extends React.PureComponent {
this.setInitialError();
- this.state = this.getState();
+ this.state = this.getState(props);
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (nextProps.enableBanner !== this.props.enableBanner ||
+ nextProps.bannerText !== this.props.bannerText ||
+ nextProps.bannerColor !== this.props.bannerColor ||
+ nextProps.bannerTextColor !== this.props.bannerTextColor ||
+ nextProps.allowBannerDismissal !== this.props.allowBannerDismissal) {
+ this.setState(this.getState(nextProps));
+ }
}
setInitialError = () => {
@@ -85,17 +95,17 @@ export default class AnnouncementBar extends React.PureComponent {
}
}
- getState() {
+ getState(props = this.props) {
const error = ErrorStore.getLastError();
if (error && error.message) {
return {message: error.message, color: null, textColor: null, type: error.type, allowDismissal: true};
}
- const bannerText = this.props.bannerText || '';
- const allowDismissal = this.props.allowBannerDismissal;
- const bannerDismissed = localStorage.getItem(StoragePrefixes.ANNOUNCEMENT + this.props.bannerText);
+ const bannerText = props.bannerText || '';
+ const allowDismissal = props.allowBannerDismissal;
+ const bannerDismissed = localStorage.getItem(StoragePrefixes.ANNOUNCEMENT + props.bannerText);
- if (this.props.enableBanner &&
+ if (props.enableBanner &&
bannerText.length > 0 &&
(!bannerDismissed || !allowDismissal)
) {
@@ -103,8 +113,8 @@ export default class AnnouncementBar extends React.PureComponent {
Utils.removePrefixFromLocalStorage(StoragePrefixes.ANNOUNCEMENT);
return {
message: bannerText,
- color: this.props.bannerColor,
- textColor: this.props.bannerTextColor,
+ color: props.bannerColor,
+ textColor: props.bannerTextColor,
type: BAR_ANNOUNCEMENT_TYPE,
allowDismissal,
};
diff --git a/components/post_view/post_list.jsx b/components/post_view/post_list.jsx
index 944895863c90..b90bce63331e 100644
--- a/components/post_view/post_list.jsx
+++ b/components/post_view/post_list.jsx
@@ -267,16 +267,15 @@ export default class PostList extends React.PureComponent {
}
const messageSeparator = this.refs.newMessageSeparator;
- if (messageSeparator) {
- // Scroll to new message indicator since we have unread posts
+
+ // Scroll to new message indicator since we have unread posts and we can't show every new post in the screen
+ if (messageSeparator && (postList.scrollHeight - messageSeparator.offsetTop) > postList.clientHeight) {
messageSeparator.scrollIntoView();
- if (!this.checkBottom()) {
- this.setUnreadsBelow(posts, this.props.currentUserId);
- }
+ this.setUnreadsBelow(posts, this.props.currentUserId);
return true;
}
- // Scroll to bottom since we don't have unread posts
+ // Scroll to bottom since we don't have unread posts or we can show every new post in the screen
postList.scrollTop = postList.scrollHeight;
this.atBottom = true;
return true;
diff --git a/components/suggestion/switch_channel_provider.jsx b/components/suggestion/switch_channel_provider.jsx
index ebccbcebf08e..e9523d0f044a 100644
--- a/components/suggestion/switch_channel_provider.jsx
+++ b/components/suggestion/switch_channel_provider.jsx
@@ -17,7 +17,12 @@ import {getMyChannelMemberships} from 'mattermost-redux/selectors/entities/commo
import {getBool} from 'mattermost-redux/selectors/entities/preferences';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
-import {searchProfiles, getUserIdsInChannels, getUser} from 'mattermost-redux/selectors/entities/users';
+import {
+ getCurrentUserId,
+ getUserIdsInChannels,
+ getUser,
+ searchProfiles,
+} from 'mattermost-redux/selectors/entities/users';
import GlobeIcon from 'components/svg/globe_icon';
import LockIcon from 'components/svg/lock_icon';
@@ -240,9 +245,10 @@ export default class SwitchChannelProvider extends Provider {
}
const users = Object.assign([], searchProfiles(state, channelPrefix, false)).concat(usersFromServer.users);
+ const currentUserId = getCurrentUserId(state);
store.dispatch({
type: UserTypes.RECEIVED_PROFILES_LIST,
- data: users,
+ data: users.filter((user) => user.id !== currentUserId),
});
const channels = getChannelsInCurrentTeam(state).concat(getDirectChannels(state)).concat(channelsFromServer);
this.formatChannelsAndDispatch(channelPrefix, suggestionId, channels, users);
diff --git a/components/system_notice/notices.jsx b/components/system_notice/notices.jsx
index cc8a1d45b745..5366d084b1ec 100644
--- a/components/system_notice/notices.jsx
+++ b/components/system_notice/notices.jsx
@@ -21,7 +21,7 @@ export default [
title: (
),
icon: mattermostIcon,
diff --git a/components/system_notice/system_notice.jsx b/components/system_notice/system_notice.jsx
index 328a13a14d6c..6e4d425ca478 100644
--- a/components/system_notice/system_notice.jsx
+++ b/components/system_notice/system_notice.jsx
@@ -93,6 +93,19 @@ export default class SystemNotice extends React.PureComponent {
return null;
}
+ let visibleMessage;
+ if (notice.adminOnly) {
+ visibleMessage = (
+
+
+
+
+ );
+ }
+
return (
{notice.body}
+ {visibleMessage}
+
+
+
+
diff --git a/tests/components/announcement_bar.test.jsx b/tests/components/announcement_bar.test.jsx
new file mode 100644
index 000000000000..008b6d559d4b
--- /dev/null
+++ b/tests/components/announcement_bar.test.jsx
@@ -0,0 +1,97 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React from 'react';
+import {shallow} from 'enzyme';
+import 'tests/helpers/localstorage.jsx';
+
+import AnnouncementBar from 'components/announcement_bar/announcement_bar.jsx';
+
+describe('components/AnnouncementBar', () => {
+ const baseProps = {
+ isLoggedIn: true,
+ canViewSystemErrors: false,
+ canViewAPIv3Banner: false,
+ licenseId: '',
+ siteURL: '',
+ sendEmailNotifications: true,
+ bannerText: 'Banner text',
+ allowBannerDismissal: true,
+ enableBanner: true,
+ bannerColor: 'green',
+ bannerTextColor: 'black',
+ enableSignUpWithGitLab: false,
+ };
+
+ test('should match snapshot, bar showing', () => {
+ const props = baseProps;
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ test('should match snapshot, bar not showing', () => {
+ const props = {...baseProps, enableBanner: false};
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ test('should match snapshot, bar showing, no dismissal', () => {
+ const props = {...baseProps, allowBannerDismissal: false};
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ test('should match snapshot, props change', () => {
+ const props = baseProps;
+ const wrapper = shallow(
+
+ );
+
+ expect(wrapper).toMatchSnapshot();
+
+ const newProps = {...baseProps, bannerColor: 'yellow', bannerTextColor: 'red'};
+ wrapper.setProps(newProps);
+ expect(wrapper).toMatchSnapshot();
+
+ newProps.allowBannerDismissal = false;
+ wrapper.setProps(newProps);
+ expect(wrapper).toMatchSnapshot();
+
+ newProps.enableBanner = false;
+ wrapper.setProps(newProps);
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ test('should match snapshot, dismissal', () => {
+ const props = baseProps;
+ const wrapper = shallow(
+
+ );
+
+ // Banner should show
+ expect(wrapper).toMatchSnapshot();
+
+ // Banner should hide
+ wrapper.find('a').simulate('click');
+ expect(wrapper).toMatchSnapshot();
+
+ // Banner should remain hidden
+ const newProps = {...baseProps, bannerColor: 'yellow', bannerTextColor: 'red'};
+ wrapper.setProps(newProps);
+ expect(wrapper).toMatchSnapshot();
+
+ // Banner should return
+ newProps.bannerText = 'Some new text';
+ wrapper.setProps(newProps);
+ expect(wrapper).toMatchSnapshot();
+ });
+});
diff --git a/tests/helpers/localstorage.jsx b/tests/helpers/localstorage.jsx
new file mode 100644
index 000000000000..9a250c0a2b19
--- /dev/null
+++ b/tests/helpers/localstorage.jsx
@@ -0,0 +1,27 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+// Based on https://stackoverflow.com/a/41434763
+class LocalStorageMock {
+ constructor() {
+ this.store = {};
+ }
+
+ clear() {
+ this.store = {};
+ }
+
+ getItem(key) {
+ return this.store[key] || null;
+ }
+
+ setItem(key, value) {
+ this.store[key] = value.toString();
+ }
+
+ removeItem(key) {
+ delete this.store[key];
+ }
+}
+
+global.localStorage = new LocalStorageMock();