Skip to content

Commit

Permalink
Channel Sidebar Organization (mattermost#4999)
Browse files Browse the repository at this point in the history
* [MM-20881] Moved out old sidebar and added setting to activate new one (mattermost#4553)

* Redux update

* Update redux commit

* Redux merge

* Base channel sidebar architecture (mattermost#4706)

Automatic Merge

* [MM-21911] Moved favicon and titlebar update to its own component (mattermost#4811)

* [MM-21911] Moved favicon and titlebar update to its own component

* Moved favicon/title tests to their own file

* PR feedback

* [MM-21907][MM-21908] Unread Handling and Scrolling (mattermost#4799)

* WIP

* Barely working sidebar

* Temp fix so you can use the sidebar better

* Lint and type fixes

* Unread badge and bolding class added

* Split up sidebar channel types into different components

* Lint/type fixes

* WIP

* PR feedback

* Lint fix

* Unread indicators and scrolling!

* Auto scrolling when changing teams/when channel is in view

* Lint and type fixes

* Another type fix

* Removing unnecessary styling

* PR feedback

* package fix

* Reverted to regular scrollbars

* Whoops, left my dumb test styling in :P

* PR feedback

* aaa

* [MM-21909] Keyboard shortcuts and a11y-controller classes (mattermost#4821)

* WIP

* Barely working sidebar

* Temp fix so you can use the sidebar better

* Lint and type fixes

* Unread badge and bolding class added

* Split up sidebar channel types into different components

* Lint/type fixes

* WIP

* PR feedback

* Lint fix

* Unread indicators and scrolling!

* Auto scrolling when changing teams/when channel is in view

* Lint and type fixes

* Another type fix

* Removing unnecessary styling

* PR feedback

* package fix

* Added a11y classes to new sidebar

* Keyboard shortcuts

* Reverted to regular scrollbars

* Whoops, left my dumb test styling in :P

* PR feedback

* Removed isSwitchingChannel

* aaa

* Revert redux commit until tests are fixed

* [MM-22290] Collapsible Category Logic (mattermost#4873)

* Barebone collapsing of categories (no saving)

* Take into account unreads and if the channel is the current visible channel

* Reactifying and cleaning up a couple small things

* More cleanup

* Clarity

* Added new style sheet for new sidebar, and the animation for collapsing categories

* Lint/type fixes and accessibility

* Moved a11y section to just the title, for better reading and navigation

* Removing code that won't be used

* Merge'd

* Category name is repeated

* PR feedback

* Removing unnecessary code

* [MM-21910] Modal Handling for new Sidebar (mattermost#4847)

* WIP

* Barely working sidebar

* Temp fix so you can use the sidebar better

* Lint and type fixes

* Unread badge and bolding class added

* Split up sidebar channel types into different components

* Lint/type fixes

* WIP

* PR feedback

* Lint fix

* Unread indicators and scrolling!

* Auto scrolling when changing teams/when channel is in view

* Lint and type fixes

* Another type fix

* Removing unnecessary styling

* PR feedback

* package fix

* Added a11y classes to new sidebar

* Keyboard shortcuts

* Reverted to regular scrollbars

* Whoops, left my dumb test styling in :P

* Moved modals over to sidebar

* PR feedback

* Removed isSwitchingChannel

* aaa

* Modals functional

* PR feedback

* Merge'd

* MM-22114 Add view state for collapsed categories and unread filter (mattermost#4914)

* [MM-22291] Additional functionality from old sidebar (mattermost#4860)

* [MM-22291] Additional functionality from old sidebar

* PR feedback

* PR feedback

* Lank bline

* Lint again

* PR feedback

* Merge'd

* PR feedback

* MM-21584 Replace removeItem call with setItem (mattermost#5003)

* Update redux commit

* Merging of CSS styles into new sidebar code (mattermost#4998)

* [MM-22291] Additional functionality from old sidebar

* PR feedback

* PR feedback

* Lank bline

* Lint again

* PR feedback

* Merge'd

* Actual functioning sidebar!

* Using react-custom-scrollbars again and fixed a few styling issues

* PR feedback

* Lint fixes

Co-authored-by: mattermod <[email protected]>

* [MM-22478] Unread Filtering and Create New Channel Menu (mattermost#5005)

* [MM-22291] Additional functionality from old sidebar

* PR feedback

* PR feedback

* Lank bline

* Lint again

* PR feedback

* Merge'd

* Actual functioning sidebar!

* Using react-custom-scrollbars again and fixed a few styling issues

* PR feedback

* Added base structure

* Unread filter and add channel dropdown design and some functionality

* Unread filtering and collapsible category logic hookup to redux

* Lint fixes

* Added translations

* Disable collapse when unread filter is on

* Merge'd

* PR feedback

* And more

Co-authored-by: mattermod <[email protected]>

* CSS fix for single team instances

* PR feedback and some more missing redux hookups

* Mobile/Desktop Sidebar View + Restored Team Sidebar and Header (mattermost#5016)

* [MM-22291] Additional functionality from old sidebar

* PR feedback

* PR feedback

* Lank bline

* Lint again

* PR feedback

* Merge'd

* Actual functioning sidebar!

* Using react-custom-scrollbars again and fixed a few styling issues

* PR feedback

* Lint fixes

* WIP

* Added back original team sidebar and sidebar header for Phase 1

* Mobile bar fits, still styling issues

* Style and lint fixes to make the sidebar work properly

* Desktop and other mobile fixes

* Merge'd

Co-authored-by: mattermod <[email protected]>

* Fixed labels to match spec

* Snapshot update

* Channel Switcher + History Button Style (mattermost#5023)

* [MM-22291] Additional functionality from old sidebar

* PR feedback

* PR feedback

* Lank bline

* Lint again

* PR feedback

* Merge'd

* Actual functioning sidebar!

* Using react-custom-scrollbars again and fixed a few styling issues

* PR feedback

* Lint fixes

* Added channel switcher modal button and back/forward buttons

* Lint fixes and formatting

* Styled arrows and buttons, and mock back/forward

* Unmocking for Phase 1

* Removed unnecessary ms-filter

Co-authored-by: mattermod <[email protected]>

* Fixed so that the swap between old and new sidebars is instant, mobile view fixes

* Type fix

* Lint fix

* PR feedback for Eric/Harrison

* A bunch of alignment changes

* Another design fix

* [MM-23056] Analytics for new and old sidebar

* More alignment fixes

* Fixed letter cutoff issue and an alignment issue

* [MM-22834] Hide unused Account Settings options when the new sidebar is enabled

* Even more more alignment fixes, a test fix and some scrolling fixes

* Additional alignment and style fixes

* Alignment fixes for the history arrows

* Fixed a test and a couple small issues

* Added missing tooltips

* Fix for the team switching crashing issue

* Lint fix

* A lot of a11y fixes, still need more

* Lint and test fixes

* Added some missing aria-labels

* Fix crash issue mattermost#2

* Removed autoclose redirect since it can't fire

* Removed more dead code

* Redux update

* PR feedback

* Fixed a couple more small issues

* Fixed a redux issue

* More redux fixes and a bug fix

* Fixed a test

* Lint error

Co-authored-by: Harrison Healey <[email protected]>
Co-authored-by: mattermod <[email protected]>
  • Loading branch information
3 people committed Mar 18, 2020
1 parent da9604d commit 5af1795
Show file tree
Hide file tree
Showing 107 changed files with 5,010 additions and 636 deletions.
33 changes: 33 additions & 0 deletions actions/views/channel_sidebar.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import configureStore from 'store';

import {isCategoryCollapsedFromStorage} from 'selectors/views/channel_sidebar';
import {getPrefix} from 'utils/storage_utils';

import * as Actions from './channel_sidebar';

describe('setCategoryCollapsed', () => {
test('should save category expanded and category collapsed', async () => {
const category1 = 'category1';
const initialState = {
users: {
currentUserId: 'user1',
profiles: {
user1: {},
}
}
};

const store = await configureStore(initialState);

store.dispatch(Actions.setCategoryCollapsed(category1, true));

expect(isCategoryCollapsedFromStorage(getPrefix(store.getState()), store.getState().storage.storage, category1)).toBe(true);

store.dispatch(Actions.setCategoryCollapsed(category1, false));

expect(isCategoryCollapsedFromStorage(getPrefix(store.getState()), store.getState().storage.storage, category1)).toBe(false);
});
});
30 changes: 30 additions & 0 deletions actions/views/channel_sidebar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {setItem} from 'actions/storage';

import {ActionTypes, StoragePrefixes} from 'utils/constants';

export function collapseCategory(categoryId: string) {
return setItem(StoragePrefixes.CHANNEL_CATEGORY_COLLAPSED + categoryId, true);
}

export function expandCategory(categoryId: string) {
// You should be able to use removeItem here, but removeItem was not working at all
return setItem(StoragePrefixes.CHANNEL_CATEGORY_COLLAPSED + categoryId, false);
}

export function setCategoryCollapsed(categoryId: string, collapsed: boolean) {
if (collapsed) {
return collapseCategory(categoryId);
}

return expandCategory(categoryId);
}

export function setUnreadFilterEnabled(enabled: boolean) {
return {
type: ActionTypes.SET_UNREAD_FILTER_ENABLED,
enabled,
};
}
30 changes: 28 additions & 2 deletions components/admin_console/admin_definition.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4456,13 +4456,39 @@ const AdminDefinition = {
help_text_markdown: false,
isHidden: it.isnt(it.licensedForFeature('SAML')),
},
{
type: Constants.SettingsTypes.TYPE_DROPDOWN,
key: 'ServiceSettings.ExperimentalChannelSidebarOrganization',
label: t('admin.experimental.experimentalChannelSidebarOrganization.title'),
label_default: 'Experimental Sidebar Features',
help_text: t('admin.experimental.experimentalChannelSidebarOrganization.desc'),
help_text_default: 'When enabled, users can access experimental channel sidebar features, including collapsible sections and unreads filtering. If default on, this enabled the new sidebar features by default for all users on this server. Users can disable the features in **Account Settings > Sidebar > Experimental Sidebar Features**. If default off, users must enable the experimental sidebar features in Account Settings. [Learn more](!https://about.mattermost.com/default-sidebar/) or [give us feedback](!https://about.mattermost.com/default-sidebar-survey/)',
help_text_markdown: true,
options: [
{
value: 'disabled',
display_name: t('admin.experimental.experimentalChannelSidebarOrganization.disabled'),
display_name_default: 'Disabled',
},
{
value: 'default_on',
display_name: t('admin.experimental.experimentalChannelSidebarOrganization.default_on'),
display_name_default: 'Enabled (Default On)',
},
{
value: 'default_off',
display_name: t('admin.experimental.experimentalChannelSidebarOrganization.default_off'),
display_name_default: 'Enabled (Default Off)',
},
],
},
{
type: Constants.SettingsTypes.TYPE_BOOL,
key: 'ServiceSettings.ExperimentalChannelOrganization',
label: t('admin.experimental.experimentalChannelOrganization.title'),
label_default: 'Sidebar Organization:',
label_default: 'Channel Grouping and Sorting',
help_text: t('admin.experimental.experimentalChannelOrganization.desc'),
help_text_default: 'Enables channel sidebar organization options in **Account Settings > Sidebar > Channel grouping and sorting** including options for grouping unread channels, sorting channels by most recent post and combining all channel types into a single list.',
help_text_default: 'Enables channel sidebar organization options in **Account Settings > Sidebar > Channel grouping and sorting** including options for grouping unread channels, sorting channels by most recent post and combining all channel types into a single list. These settings are not available if **Account Settings > Sidebar > Experimental Sidebar Features** are enabled.',
help_text_markdown: true,
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ exports[`components/ChannelHeader should render active flagged posts 1`] = `
<div
aria-label="channel header region"
className="channel-header alt a11y__region"
data-a11y-sort-order="7"
data-a11y-sort-order="8"
data-channelid="channel_id"
id="channel-header"
role="banner"
Expand Down Expand Up @@ -213,7 +213,7 @@ exports[`components/ChannelHeader should render active mentions posts 1`] = `
<div
aria-label="channel header region"
className="channel-header alt a11y__region"
data-a11y-sort-order="7"
data-a11y-sort-order="8"
data-channelid="channel_id"
id="channel-header"
role="banner"
Expand Down Expand Up @@ -422,7 +422,7 @@ exports[`components/ChannelHeader should render active pinned posts 1`] = `
<div
aria-label="channel header region"
className="channel-header alt a11y__region"
data-a11y-sort-order="7"
data-a11y-sort-order="8"
data-channelid="channel_id"
id="channel-header"
role="banner"
Expand Down Expand Up @@ -631,7 +631,7 @@ exports[`components/ChannelHeader should render archived view 1`] = `
<div
aria-label="channel header region"
className="channel-header alt a11y__region"
data-a11y-sort-order="7"
data-a11y-sort-order="8"
data-channelid="channel_id"
id="channel-header"
role="banner"
Expand Down Expand Up @@ -780,7 +780,7 @@ exports[`components/ChannelHeader should render correct menu when muted 1`] = `
<div
aria-label="channel header region"
className="channel-header alt a11y__region"
data-a11y-sort-order="7"
data-a11y-sort-order="8"
data-channelid="channel_id"
id="channel-header"
role="banner"
Expand Down Expand Up @@ -1030,7 +1030,7 @@ exports[`components/ChannelHeader should render properly when populated 1`] = `
<div
aria-label="channel header region"
className="channel-header alt a11y__region"
data-a11y-sort-order="7"
data-a11y-sort-order="8"
data-channelid="channel_id"
id="channel-header"
role="banner"
Expand Down Expand Up @@ -1239,7 +1239,7 @@ exports[`components/ChannelHeader should render properly when populated with cha
<div
aria-label="channel header region"
className="channel-header alt a11y__region"
data-a11y-sort-order="7"
data-a11y-sort-order="8"
data-channelid="channel_id"
id="channel-header"
role="banner"
Expand Down
2 changes: 1 addition & 1 deletion components/channel_header/channel_header.js
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ class ChannelHeader extends React.PureComponent {
tabIndex='-1'
data-channelid={`${channel.id}`}
className='channel-header alt a11y__region'
data-a11y-sort-order='7'
data-a11y-sort-order='8'
>
<div className='flex-parent'>
<div className='flex-child'>
Expand Down
15 changes: 11 additions & 4 deletions components/channel_layout/channel_controller.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,25 @@ import SidebarRight from 'components/sidebar_right';
import SidebarRightMenu from 'components/sidebar_right_menu';
import ImportThemeModal from 'components/user_settings/import_theme_modal.jsx';
import ModalController from 'components/modal_controller';
import TeamSidebar from 'components/team_sidebar';
import LegacyTeamSidebar from 'components/legacy_team_sidebar';
import LegacySidebar from 'components/legacy_sidebar';
import Sidebar from 'components/sidebar';
import * as Utils from 'utils/utils';
import * as UserAgent from 'utils/user_agent';
import CenterChannel from 'components/channel_layout/center_channel';
import LoadingScreen from 'components/loading_screen';
import FaviconTitleHandler from 'components/favicon_title_handler';

export default class ChannelController extends React.Component {
static propTypes = {
pathName: PropTypes.string.isRequired,
teamType: PropTypes.string.isRequired,
fetchingChannels: PropTypes.bool.isRequired,
useLegacyLHS: PropTypes.bool.isRequired,
};

shouldComponentUpdate(nextProps) {
return this.props.teamType !== nextProps.teamType || this.props.pathName !== nextProps.pathName || this.props.fetchingChannels !== nextProps.fetchingChannels;
return this.props.teamType !== nextProps.teamType || this.props.pathName !== nextProps.pathName || this.props.fetchingChannels !== nextProps.fetchingChannels || this.props.useLegacyLHS !== nextProps.useLegacyLHS;
}

componentDidMount() {
Expand All @@ -60,19 +63,23 @@ export default class ChannelController extends React.Component {
}

render() {
const PreferredTeamSidebar = LegacyTeamSidebar; // TODO: Replace with switch when we rewrite team sidebar
const PreferredSidebar = this.props.useLegacyLHS ? LegacySidebar : Sidebar;

return (
<div
id='channel_view'
className='channel-view'
>
<AnnouncementBarController/>
<SystemNotice/>
<FaviconTitleHandler/>

<div className='container-fluid'>
<SidebarRight/>
<SidebarRightMenu teamType={this.props.teamType}/>
<Route component={TeamSidebar}/>
<Route component={Sidebar}/>
<Route component={PreferredTeamSidebar}/>
<Route component={PreferredSidebar}/>
{!this.props.fetchingChannels && <Route component={CenterChannel}/>}
{this.props.fetchingChannels && <LoadingScreen/>}
<Pluggable pluggableName='Root'/>
Expand Down
1 change: 1 addition & 0 deletions components/channel_layout/channel_controller.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ describe('components/channel_layout/ChannelController', () => {
pathName: 'test',
teamType: 'test',
fetchingChannels: false,
useLegacyLHS: true,
};
test('Should have app__body and channel-view classes on body after mount', () => {
Object.defineProperty(window.navigator, 'platform', {
Expand Down
84 changes: 84 additions & 0 deletions components/favicon_title_handler/favicon_title_handler.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import React from 'react';

import {shallowWithIntl} from 'tests/helpers/intl-test-helper';

import {Constants} from 'utils/constants';

import FaviconTitleHandler from 'components/favicon_title_handler/favicon_title_handler';

jest.mock('utils/user_agent', () => {
const original = require.requireActual('utils/user_agent');
return {
...original,
isFirefox: () => true,
};
});

describe('components/FaviconTitleHandler', () => {
const defaultProps = {
unreads: {
messageCount: 0,
mentionCount: 0,
},
siteName: 'Test site',
currentChannel: {
id: 'c1',
display_name: 'Public test 1',
name: 'public-test-1',
type: Constants.OPEN_CHANNEL,
},
currentTeam: {
id: 'team_id',
name: 'test-team',
display_name: 'Test team display name',
description: 'Test team description',
type: 'team-type',
},
currentTeammate: null,
};

test('set correctly the title when needed', () => {
const wrapper = shallowWithIntl(
<FaviconTitleHandler {...defaultProps}/>
);
const instance = wrapper.instance();
instance.updateTitle();
instance.componentDidUpdate = jest.fn();
instance.render = jest.fn();
expect(document.title).toBe('Public test 1 - Test team display name Test site');
wrapper.setProps({siteName: null});
instance.updateTitle();
expect(document.title).toBe('Public test 1 - Test team display name');
wrapper.setProps({currentChannel: {id: 1, type: Constants.DM_CHANNEL}, currentTeammate: {display_name: 'teammate'}});
instance.updateTitle();
expect(document.title).toBe('teammate - Test team display name');
wrapper.setProps({unreads: {mentionCount: 3, messageCount: 4}});
instance.updateTitle();
expect(document.title).toBe('(3) * teammate - Test team display name');
wrapper.setProps({currentChannel: {}, currentTeammate: {}});
instance.updateTitle();
expect(document.title).toBe('Mattermost - Join a team');
});

test('should display correct favicon', () => {
const link = document.createElement('link');
link.rel = 'icon';
link.sizes = '16x16';
document.head.appendChild(link);

const wrapper = shallowWithIntl(
<FaviconTitleHandler {...defaultProps}/>
);
const instance = wrapper.instance();
instance.updateFavicon = jest.fn();

wrapper.setProps({unreads: {mentionCount: 3, messageCount: 4}});
expect(instance.updateFavicon).lastCalledWith(true);

wrapper.setProps({unreads: {mentionCount: 0, messageCount: 4}});
expect(instance.updateFavicon).lastCalledWith(false);
});
});
Loading

0 comments on commit 5af1795

Please sign in to comment.