MM-51231 Replace react-custom-scrollbars with react-simplebar (#33783)

* Add simplebar-react at latest version supported by React 17

* Prevent sidebars and centre channel from overflowing the page vertically

* Add Scrollbars component and replace react-custom-scrollbars

* Remove react-custom-scrollbars

* Fix usage of Scrollbars in modals

The various way we reuse .more-modal, .more-modal__list, and
.filtered-user-list in slightly different ways really made painful. We
should revisit that at some point when we get the chance since those
classes are used in a bunch of contradictory ways like using
.more-modal__list in a popover list in Multiselect versus as part of the
modal in the Browse Channel modal. It also doesn't help that some modals
use Scrollbars and others just use `overflow: auto` on either the whole
modal or on the .more-modal__list.

* Fix dragging the LHS scrollbar also dragging the channel behind it

* Fix scrolling in Browse Channels modal

* Fix results in user group modals not scrolling

* Update snapshots and fix tests

* Prevent focusing simplebar divs (accessibility_sidebar_spec.ts)

* Fix admin LHS still being able to scroll (openid_spec.ts)

* Migrate Team Members modal to GenericModal and the new menu (manage_members_spec.js)

The menu was being cut off yet again by the contents of the modal, so I
migrated that to the new menu to portal it out of that. Unfortunately,
that caused the infinite recursion bug between MUI and React Bootstrap's
focus trap logic, so I had to also migrate the modal to GenericModal to
fix that.

* Update snapshot and fix test

* Update snapshot and fix test

* Fix another E2E test

* More snapshots
This commit is contained in:
Harrison Healey
2025-09-03 18:30:19 -04:00
committed by GitHub
parent b007b6f339
commit 5f51497219
44 changed files with 1040 additions and 1500 deletions
-36
View File
@@ -9947,42 +9947,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---
## react-custom-scrollbars
This product contains 'react-custom-scrollbars' by Malte Wessel.
React scrollbars component
* HOMEPAGE:
* https://github.com/malte-wessel/react-custom-scrollbars
* LICENSE: MIT
The MIT License (MIT)
Copyright (c) 2015 react-custom-scrollbars
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---
## react-day-picker
@@ -51,7 +51,7 @@ describe('Managing bots in Teams and Channels', () => {
cy.get(`#teamMembersDropdown_${bot.username}`).as('memberDropdown').should('contain.text', 'Member').click();
// # Promote bot to team admin
cy.findByTestId('userListItemActions').find('button').contains('Make Team Admin').click();
cy.get('li').contains('Make Team Admin').click();
// * Verify bot was promoted
cy.get('@memberDropdown').should('contain.text', 'Team Admin');
@@ -52,48 +52,41 @@ describe('Channel settings', () => {
cy.apiLogin(mainUser);
cy.visit(`/${myTeam.name}/channels/off-topic`);
// # Post message as the second user, in a channel near the top of the list
cy.apiGetChannelByName(myTeam.name, channelNames[firstChannelIndex]).then(({channel}) => {
cy.postMessageAs({
sender: otherUser,
message: 'Bleep bloop I am a robot',
channelId: channel.id,
});
// # Wait for channels to load
cy.get(`#sidebarItem_${channelNames[firstChannelIndex]}`).should('be.visible');
// # Scroll down in channels list until last created channel is visible
cy.get(`#sidebarItem_${channelNames[lastChannelIndex]}`).scrollIntoView({duration: TIMEOUTS.TWO_SEC});
cy.get('.scrollbar--view').scrollTo('bottom');
});
// # Nudge the scrollbar slightly to make the "More Unreads" pills appear
// @hmhealey - They seem to appear automatically on a regular browser, but not when running under Cypress
cy.get('#SidebarContainer .simplebar-content-wrapper').scrollTo(0, 1);
// * After scrolling is complete, "More Unreads" pill should be visible at the top of the channels list
cy.get('#unreadIndicatorBottom').should('not.be.visible');
// * "More Unreads" pill should be visible at the top of the channels list
// # Click on "More Unreads" pill
cy.get('#unreadIndicatorTop').should('be.visible').click();
// # Post as another user in a channel near the bottom of the list, scroll channels list to view it (should be in bold)
cy.apiGetChannelByName(myTeam.name, channelNames[lastChannelIndex]).then(({channel}) => {
cy.postMessageAs({
sender: otherUser,
message: 'Bleep bloop I am a robot',
channelId: channel.id,
});
// # Scroll down in channels list until last created channel is visible
cy.get(`#sidebarItem_${channelNames[firstChannelIndex]}`).scrollIntoView({duration: TIMEOUTS.TWO_SEC});
cy.get('.scrollbar--view').scrollTo('top');
});
// * After scrolling is complete, "More Unreads" pill should not be visible at the top of the channels list
// * The bottom "More Unreads" pill should be visible and the top one should not
cy.get('#unreadIndicatorTop').should('not.be.visible');
cy.get('#unreadIndicatorBottom').should('be.visible');
// * "More Unreads" pill should be visible at the bottom of the channels list
// # Click on "More Unreads" pill
cy.get('#unreadIndicatorBottom').should('be.visible').click();
// # Click on the "More Unreads" pill to scroll down to the bottom
cy.get('#unreadIndicatorBottom').click();
// * "More Unreads" pill should not be visible at the bottom of the channels list & visible at the top
cy.get('#unreadIndicatorBottom').should('not.be.visible');
// * The list should have scrolled down to the bottom
cy.get(`#sidebarItem_${channelNames[firstChannelIndex]}`).should('not.be.visible');
cy.get(`#sidebarItem_${channelNames[lastChannelIndex]}`).should('be.visible');
// * The top "More Unreads" pill should now be visible and the bottom one should not
cy.get('#unreadIndicatorTop').should('be.visible');
cy.get('#unreadIndicatorBottom').should('not.be.visible');
// * The "More Unreads" pill should now be visible at the top of the channels list
// # Click on the "More Unreads" pill to scroll up to the top
cy.get('#unreadIndicatorTop').click();
// * The list should have scrolled up to the top
cy.get(`#sidebarItem_${channelNames[firstChannelIndex]}`).should('be.visible');
cy.get(`#sidebarItem_${channelNames[lastChannelIndex]}`).should('not.be.visible');
// # Scroll somewhere to the middle of the list
cy.get('#SidebarContainer .simplebar-content-wrapper').scrollTo(0, 200);
// * Both "More Unreads" pills should now be visible
cy.get('#unreadIndicatorTop').should('be.visible');
cy.get('#unreadIndicatorBottom').should('be.visible');
});
});
@@ -36,7 +36,7 @@ describe('Category sorting', () => {
// # Create 5 channels and add them to a custom category
for (let i = 0; i < 5; i++) {
channelNames.push(createChannelAndAddToCategory(categoryName));
cy.get('#SidebarContainer .scrollbar--view').scrollTo('bottom', {ensureScrollable: false});
cy.get('#SidebarContainer .simplebar-content-wrapper').scrollTo('bottom', {ensureScrollable: false});
}
// # Sort alphabetically
@@ -47,7 +47,7 @@ describe('Category sorting', () => {
// # Add another channel
channelNames.push(createChannelAndAddToCategory(categoryName));
cy.get('#SidebarContainer .scrollbar--view').scrollTo('bottom', {ensureScrollable: false});
cy.get('#SidebarContainer .simplebar-content-wrapper').scrollTo('bottom', {ensureScrollable: false});
// * Verify channels are still sorted alphabetically
verifyAlphabeticalSortingOrder(categoryName, channelNames.length);
@@ -64,7 +64,7 @@ describe('Category sorting', () => {
// # Add another channel
channelNames.push(createChannelAndAddToCategory(categoryName));
cy.get('#SidebarContainer .scrollbar--view').scrollTo('bottom', {ensureScrollable: false});
cy.get('#SidebarContainer .simplebar-content').scrollTo('bottom', {ensureScrollable: false});
// # Sort channel names in reverse order that they were created (ie. most recent to least)
sortedByRecencyChannelNames = channelNames.concat().reverse();
@@ -98,7 +98,7 @@ describe('Category sorting', () => {
// # Add another channel
channelNames.push(createChannelAndAddToCategory(categoryName));
cy.get('#SidebarContainer .scrollbar--view').scrollTo('bottom', {ensureScrollable: false});
cy.get('#SidebarContainer .simplebar-content').scrollTo('bottom', {ensureScrollable: false});
// * Verify that the channel has been placed at the bottom of the category
cy.get(`.SidebarChannelGroup:contains(${categoryName}) .NavGroupContent li:nth-child(1) a[id^="sidebarItem_${channelNames[channelNames.length - 1]}"]`).should('be.visible');
@@ -77,24 +77,24 @@ describe('Category sorting', () => {
// // # Create 15 channels and add them to a custom category
// for (let i = 0; i < 15; i++) {
// createChannelAndAddToCategory(categoryName);
// cy.get('#SidebarContainer .scrollbar--view').scrollTo('bottom', {ensureScrollable: false});
// cy.get('#SidebarContainer .simplebar-content-wrapper').scrollTo('bottom', {ensureScrollable: false});
// }
// // # Create 10 channels and add them to Favourites
// for (let i = 0; i < 10; i++) {
// createChannelAndAddToFavourites();
// cy.get('#SidebarContainer .scrollbar--view').scrollTo('bottom', {ensureScrollable: false});
// cy.get('#SidebarContainer .simplebar-content-wrapper').scrollTo('bottom', {ensureScrollable: false});
// }
// // # Scroll to the center of the channel list
// cy.get('#SidebarContainer .scrollbar--view').scrollTo('center', {ensureScrollable: false});
// cy.get('#SidebarContainer .simplebar-content-wrapper').scrollTo('center', {ensureScrollable: false});
// // * Verify that both the 'More Unreads' label and the category header are visible
// cy.get('#unreadIndicatorTop').should('be.visible');
// cy.get('#SidebarContainer .SidebarChannelGroupHeader:contains(FAVORITES)').should('be.visible');
// // # Scroll to the bottom of the list
// cy.get('#SidebarContainer .scrollbar--view').scrollTo('bottom', {ensureScrollable: false});
// cy.get('#SidebarContainer .simplebar-content-wrapper').scrollTo('bottom', {ensureScrollable: false});
// // * Verify that the 'More Unreads' label is still visible but the category is not
// cy.get('#unreadIndicatorTop').should('be.visible');
@@ -40,7 +40,7 @@ describe('Manage Members', () => {
cy.get(`#teamMembersDropdown_${testUser.username}`).should('be.visible').click();
// # Click Make Team Admin
cy.get(`#teamMembersDropdown_${testUser.username} ~ div button:contains(Make Team Admin)`).should('be.visible').click();
cy.get('li').contains('Make Team Admin').should('be.visible').click();
// * Verify dropdown shows that user is now a Team Admin
cy.get(`#teamMembersDropdown_${testUser.username} span:contains(Team Admin)`).should('be.visible');
@@ -67,7 +67,7 @@ describe('Manage Members', () => {
cy.get(`#teamMembersDropdown_${user.username}`).should('be.visible').click();
// # Click Make Team Admin
cy.get(`#teamMembersDropdown_${user.username} ~ div button:contains(Make Team Admin)`).should('be.visible').click();
cy.get('li').contains('Make Team Admin').should('be.visible').click();
// * Verify dropdown shows that user is now a Team Admin
cy.get(`#teamMembersDropdown_${user.username} span:contains(Team Admin)`).should('be.visible');
@@ -100,7 +100,7 @@ describe('Manage Members', () => {
cy.get(`#teamMembersDropdown_${user.username}`).should('be.visible').click();
// # Click Remove from Team
cy.get(`#teamMembersDropdown_${user.username} ~ div button:contains(Remove from Team)`).should('be.visible').click();
cy.get('li').contains('Remove from Team').should('be.visible').click();
// * Verify teammate no longer appears
cy.get(`#teamMembersDropdown_${user.username}`).should('not.exist');
@@ -145,7 +145,7 @@ describe('Manage Members', () => {
cy.get(`#teamMembersDropdown_${user.username}`).should('be.visible').click();
// # Click Remove from Team
cy.get(`#teamMembersDropdown_${user.username} ~ div button:contains(Remove from Team)`).should('be.visible').click();
cy.get('li').contains('Remove from Team').should('be.visible').click();
// * Verify teammate no longer appears
cy.get(`#teamMembersDropdown_${user.username}`).should('not.exist');
+1 -1
View File
@@ -62,7 +62,6 @@
"react-beautiful-dnd": "13.1.1",
"react-bootstrap": "github:mattermost/react-bootstrap#7d8660f06188a6433bb0f69b5816a97dc9ebe48c",
"react-color": "2.19.3",
"react-custom-scrollbars": "4.2.1",
"react-day-picker": "8.3.6",
"react-dom": "17.0.2",
"react-intl": "*",
@@ -85,6 +84,7 @@
"semver": "7.6.3",
"serialize-error": "11.0.3",
"shallow-equals": "1.0.0",
"simplebar-react": "3.3.2",
"smooth-scroll-into-view-if-needed": "2.0.2",
"stream-browserify": "3.0.0",
"styled-components": "5.3.7",
@@ -5,7 +5,7 @@ exports[`components/SearchableChannelList should match init snapshot 1`] = `
className="filtered-user-list"
>
<div
className="filter-row filter-row--full"
className="filter-row"
>
<span
aria-hidden="true"
@@ -2,7 +2,7 @@
.modal-header{
padding: 16px 64px 4px 32px;
}
.filtered-user-list {
height: 440px;
cursor: pointer;
@@ -22,14 +22,14 @@
width: 10px;
height: 10px;
border-radius: 50%;
&.status-success { background-color: var(--online-indicator); }
&.status-error { background-color: var(--error-text); }
&.status-in-progress { background-color: var(--away-indicator); }
&.status-pending { background-color: var(--offline-indicator); }
}
.filter-row--full {
.filter-row {
position: relative;
padding: 0 32px;
@@ -73,7 +73,7 @@
.changes-cell {
text-align: center;
.changes-summary {
.added {
color: var(--online-indicator);
@@ -239,7 +239,7 @@ const SearchableSyncJobChannelList = (props: Props) => {
}
const input = (
<div className='filter-row filter-row--full'>
<div className='filter-row'>
<span
id='searchIcon'
aria-hidden='true'
@@ -23,23 +23,7 @@ exports[`components/AdminSidebar Plugins should filter plugins 1`] = `
value="autolink"
/>
</div>
<Scrollbars
autoHeight={false}
autoHeightMax={200}
autoHeightMin={0}
autoHide={true}
autoHideDuration={500}
autoHideTimeout={500}
hideTracksWhenNotNeeded={false}
renderThumbHorizontal={[Function]}
renderThumbVertical={[Function]}
renderTrackHorizontal={[Function]}
renderTrackVertical={[Function]}
renderView={[Function]}
tagName="div"
thumbMinSize={30}
universal={false}
>
<Scrollbars>
<div
className="nav-pills__container"
>
@@ -103,23 +87,7 @@ exports[`components/AdminSidebar Plugins should match snapshot 1`] = `
value=""
/>
</div>
<Scrollbars
autoHeight={false}
autoHeightMax={200}
autoHeightMin={0}
autoHide={true}
autoHideDuration={500}
autoHideTimeout={500}
hideTracksWhenNotNeeded={false}
renderThumbHorizontal={[Function]}
renderThumbVertical={[Function]}
renderTrackHorizontal={[Function]}
renderTrackVertical={[Function]}
renderView={[Function]}
tagName="div"
thumbMinSize={30}
universal={false}
>
<Scrollbars>
<div
className="nav-pills__container"
>
@@ -194,23 +162,7 @@ exports[`components/AdminSidebar should match snapshot 1`] = `
value=""
/>
</div>
<Scrollbars
autoHeight={false}
autoHeightMax={200}
autoHeightMin={0}
autoHide={true}
autoHideDuration={500}
autoHideTimeout={500}
hideTracksWhenNotNeeded={false}
renderThumbHorizontal={[Function]}
renderThumbVertical={[Function]}
renderTrackHorizontal={[Function]}
renderTrackVertical={[Function]}
renderView={[Function]}
tagName="div"
thumbMinSize={30}
universal={false}
>
<Scrollbars>
<div
className="nav-pills__container"
>
@@ -888,23 +840,7 @@ exports[`components/AdminSidebar should match snapshot with license with enterpr
value=""
/>
</div>
<Scrollbars
autoHeight={false}
autoHeightMax={200}
autoHeightMin={0}
autoHide={true}
autoHideDuration={500}
autoHideTimeout={500}
hideTracksWhenNotNeeded={false}
renderThumbHorizontal={[Function]}
renderThumbVertical={[Function]}
renderTrackHorizontal={[Function]}
renderTrackVertical={[Function]}
renderView={[Function]}
tagName="div"
thumbMinSize={30}
universal={false}
>
<Scrollbars>
<div
className="nav-pills__container"
>
@@ -1967,23 +1903,7 @@ exports[`components/AdminSidebar should match snapshot with license with enterpr
value=""
/>
</div>
<Scrollbars
autoHeight={false}
autoHeightMax={200}
autoHeightMin={0}
autoHide={true}
autoHideDuration={500}
autoHideTimeout={500}
hideTracksWhenNotNeeded={false}
renderThumbHorizontal={[Function]}
renderThumbVertical={[Function]}
renderTrackHorizontal={[Function]}
renderTrackVertical={[Function]}
renderView={[Function]}
tagName="div"
thumbMinSize={30}
universal={false}
>
<Scrollbars>
<div
className="nav-pills__container"
>
@@ -3033,23 +2953,7 @@ exports[`components/AdminSidebar should match snapshot with license with profess
value=""
/>
</div>
<Scrollbars
autoHeight={false}
autoHeightMax={200}
autoHeightMin={0}
autoHide={true}
autoHideDuration={500}
autoHideTimeout={500}
hideTracksWhenNotNeeded={false}
renderThumbHorizontal={[Function]}
renderThumbVertical={[Function]}
renderTrackHorizontal={[Function]}
renderTrackVertical={[Function]}
renderView={[Function]}
tagName="div"
thumbMinSize={30}
universal={false}
>
<Scrollbars>
<div
className="nav-pills__container"
>
@@ -4010,23 +3914,7 @@ exports[`components/AdminSidebar should match snapshot with workspace optimizati
value=""
/>
</div>
<Scrollbars
autoHeight={false}
autoHeightMax={200}
autoHeightMin={0}
autoHide={true}
autoHideDuration={500}
autoHideTimeout={500}
hideTracksWhenNotNeeded={false}
renderThumbHorizontal={[Function]}
renderThumbVertical={[Function]}
renderTrackHorizontal={[Function]}
renderTrackVertical={[Function]}
renderView={[Function]}
tagName="div"
thumbMinSize={30}
universal={false}
>
<Scrollbars>
<div
className="nav-pills__container"
>
@@ -4704,23 +4592,7 @@ exports[`components/AdminSidebar should match snapshot, no access 1`] = `
value=""
/>
</div>
<Scrollbars
autoHeight={false}
autoHeightMax={200}
autoHeightMin={0}
autoHide={true}
autoHideDuration={500}
autoHideTimeout={500}
hideTracksWhenNotNeeded={false}
renderThumbHorizontal={[Function]}
renderThumbVertical={[Function]}
renderTrackHorizontal={[Function]}
renderTrackVertical={[Function]}
renderView={[Function]}
tagName="div"
thumbMinSize={30}
universal={false}
>
<Scrollbars>
<div
className="nav-pills__container"
>
@@ -4759,23 +4631,7 @@ exports[`components/AdminSidebar should match snapshot, not prevent the console
value=""
/>
</div>
<Scrollbars
autoHeight={false}
autoHeightMax={200}
autoHeightMin={0}
autoHide={true}
autoHideDuration={500}
autoHideTimeout={500}
hideTracksWhenNotNeeded={false}
renderThumbHorizontal={[Function]}
renderThumbVertical={[Function]}
renderTrackHorizontal={[Function]}
renderTrackVertical={[Function]}
renderView={[Function]}
tagName="div"
thumbMinSize={30}
universal={false}
>
<Scrollbars>
<div
className="nav-pills__container"
>
@@ -5453,23 +5309,7 @@ exports[`components/AdminSidebar should match snapshot, render plugins without a
value=""
/>
</div>
<Scrollbars
autoHeight={false}
autoHeightMax={200}
autoHeightMin={0}
autoHide={true}
autoHideDuration={500}
autoHideTimeout={500}
hideTracksWhenNotNeeded={false}
renderThumbHorizontal={[Function]}
renderThumbVertical={[Function]}
renderTrackHorizontal={[Function]}
renderTrackVertical={[Function]}
renderView={[Function]}
tagName="div"
thumbMinSize={30}
universal={false}
>
<Scrollbars>
<div
className="nav-pills__container"
>
@@ -6147,23 +5987,7 @@ exports[`components/AdminSidebar should match snapshot, with license (with all f
value=""
/>
</div>
<Scrollbars
autoHeight={false}
autoHeightMax={200}
autoHeightMin={0}
autoHide={true}
autoHideDuration={500}
autoHideTimeout={500}
hideTracksWhenNotNeeded={false}
renderThumbHorizontal={[Function]}
renderThumbVertical={[Function]}
renderTrackHorizontal={[Function]}
renderTrackVertical={[Function]}
renderView={[Function]}
tagName="div"
thumbMinSize={30}
universal={false}
>
<Scrollbars>
<div
className="nav-pills__container"
>
@@ -7038,23 +6862,7 @@ exports[`components/AdminSidebar should match snapshot, with license (without an
value=""
/>
</div>
<Scrollbars
autoHeight={false}
autoHeightMax={200}
autoHeightMin={0}
autoHide={true}
autoHideDuration={500}
autoHideTimeout={500}
hideTracksWhenNotNeeded={false}
renderThumbHorizontal={[Function]}
renderThumbVertical={[Function]}
renderTrackHorizontal={[Function]}
renderTrackVertical={[Function]}
renderView={[Function]}
tagName="div"
thumbMinSize={30}
universal={false}
>
<Scrollbars>
<div
className="nav-pills__container"
>
@@ -4,7 +4,6 @@
import classNames from 'classnames';
import isEqual from 'lodash/isEqual';
import React from 'react';
import Scrollbars from 'react-custom-scrollbars';
import {FormattedMessage, injectIntl} from 'react-intl';
import type {IntlShape} from 'react-intl';
@@ -14,6 +13,7 @@ import AdminSidebarCategory from 'components/admin_console/admin_sidebar/admin_s
import AdminSidebarSection from 'components/admin_console/admin_sidebar/admin_sidebar_section';
import AdminSidebarHeader from 'components/admin_console/admin_sidebar_header';
import SearchKeywordMarking from 'components/admin_console/search_keyword_marking';
import Scrollbars from 'components/common/scrollbars';
import QuickInput from 'components/quick_input';
import SearchIcon from 'components/widgets/icons/search_icon';
@@ -35,27 +35,6 @@ type State = {
filter: string;
}
const renderScrollView = (props: Props) => (
<div
{...props}
className='scrollbar--view'
/>
);
const renderScrollThumbHorizontal = (props: Props) => (
<div
{...props}
className='scrollbar--horizontal'
/>
);
const renderScrollThumbVertical = (props: Props) => (
<div
{...props}
className='scrollbar--vertical'
/>
);
class AdminSidebar extends React.PureComponent<Props, State> {
searchRef: React.RefObject<HTMLInputElement>;
idx: Index | null;
@@ -308,14 +287,7 @@ class AdminSidebar extends React.PureComponent<Props, State> {
onClear={this.handleClearFilter}
/>
</div>
<Scrollbars
autoHide={true}
autoHideTimeout={500}
autoHideDuration={500}
renderThumbHorizontal={renderScrollThumbHorizontal}
renderThumbVertical={renderScrollThumbVertical}
renderView={renderScrollView}
>
<Scrollbars>
<div className='nav-pills__container'>
<SearchKeywordMarking keyword={this.state.filter}>
<ul className={classNames('nav nav-pills nav-stacked', {'task-list-shown': showTaskList})}>
@@ -7,7 +7,7 @@
}
}
.filter-row--full {
.filter-row {
position: relative;
padding: 0 32px;
@@ -0,0 +1,21 @@
.simplebar-track {
// Prevent clicking or dragging the scrollbar from interacting with elements behind it
pointer-events: all;
}
.simplebar-track .simplebar-scrollbar::before {
width: 6px;
border-radius: 2px;
// Defaults to sidebar text BG, but it can be overridden by the Scrollbars component
background-color: rgba(var(--scrollbar-color, var(--sidebar-text-rgb)), 0.32);
}
.simplebar-track .simplebar-scrollbar.simplebar-visible::before {
opacity: 1;
}
[data-simplebar] {
// Ensures that scrollbar divs never overflow their flexbox parents
min-height: 0;
}
@@ -0,0 +1,40 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {fireEvent, render} from 'tests/react_testing_utils';
import Scrollbars from './scrollbars';
describe('Scrollbars', () => {
test('should attach scroll handler to the correct element', () => {
const onScroll = jest.fn();
render(
<Scrollbars onScroll={onScroll}>
{'This is some content in a scrollable area'}
</Scrollbars>,
);
// Ideally, we'd actually scroll the content of the element, but jsdom doesn't implement scroll events
fireEvent.scroll(document.querySelector('.simplebar-content-wrapper')!);
expect(onScroll).toHaveBeenCalled();
});
test('should attach ref to the correct element', () => {
let scrollElement;
render(
<Scrollbars
ref={(element) => {
scrollElement = element;
}}
>
<div/>
</Scrollbars>,
);
expect(scrollElement).toBe(document.querySelector('.simplebar-content-wrapper'));
});
});
@@ -0,0 +1,52 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {useMergeRefs} from '@floating-ui/react';
import React, {useCallback, useRef} from 'react';
import SimpleBar from 'simplebar-react';
import 'simplebar-react/dist/simplebar.min.css';
import './scrollbars.scss';
export type ScrollbarsProps = {
children: React.ReactNode;
color?: string;
onScroll?: (e: React.UIEvent) => void;
};
const Scrollbars = React.forwardRef<HTMLElement, ScrollbarsProps>(({
children,
color,
onScroll,
}, ref) => {
const removeListener = useRef<() => void>();
// We can't pass scroll handlers directly to SimpleBar, so we have to attach it to the DOM element directly
const setScrollRef = useCallback((el) => {
removeListener.current?.();
removeListener.current = undefined;
if (el && onScroll) {
el.addEventListener('scroll', onScroll);
removeListener.current = () => el.removeEventListener('scroll', onScroll);
}
}, [onScroll]);
const mergedRef = useMergeRefs<HTMLElement>([ref, setScrollRef]);
return (
<SimpleBar
autoHide={true}
scrollableNodeProps={{ref: mergedRef}}
style={{
'--scrollbar-color': `var(${color})`,
} as React.CSSProperties}
tabIndex={-1}
>
{children}
</SimpleBar>
);
});
Scrollbars.displayName = 'Scrollbars';
export default Scrollbars;
@@ -6,7 +6,7 @@ exports[`components/multiselect/multiselect MultiSelectList should match snapsho
className="filtered-user-list"
>
<div
className="filter-row filter-row--full"
className="filter-row"
>
<div
className="multi-select__container react-select"
@@ -192,7 +192,7 @@ exports[`components/multiselect/multiselect should match snapshot 1`] = `
className="filtered-user-list"
>
<div
className="filter-row filter-row--full"
className="filter-row"
>
<div
className="multi-select__container react-select"
@@ -369,7 +369,7 @@ exports[`components/multiselect/multiselect should match snapshot for page 2 1`]
className="filtered-user-list"
>
<div
className="filter-row filter-row--full"
className="filter-row"
>
<div
className="multi-select__container react-select"
@@ -493,7 +493,7 @@ export class MultiSelect<T extends Value> extends React.PureComponent<Props<T>,
return (
<>
<div className='filtered-user-list'>
<div className='filter-row filter-row--full'>
<div className='filter-row'>
<div
className={classNames('multi-select__container react-select', {
'has-error': this.state.hasError,
@@ -7,160 +7,194 @@ exports[`components/post_edit_history should display error screen if errors are
id="rhsContainer"
>
<div
style="position: relative; overflow: hidden; width: 100%; height: 100%;"
data-simplebar="init"
style="--scrollbar-color: var(undefined);"
>
<div
class="scrollbar--view"
style="position: absolute; top: 0px; left: 0px; right: 0px; bottom: 0px; overflow: scroll; margin-right: 0px; margin-bottom: 0px;"
class="simplebar-wrapper"
>
<div
class="sidebar--right__header"
class="simplebar-height-auto-observer-wrapper"
>
<span
class="sidebar--right__title"
id="rhsPanelTitle"
<div
class="simplebar-height-auto-observer"
/>
</div>
<div
class="simplebar-mask"
>
<div
class="simplebar-offset"
style="right: 0px; bottom: 0px;"
>
<h2
id="rhsPanelTitle"
>
Edit History
</h2>
<div
class="sidebar--right__title__channel"
aria-label="scrollable content"
class="simplebar-content-wrapper"
role="region"
style="height: auto; overflow-x: hidden; overflow-y: hidden;"
tabindex="-1"
>
channel_display_name
<div
class="simplebar-content"
>
<div
class="sidebar--right__header"
>
<span
class="sidebar--right__title"
id="rhsPanelTitle"
>
<h2
id="rhsPanelTitle"
>
Edit History
</h2>
<div
class="sidebar--right__title__channel"
>
channel_display_name
</div>
</span>
<div
class="pull-right"
>
<button
aria-label="Expand Sidebar Icon"
class="sidebar--right__expand btn btn-icon btn-sm"
type="button"
>
<i
aria-hidden="true"
class="icon icon-arrow-expand"
/>
<i
aria-hidden="true"
class="icon icon-arrow-collapse"
/>
</button>
<button
aria-label="Close"
class="sidebar--right__close btn btn-icon btn-sm"
id="searchResultsCloseButton"
type="button"
>
<i
aria-label="Close Sidebar Icon"
class="icon icon-close"
/>
</button>
</div>
</div>
<div
class="edit-post-history__error_container"
>
<div
class="edit-post-history__error_item"
>
<svg
fill="none"
height="127"
viewBox="0 0 87 70"
width="127"
xmlns="http://www.w3.org/2000/svg"
>
<rect
fill="var(--button-bg)"
fill-opacity="0.12"
height="24"
rx="3.75"
width="72"
x="8.00098"
y="7"
/>
<rect
fill="var(--button-bg)"
fill-opacity="0.12"
height="25"
rx="3.75"
width="87"
x="0.000976562"
y="34"
/>
<path
d="M38.3214 2.31098C39.4303 0.112261 42.5697 0.112256 43.6786 2.31098L71.7146 57.899C72.7209 59.8943 71.2707 62.25 69.0359 62.25H12.9641C10.7294 62.25 9.27912 59.8943 10.2854 57.899L38.3214 2.31098Z"
fill="var(--center-channel-bg)"
/>
<path
d="M40.3214 4.31098C41.4303 2.11226 44.5697 2.11226 45.6786 4.31098L73.7146 59.899C74.7209 61.8943 73.2707 64.25 71.0359 64.25H14.9641C12.7294 64.25 11.2791 61.8943 12.2854 59.899L40.3214 4.31098Z"
fill="#FFBC1F"
/>
<path
d="M43.2322 2.53614L71.2681 58.1242C72.1067 59.7869 70.8982 61.75 69.0359 61.75H12.9641C11.1018 61.75 9.89327 59.7869 10.7319 58.1242L38.7678 2.53614C39.6919 0.703873 42.3081 0.703871 43.2322 2.53614Z"
stroke="var(--center-channel-color)"
/>
<path
d="M49.542 4.23999L52.8888 10.72M74.922 53.38L68.5073 40.96L66.8339 37.72L64.6027 33.4L61.5348 27.46M59.3036 23.14L55.12 15.04"
stroke="var(--center-channel-color)"
stroke-linecap="round"
stroke-opacity="0.56"
stroke-width="1.08"
/>
<path
d="M38.0164 25.2833L40.2971 39.9301C40.3191 40.2208 40.4554 40.4927 40.6786 40.6912C40.9018 40.8897 41.1954 41 41.5002 41C41.8051 41 42.0986 40.8897 42.3219 40.6912C42.5451 40.4927 42.6814 40.2208 42.7034 39.9301L44.984 25.2833C45.3987 19.5722 37.5955 19.5722 38.0164 25.2833Z"
fill="#3F4350"
/>
<path
d="M41.0072 47C41.798 47.0014 42.5706 47.2372 43.2275 47.6776C43.8843 48.118 44.396 48.7432 44.6976 49.4742C44.9993 50.2053 45.0774 51.0093 44.9222 51.7848C44.7671 52.5602 44.3856 53.2723 43.8259 53.831C43.2662 54.3897 42.5535 54.7699 41.7777 54.9237C41.002 55.0774 40.1981 54.9978 39.4676 54.6948C38.7371 54.3919 38.1128 53.8792 37.6736 53.2215C37.2344 52.5639 37 51.7908 37 51C37 50.4741 37.1036 49.9534 37.3051 49.4676C37.5066 48.9818 37.8019 48.5406 38.1741 48.169C38.5463 47.7975 38.9881 47.503 39.4743 47.3024C39.9604 47.1018 40.4813 46.9991 41.0072 47Z"
fill="#3F4350"
/>
<path
d="M48.4619 68.5H70.0619"
stroke="var(--center-channel-color)"
stroke-linecap="round"
stroke-opacity="0.56"
stroke-width="1.08"
/>
<path
d="M10.001 50L26.001 19"
stroke="var(--center-channel-color)"
stroke-linecap="round"
stroke-opacity="0.56"
stroke-width="1.08"
/>
</svg>
<p
class="edit-post-history__error_heading"
>
Unable to load edit history
</p>
<p
class="edit-post-history__error_subheading"
>
There was an error loading the history for this message. Check your network connection or try again later.
</p>
</div>
</div>
</div>
</div>
</span>
<div
class="pull-right"
>
<button
aria-label="Expand Sidebar Icon"
class="sidebar--right__expand btn btn-icon btn-sm"
type="button"
>
<i
aria-hidden="true"
class="icon icon-arrow-expand"
/>
<i
aria-hidden="true"
class="icon icon-arrow-collapse"
/>
</button>
<button
aria-label="Close"
class="sidebar--right__close btn btn-icon btn-sm"
id="searchResultsCloseButton"
type="button"
>
<i
aria-label="Close Sidebar Icon"
class="icon icon-close"
/>
</button>
</div>
</div>
<div
class="edit-post-history__error_container"
>
<div
class="edit-post-history__error_item"
>
<svg
fill="none"
height="127"
viewBox="0 0 87 70"
width="127"
xmlns="http://www.w3.org/2000/svg"
>
<rect
fill="var(--button-bg)"
fill-opacity="0.12"
height="24"
rx="3.75"
width="72"
x="8.00098"
y="7"
/>
<rect
fill="var(--button-bg)"
fill-opacity="0.12"
height="25"
rx="3.75"
width="87"
x="0.000976562"
y="34"
/>
<path
d="M38.3214 2.31098C39.4303 0.112261 42.5697 0.112256 43.6786 2.31098L71.7146 57.899C72.7209 59.8943 71.2707 62.25 69.0359 62.25H12.9641C10.7294 62.25 9.27912 59.8943 10.2854 57.899L38.3214 2.31098Z"
fill="var(--center-channel-bg)"
/>
<path
d="M40.3214 4.31098C41.4303 2.11226 44.5697 2.11226 45.6786 4.31098L73.7146 59.899C74.7209 61.8943 73.2707 64.25 71.0359 64.25H14.9641C12.7294 64.25 11.2791 61.8943 12.2854 59.899L40.3214 4.31098Z"
fill="#FFBC1F"
/>
<path
d="M43.2322 2.53614L71.2681 58.1242C72.1067 59.7869 70.8982 61.75 69.0359 61.75H12.9641C11.1018 61.75 9.89327 59.7869 10.7319 58.1242L38.7678 2.53614C39.6919 0.703873 42.3081 0.703871 43.2322 2.53614Z"
stroke="var(--center-channel-color)"
/>
<path
d="M49.542 4.23999L52.8888 10.72M74.922 53.38L68.5073 40.96L66.8339 37.72L64.6027 33.4L61.5348 27.46M59.3036 23.14L55.12 15.04"
stroke="var(--center-channel-color)"
stroke-linecap="round"
stroke-opacity="0.56"
stroke-width="1.08"
/>
<path
d="M38.0164 25.2833L40.2971 39.9301C40.3191 40.2208 40.4554 40.4927 40.6786 40.6912C40.9018 40.8897 41.1954 41 41.5002 41C41.8051 41 42.0986 40.8897 42.3219 40.6912C42.5451 40.4927 42.6814 40.2208 42.7034 39.9301L44.984 25.2833C45.3987 19.5722 37.5955 19.5722 38.0164 25.2833Z"
fill="#3F4350"
/>
<path
d="M41.0072 47C41.798 47.0014 42.5706 47.2372 43.2275 47.6776C43.8843 48.118 44.396 48.7432 44.6976 49.4742C44.9993 50.2053 45.0774 51.0093 44.9222 51.7848C44.7671 52.5602 44.3856 53.2723 43.8259 53.831C43.2662 54.3897 42.5535 54.7699 41.7777 54.9237C41.002 55.0774 40.1981 54.9978 39.4676 54.6948C38.7371 54.3919 38.1128 53.8792 37.6736 53.2215C37.2344 52.5639 37 51.7908 37 51C37 50.4741 37.1036 49.9534 37.3051 49.4676C37.5066 48.9818 37.8019 48.5406 38.1741 48.169C38.5463 47.7975 38.9881 47.503 39.4743 47.3024C39.9604 47.1018 40.4813 46.9991 41.0072 47Z"
fill="#3F4350"
/>
<path
d="M48.4619 68.5H70.0619"
stroke="var(--center-channel-color)"
stroke-linecap="round"
stroke-opacity="0.56"
stroke-width="1.08"
/>
<path
d="M10.001 50L26.001 19"
stroke="var(--center-channel-color)"
stroke-linecap="round"
stroke-opacity="0.56"
stroke-width="1.08"
/>
</svg>
<p
class="edit-post-history__error_heading"
>
Unable to load edit history
</p>
<p
class="edit-post-history__error_subheading"
>
There was an error loading the history for this message. Check your network connection or try again later.
</p>
</div>
</div>
</div>
<div
style="position: absolute; height: 6px; transition: opacity 500ms; opacity: 0; display: none; right: 2px; bottom: 2px; left: 2px; border-radius: 3px;"
>
<div
class="scrollbar--horizontal"
style="position: relative; display: block; height: 100%;"
class="simplebar-placeholder"
style="width: 0px; height: 0px;"
/>
</div>
<div
style="position: absolute; width: 6px; transition: opacity 500ms; opacity: 0; display: none; right: 2px; bottom: 2px; top: 2px; border-radius: 3px;"
class="simplebar-track simplebar-horizontal"
style="visibility: hidden;"
>
<div
class="scrollbar--vertical"
style="position: relative; display: block; width: 100%;"
class="simplebar-scrollbar"
style="width: 0px; display: none;"
/>
</div>
<div
class="simplebar-track simplebar-vertical"
style="visibility: hidden;"
>
<div
class="simplebar-scrollbar"
style="height: 0px; display: none;"
/>
</div>
</div>
@@ -175,147 +209,261 @@ exports[`components/post_edit_history should match snapshot 1`] = `
id="rhsContainer"
>
<div
style="position: relative; overflow: hidden; width: 100%; height: 100%;"
data-simplebar="init"
style="--scrollbar-color: var(undefined);"
>
<div
class="scrollbar--view"
style="position: absolute; top: 0px; left: 0px; right: 0px; bottom: 0px; overflow: scroll; margin-right: 0px; margin-bottom: 0px;"
class="simplebar-wrapper"
>
<div
class="sidebar--right__header"
class="simplebar-height-auto-observer-wrapper"
>
<span
class="sidebar--right__title"
id="rhsPanelTitle"
>
<h2
id="rhsPanelTitle"
>
Edit History
</h2>
<div
class="sidebar--right__title__channel"
>
channel_display_name
</div>
</span>
<div
class="pull-right"
>
<button
aria-label="Expand Sidebar Icon"
class="sidebar--right__expand btn btn-icon btn-sm"
type="button"
>
<i
aria-hidden="true"
class="icon icon-arrow-expand"
/>
<i
aria-hidden="true"
class="icon icon-arrow-collapse"
/>
</button>
<button
aria-label="Close"
class="sidebar--right__close btn btn-icon btn-sm"
id="searchResultsCloseButton"
type="button"
>
<i
aria-label="Close Sidebar Icon"
class="icon icon-close"
/>
</button>
</div>
class="simplebar-height-auto-observer"
/>
</div>
<div
class="edit-post-history__container edit-post-history__container__background"
class="simplebar-mask"
>
<div
aria-label="At 12:00 AM Thursday, January 1, Someone wrote, post message"
class="a11y__section post"
id="searchResult_post_id"
class="simplebar-offset"
style="right: 0px; bottom: 0px;"
>
<div
class="edit-post-history__title__container"
aria-label="scrollable content"
class="simplebar-content-wrapper"
role="region"
style="height: auto; overflow-x: hidden; overflow-y: hidden;"
tabindex="-1"
>
<div
class="edit-post-history__date__badge__container"
class="simplebar-content"
>
<button
aria-label="Toggle to see an old message."
class="edit-post-history__icon__button toggleCollapseButton"
<div
class="sidebar--right__header"
>
<i
class="icon icon-chevron-down"
/>
</button>
<span
class="edit-post-history__date"
>
<time
datetime="1970-01-01T00:00:00.000"
<span
class="sidebar--right__title"
id="rhsPanelTitle"
>
January 01, 1970 at 12:00 AM
</time>
</span>
<div
class="edit-post-history__current__indicator"
>
Current Version
</div>
</div>
</div>
<div
class="edit-post-history__content_container"
>
<div
class="edit-post-history__header"
>
<span
class="profile-icon"
>
<img
alt="user profile image"
class="Avatar Avatar-sm avatar-post-preview"
loading="lazy"
src="/api/v4/users/user_id/image?_=0"
/>
</span>
<div
class="edit-post-history__header__username"
>
<h2
id="rhsPanelTitle"
>
Edit History
</h2>
<div
class="sidebar--right__title__channel"
>
channel_display_name
</div>
</span>
<div
class="user-popover"
class="pull-right"
>
Someone
<button
aria-label="Expand Sidebar Icon"
class="sidebar--right__expand btn btn-icon btn-sm"
type="button"
>
<i
aria-hidden="true"
class="icon icon-arrow-expand"
/>
<i
aria-hidden="true"
class="icon icon-arrow-collapse"
/>
</button>
<button
aria-label="Close"
class="sidebar--right__close btn btn-icon btn-sm"
id="searchResultsCloseButton"
type="button"
>
<i
aria-label="Close Sidebar Icon"
class="icon icon-close"
/>
</button>
</div>
</div>
</div>
<div
class="post__content"
>
<div
class="search-item-snippet post__body"
class="edit-post-history__container edit-post-history__container__background"
>
<div
class="post-message post-message--collapsed"
aria-label="At 12:00 AM Thursday, January 1, Someone wrote, post message"
class="a11y__section post"
id="searchResult_post_id"
>
<div
class="post-message__text-container"
style="max-height: 600px;"
class="edit-post-history__title__container"
>
<div
class="post-message__text"
dir="auto"
id="rhsPostMessageText_post_id"
class="edit-post-history__date__badge__container"
>
<p>
post message
</p>
<button
aria-label="Toggle to see an old message."
class="edit-post-history__icon__button toggleCollapseButton"
>
<i
class="icon icon-chevron-down"
/>
</button>
<span
class="edit-post-history__date"
>
<time
datetime="1970-01-01T00:00:00.000"
>
January 01, 1970 at 12:00 AM
</time>
</span>
<div
class="edit-post-history__current__indicator"
>
Current Version
</div>
</div>
</div>
<div
class="edit-post-history__content_container"
>
<div
class="edit-post-history__header"
>
<span
class="profile-icon"
>
<img
alt="user profile image"
class="Avatar Avatar-sm avatar-post-preview"
loading="lazy"
src="/api/v4/users/user_id/image?_=0"
/>
</span>
<div
class="edit-post-history__header__username"
>
<div
class="user-popover"
>
Someone
</div>
</div>
</div>
<div
class="post__content"
>
<div
class="search-item-snippet post__body"
>
<div
class="post-message post-message--collapsed"
>
<div
class="post-message__text-container"
style="max-height: 600px;"
>
<div
class="post-message__text"
dir="auto"
id="rhsPostMessageText_post_id"
>
<p>
post message
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="edit-post-history__container"
>
<div
aria-label="At 12:00 AM Thursday, January 1, Someone wrote, post message version 1"
class="a11y__section post"
id="searchResult_post_id_1"
>
<div
class="edit-post-history__title__container"
>
<div
class="edit-post-history__date__badge__container"
>
<button
aria-label="Toggle to see an old message."
class="edit-post-history__icon__button toggleCollapseButton"
>
<i
class="icon icon-chevron-right"
/>
</button>
<span
class="edit-post-history__date"
>
<time
datetime="1970-01-01T00:00:00.000"
>
January 01, 1970 at 12:00 AM
</time>
</span>
</div>
<button
aria-label="Select to restore an old message."
class="edit-post-history__icon__button restore-icon"
>
<i
class="icon icon-restore"
/>
</button>
</div>
</div>
</div>
<div
class="edit-post-history__container"
>
<div
aria-label="At 12:00 AM Thursday, January 1, Someone wrote, post message version 2"
class="a11y__section post"
id="searchResult_post_id_2"
>
<div
class="edit-post-history__title__container"
>
<div
class="edit-post-history__date__badge__container"
>
<button
aria-label="Toggle to see an old message."
class="edit-post-history__icon__button toggleCollapseButton"
>
<i
class="icon icon-chevron-right"
/>
</button>
<span
class="edit-post-history__date"
>
<time
datetime="1970-01-01T00:00:00.000"
>
January 01, 1970 at 12:00 AM
</time>
</span>
</div>
<button
aria-label="Select to restore an old message."
class="edit-post-history__icon__button restore-icon"
>
<i
class="icon icon-restore"
/>
</button>
</div>
</div>
</div>
</div>
@@ -323,106 +471,26 @@ exports[`components/post_edit_history should match snapshot 1`] = `
</div>
</div>
<div
class="edit-post-history__container"
>
<div
aria-label="At 12:00 AM Thursday, January 1, Someone wrote, post message version 1"
class="a11y__section post"
id="searchResult_post_id_1"
>
<div
class="edit-post-history__title__container"
>
<div
class="edit-post-history__date__badge__container"
>
<button
aria-label="Toggle to see an old message."
class="edit-post-history__icon__button toggleCollapseButton"
>
<i
class="icon icon-chevron-right"
/>
</button>
<span
class="edit-post-history__date"
>
<time
datetime="1970-01-01T00:00:00.000"
>
January 01, 1970 at 12:00 AM
</time>
</span>
</div>
<button
aria-label="Select to restore an old message."
class="edit-post-history__icon__button restore-icon"
>
<i
class="icon icon-restore"
/>
</button>
</div>
</div>
</div>
<div
class="edit-post-history__container"
>
<div
aria-label="At 12:00 AM Thursday, January 1, Someone wrote, post message version 2"
class="a11y__section post"
id="searchResult_post_id_2"
>
<div
class="edit-post-history__title__container"
>
<div
class="edit-post-history__date__badge__container"
>
<button
aria-label="Toggle to see an old message."
class="edit-post-history__icon__button toggleCollapseButton"
>
<i
class="icon icon-chevron-right"
/>
</button>
<span
class="edit-post-history__date"
>
<time
datetime="1970-01-01T00:00:00.000"
>
January 01, 1970 at 12:00 AM
</time>
</span>
</div>
<button
aria-label="Select to restore an old message."
class="edit-post-history__icon__button restore-icon"
>
<i
class="icon icon-restore"
/>
</button>
</div>
</div>
</div>
</div>
<div
style="position: absolute; height: 6px; transition: opacity 500ms; opacity: 0; display: none; right: 2px; bottom: 2px; left: 2px; border-radius: 3px;"
>
<div
class="scrollbar--horizontal"
style="position: relative; display: block; height: 100%;"
class="simplebar-placeholder"
style="width: 0px; height: 0px;"
/>
</div>
<div
style="position: absolute; width: 6px; transition: opacity 500ms; opacity: 0; display: none; right: 2px; bottom: 2px; top: 2px; border-radius: 3px;"
class="simplebar-track simplebar-horizontal"
style="visibility: hidden;"
>
<div
class="scrollbar--vertical"
style="position: relative; display: block; width: 100%;"
class="simplebar-scrollbar"
style="width: 0px; display: none;"
/>
</div>
<div
class="simplebar-track simplebar-vertical"
style="visibility: hidden;"
>
<div
class="simplebar-scrollbar"
style="height: 0px; display: none;"
/>
</div>
</div>
@@ -1,4 +1,4 @@
.sidebar-right__edit-post-history .scrollbar--view {
.sidebar-right__edit-post-history .simplebar-content {
display: flex;
flex-direction: column;
}
@@ -11,6 +11,9 @@ import {TestHelper} from 'utils/test_helper';
import PostEditHistory from './post_edit_history';
// jsdom doesn't implement scrolling, so we need to manually define this
window.HTMLElement.prototype.scrollTo = jest.fn();
describe('components/post_edit_history', () => {
const baseProps: ComponentProps<typeof PostEditHistory> = {
channelDisplayName: 'channel_display_name',
@@ -2,7 +2,6 @@
// See LICENSE.txt for license information.
import React, {memo, useEffect, useRef, useState} from 'react';
import Scrollbars from 'react-custom-scrollbars';
import {useIntl} from 'react-intl';
import {useDispatch} from 'react-redux';
@@ -10,6 +9,7 @@ import type {Post} from '@mattermost/types/posts';
import {getPostEditHistory} from 'mattermost-redux/actions/posts';
import Scrollbars from 'components/common/scrollbars';
import AlertIcon from 'components/common/svg_images_components/alert_svg';
import LoadingScreen from 'components/loading_screen';
import SearchResultsHeader from 'components/search_results_header';
@@ -19,27 +19,6 @@ import EditedPostItem from './edited_post_item';
import type {PropsFromRedux} from './index';
import './post_edit_history.scss';
const renderView = (props: Record<string, unknown>): JSX.Element => (
<div
{...props}
className='scrollbar--view'
/>
);
const renderThumbHorizontal = (props: Record<string, unknown>): JSX.Element => (
<div
{...props}
className='scrollbar--horizontal'
/>
);
const renderThumbVertical = (props: Record<string, unknown>): JSX.Element => (
<div
{...props}
className='scrollbar--vertical'
/>
);
const PostEditHistory = ({
channelDisplayName,
originalPost,
@@ -48,7 +27,7 @@ const PostEditHistory = ({
const [hasError, setHasError] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(false);
const dispatch = useDispatch();
const scrollbars = useRef<Scrollbars | null>(null);
const scrollbars = useRef<HTMLDivElement>(null);
const {formatMessage} = useIntl();
const retrieveErrorHeading = formatMessage({
id: 'post_info.edit.history.retrieveError',
@@ -73,7 +52,7 @@ const PostEditHistory = ({
setIsLoading(false);
};
fetchPostEditHistory();
scrollbars.current?.scrollToTop();
scrollbars.current?.scrollTo({top: 0});
}, [originalPost, dispatch]);
useEffect(() => {
@@ -140,15 +119,7 @@ const PostEditHistory = ({
id='rhsContainer'
className='sidebar-right__body sidebar-right__edit-post-history'
>
<Scrollbars
ref={scrollbars}
autoHide={true}
autoHideTimeout={500}
autoHideDuration={500}
renderThumbHorizontal={renderThumbHorizontal}
renderThumbVertical={renderThumbVertical}
renderView={renderView}
>
<Scrollbars ref={scrollbars}>
<SearchResultsHeader>
<h2 id='rhsPanelTitle'>
{title}
@@ -8,22 +8,7 @@ exports[`comoponents/rhs_card/RhsCard should match on post when no plugin defini
previousRhsState={Object {}}
/>
<Scrollbars
autoHeight={false}
autoHeightMax={200}
autoHeightMin={0}
autoHide={true}
autoHideDuration={500}
autoHideTimeout={500}
hideTracksWhenNotNeeded={false}
onScroll={[Function]}
renderThumbHorizontal={[Function]}
renderThumbVertical={[Function]}
renderTrackHorizontal={[Function]}
renderTrackVertical={[Function]}
renderView={[Function]}
tagName="div"
thumbMinSize={30}
universal={false}
>
<div
className="post-right__scroll"
@@ -114,22 +99,7 @@ exports[`comoponents/rhs_card/RhsCard should match on post when plugin defining
previousRhsState={Object {}}
/>
<Scrollbars
autoHeight={false}
autoHeightMax={200}
autoHeightMin={0}
autoHide={true}
autoHideDuration={500}
autoHideTimeout={500}
hideTracksWhenNotNeeded={false}
onScroll={[Function]}
renderThumbHorizontal={[Function]}
renderThumbVertical={[Function]}
renderTrackHorizontal={[Function]}
renderTrackVertical={[Function]}
renderView={[Function]}
tagName="div"
thumbMinSize={30}
universal={false}
>
<div
className="post-right__scroll"
@@ -220,22 +190,7 @@ exports[`comoponents/rhs_card/RhsCard should match on post when plugin defining
previousRhsState={Object {}}
/>
<Scrollbars
autoHeight={false}
autoHeightMax={200}
autoHeightMin={0}
autoHide={true}
autoHideDuration={500}
autoHideTimeout={500}
hideTracksWhenNotNeeded={false}
onScroll={[Function]}
renderThumbHorizontal={[Function]}
renderThumbVertical={[Function]}
renderTrackHorizontal={[Function]}
renderTrackVertical={[Function]}
renderView={[Function]}
tagName="div"
thumbMinSize={30}
universal={false}
>
<div
className="post-right__scroll"
@@ -4,7 +4,6 @@
import deepEqual from 'fast-deep-equal';
import React from 'react';
import type {ReactNode} from 'react';
import Scrollbars from 'react-custom-scrollbars';
import {FormattedMessage} from 'react-intl';
import {Link} from 'react-router-dom';
@@ -14,6 +13,7 @@ import {ensureString} from 'mattermost-redux/utils/post_utils';
import {emitCloseRightHandSide} from 'actions/global_actions';
import Scrollbars from 'components/common/scrollbars';
import Markdown from 'components/markdown';
import PostProfilePicture from 'components/post_profile_picture';
import RhsCardHeader from 'components/rhs_card_header';
@@ -38,33 +38,6 @@ type State = {
isScrolling: boolean;
};
export function renderView(props: Props) {
return (
<div
{...props}
className='scrollbar--view'
/>
);
}
export function renderThumbHorizontal(props: Props) {
return (
<div
{...props}
className='scrollbar--horizontal'
/>
);
}
export function renderThumbVertical(props: Props) {
return (
<div
{...props}
className='scrollbar--vertical'
/>
);
}
export default class RhsCard extends React.Component<Props, State> {
scrollStopAction: DelayedAction;
@@ -165,15 +138,7 @@ export default class RhsCard extends React.Component<Props, State> {
return (
<div className='sidebar-right__body sidebar-right__card'>
<RhsCardHeader previousRhsState={this.props.previousRhsState}/>
<Scrollbars
autoHide={true}
autoHideTimeout={500}
autoHideDuration={500}
renderThumbHorizontal={renderThumbHorizontal}
renderThumbVertical={renderThumbVertical}
renderView={renderView}
onScroll={this.handleScroll}
>
<Scrollbars onScroll={this.handleScroll}>
<div className='post-right__scroll'>
{content}
<div className='d-flex post-card--info'>
@@ -3,7 +3,6 @@
import classNames from 'classnames';
import React, {useEffect, useRef, useState} from 'react';
import Scrollbars from 'react-custom-scrollbars';
import {useIntl, FormattedMessage, defineMessage} from 'react-intl';
import {useSelector} from 'react-redux';
@@ -16,6 +15,7 @@ import {isDateLine, getDateForDateLine} from 'mattermost-redux/utils/post_list';
import {getFilesDropdownPluginMenuItems} from 'selectors/plugins';
import Scrollbars from 'components/common/scrollbars';
import FileSearchResultItem from 'components/file_search_results';
import NoResultsIndicator from 'components/no_results_indicator/no_results_indicator';
import {NoResultsVariant} from 'components/no_results_indicator/types';
@@ -37,34 +37,6 @@ import './search_results.scss';
const GET_MORE_BUFFER = 30;
const renderView = (props: Record<string, unknown>): JSX.Element => (
<div
{...props}
className='scrollbar--view'
/>
);
const renderThumbHorizontal = (props: Record<string, unknown>): JSX.Element => (
<div
{...props}
className='scrollbar--horizontal scrollbar--thumb--RHS'
/>
);
const renderThumbVertical = (props: Record<string, unknown>): JSX.Element => (
<div
{...props}
className='scrollbar--vertical scrollbar--thumb--RHS'
/>
);
const renderTrackVertical = (props: Record<string, unknown>): JSX.Element => (
<div
{...props}
className='scrollbar--vertical--RHS'
/>
);
interface NoResultsProps {
variant: NoResultsVariant;
titleValues?: Record<string, React.ReactNode>;
@@ -79,7 +51,7 @@ const defaultProps: Partial<Props> = {
};
const SearchResults: React.FC<Props> = (props: Props): JSX.Element => {
const scrollbars = useRef<Scrollbars|null>(null);
const scrollbars = useRef<HTMLDivElement>(null);
const [searchType, setSearchType] = useState<string>(props.searchType);
const filesDropdownPluginMenuItems = useSelector(getFilesDropdownPluginMenuItems);
const config = useSelector(getConfig);
@@ -90,7 +62,7 @@ const SearchResults: React.FC<Props> = (props: Props): JSX.Element => {
props.setSearchFilterType('all');
}
setSearchType(props.searchType);
scrollbars.current?.scrollToTop();
scrollbars.current?.scrollTo({top: 0});
}, [props.searchTerms]);
useEffect(() => {
@@ -116,9 +88,9 @@ const SearchResults: React.FC<Props> = (props: Props): JSX.Element => {
const handleScroll = (): void => {
if (!props.isFlaggedPosts && !props.isPinnedPosts && !props.isSearchingTerm && !props.isSearchGettingMore && !props.isChannelFiles) {
const scrollHeight = scrollbars.current?.getScrollHeight() || 0;
const scrollTop = scrollbars.current?.getScrollTop() || 0;
const clientHeight = scrollbars.current?.getClientHeight() || 0;
const scrollHeight = scrollbars.current?.scrollHeight || 0;
const scrollTop = scrollbars.current?.scrollTop || 0;
const clientHeight = scrollbars.current?.clientHeight || 0;
if ((scrollTop + clientHeight + GET_MORE_BUFFER) >= scrollHeight) {
if (searchType === DataSearchTypes.FILES_SEARCH_TYPE) {
loadMoreFiles();
@@ -411,13 +383,7 @@ const SearchResults: React.FC<Props> = (props: Props): JSX.Element => {
<SearchLimitsBanner searchType={searchType}/>
<Scrollbars
ref={scrollbars}
autoHide={true}
autoHideTimeout={500}
autoHideDuration={500}
renderTrackVertical={renderTrackVertical}
renderThumbHorizontal={renderThumbHorizontal}
renderThumbVertical={renderThumbVertical}
renderView={renderView}
color='--center-channel-color-rgb'
onScroll={handleScroll}
>
<div
@@ -423,7 +423,7 @@ export class SearchableChannelList extends React.PureComponent<Props, State> {
}
const input = (
<div className='filter-row filter-row--full'>
<div className='filter-row'>
<span
id='searchIcon'
aria-hidden='true'
@@ -2,7 +2,6 @@
// See LICENSE.txt for license information.
import React from 'react';
import Scrollbars from 'react-custom-scrollbars';
import {FormattedMessage, injectIntl} from 'react-intl';
import type {IntlShape} from 'react-intl';
@@ -10,6 +9,7 @@ import type {Channel, ChannelMembership} from '@mattermost/types/channels';
import type {TeamMembership} from '@mattermost/types/teams';
import type {UserProfile} from '@mattermost/types/users';
import Scrollbars from 'components/common/scrollbars';
import QuickInput from 'components/quick_input';
import UserList from 'components/user_list';
@@ -57,24 +57,6 @@ type Props = {
rowComponentType?: React.ComponentType<any>;
}
const renderView = (props: Record<string, unknown>): JSX.Element => (
<div
{...props}
className='scrollbar--view'
/>
);
const renderThumbHorizontal = (): JSX.Element => (
<div/>
);
const renderThumbVertical = (props: Record<string, unknown>): JSX.Element => (
<div
{...props}
className='scrollbar--vertical'
/>
);
type State = {
nextDisabled: boolean;
};
@@ -101,7 +83,7 @@ class SearchableUserList extends React.PureComponent<Props, State> {
};
private nextTimeoutId: NodeJS.Timeout;
private scrollbarsRef: React.RefObject<Scrollbars>;
private scrollbarsRef: React.RefObject<HTMLDivElement>;
private filterRef: React.RefObject<HTMLInputElement>;
constructor(props: Props) {
@@ -118,7 +100,7 @@ class SearchableUserList extends React.PureComponent<Props, State> {
}
public scrollToTop = (): void => {
this.scrollbarsRef.current?.scrollToTop();
this.scrollbarsRef.current?.scrollTo({top: 0});
};
componentDidMount() {
@@ -319,15 +301,7 @@ class SearchableUserList extends React.PureComponent<Props, State> {
</div>
</div>
<div className='more-modal__list'>
<Scrollbars
ref={this.scrollbarsRef}
autoHide={true}
autoHideTimeout={500}
autoHideDuration={500}
renderThumbHorizontal={renderThumbHorizontal}
renderThumbVertical={renderThumbVertical}
renderView={renderView}
>
<Scrollbars ref={this.scrollbarsRef}>
<UserList
users={usersToDisplay}
extraInfo={this.props.extraInfo}
@@ -37,55 +37,30 @@ exports[`SidebarList should match snapshot 1`] = `
onClick={[Function]}
show={false}
/>
<div
onPointerLeave={[Function]}
onPointerOver={[Function]}
<Scrollbars
onScroll={[Function]}
>
<Scrollbars
autoHeight={false}
autoHeightMax={200}
autoHeightMin={0}
autoHide={true}
autoHideDuration={200}
autoHideTimeout={1000}
hideTracksWhenNotNeeded={false}
onScroll={[Function]}
renderThumbHorizontal={[Function]}
renderThumbVertical={[Function]}
renderTrackHorizontal={[Function]}
renderTrackVertical={[Function]}
renderView={[Function]}
style={
Object {
"position": "absolute",
}
}
tagName="div"
thumbMinSize={30}
universal={false}
<DragDropContext
onBeforeCapture={[Function]}
onBeforeDragStart={[Function]}
onDragEnd={[Function]}
onDragStart={[Function]}
>
<DragDropContext
onBeforeCapture={[Function]}
onBeforeDragStart={[Function]}
onDragEnd={[Function]}
onDragStart={[Function]}
<Connect(Droppable)
direction="vertical"
droppableId="droppable-categories"
getContainerForClone={[Function]}
ignoreContainerClipping={false}
isCombineEnabled={false}
isDropDisabled={false}
mode="standard"
renderClone={null}
type="SIDEBAR_CATEGORY"
>
<Connect(Droppable)
direction="vertical"
droppableId="droppable-categories"
getContainerForClone={[Function]}
ignoreContainerClipping={false}
isCombineEnabled={false}
isDropDisabled={false}
mode="standard"
renderClone={null}
type="SIDEBAR_CATEGORY"
>
<Component />
</Connect(Droppable)>
</DragDropContext>
</Scrollbars>
</div>
<Component />
</Connect(Droppable)>
</DragDropContext>
</Scrollbars>
</div>
</Fragment>
`;
@@ -147,7 +147,7 @@ describe('SidebarList', () => {
instance.scrollbar = {
current: {
scrollToTop: jest.fn(),
scrollTo: jest.fn(),
} as any,
};
@@ -157,7 +157,7 @@ describe('SidebarList', () => {
};
wrapper.setProps({currentTeam: newCurrentTeam});
expect(instance.scrollbar.current!.scrollToTop).toHaveBeenCalled();
expect(instance.scrollbar.current!.scrollTo).toHaveBeenCalledWith({top: 0});
});
test('should display unread scroll indicator when channels appear outside visible area', () => {
@@ -168,8 +168,8 @@ describe('SidebarList', () => {
instance.scrollbar = {
current: {
getScrollTop: jest.fn(() => 0),
getClientHeight: jest.fn(() => 500),
scrollTop: 0,
clientHeight: 500,
} as any,
};
@@ -200,9 +200,9 @@ describe('SidebarList', () => {
instance.scrollbar = {
current: {
scrollTop: jest.fn(),
getScrollTop: jest.fn(() => 100),
getClientHeight: jest.fn(() => 500),
scrollTo: jest.fn(),
scrollTop: 100,
clientHeight: 500,
} as any,
};
@@ -4,10 +4,8 @@
import classNames from 'classnames';
import debounce from 'lodash/debounce';
import React, {lazy} from 'react';
import type {CSSProperties} from 'react';
import {DragDropContext, Droppable} from 'react-beautiful-dnd';
import type {DropResult, DragStart, BeforeCapture} from 'react-beautiful-dnd';
import Scrollbars from 'react-custom-scrollbars';
import {FormattedMessage, injectIntl, type WrappedComponentProps} from 'react-intl';
import {SpringSystem} from 'rebound';
import type {Spring} from 'rebound';
@@ -21,6 +19,7 @@ import {General} from 'mattermost-redux/constants';
import {trackEvent} from 'actions/telemetry_actions';
import {makeAsyncComponent} from 'components/async_load';
import Scrollbars from 'components/common/scrollbars';
import SidebarCategory from 'components/sidebar/sidebar_category';
import {findNextUnreadChannelId} from 'utils/channel_utils';
@@ -36,44 +35,6 @@ const GlobalThreadsLink = makeAsyncComponent('GlobalThreadsLink', lazy(() => imp
const UnreadChannelIndicator = makeAsyncComponent('UnreadChannelIndicator', lazy(() => import('../unread_channel_indicator')));
const UnreadChannels = makeAsyncComponent('UnreadChannels', lazy(() => import('../unread_channels')));
export function renderView(props: React.HTMLProps<HTMLDivElement>) {
return (
<div
{...props}
className='scrollbar--view'
/>
);
}
export function renderThumbHorizontal(props: React.HTMLProps<HTMLDivElement>) {
return (
<div
{...props}
className='scrollbar--horizontal'
/>
);
}
export function renderTrackVertical(props: React.HTMLProps<HTMLDivElement>) {
return (
<div
{...props}
className='scrollbar--verticalTrack'
/>
);
}
export function renderThumbVertical(props: React.HTMLProps<HTMLDivElement>) {
return (
<div
{...props}
className='scrollbar--vertical'
/>
);
}
const scrollbarStyles: CSSProperties = {position: 'absolute'};
type Props = WrappedComponentProps & {
currentTeam?: Team;
currentChannelId: string;
@@ -109,7 +70,6 @@ type Props = WrappedComponentProps & {
type State = {
showTopUnread: boolean;
showBottomUnread: boolean;
autoHide: boolean;
};
// scrollMargin is the margin at the edge of the channel list that we leave when scrolling to a channel.
@@ -124,10 +84,9 @@ const scrollMarginWithUnread = 55;
export class SidebarList extends React.PureComponent<Props, State> {
channelRefs: Map<string, HTMLLIElement>;
scrollbar: React.RefObject<Scrollbars>;
scrollbar: React.RefObject<HTMLDivElement>;
animate: SpringSystem;
scrollAnimation: Spring;
channelsListScrollTimeout: NodeJS.Timeout | null = null;
constructor(props: Props) {
super(props);
@@ -136,7 +95,6 @@ export class SidebarList extends React.PureComponent<Props, State> {
this.state = {
showTopUnread: false,
showBottomUnread: false,
autoHide: true,
};
this.scrollbar = React.createRef();
@@ -163,7 +121,7 @@ export class SidebarList extends React.PureComponent<Props, State> {
// reset the scrollbar upon switching teams
if (this.props.currentTeam !== prevProps.currentTeam) {
this.scrollbar.current!.scrollToTop();
this.scrollbar.current!.scrollTo({top: 0});
}
// Scroll to selected channel so it's in view
@@ -205,7 +163,7 @@ export class SidebarList extends React.PureComponent<Props, State> {
handleScrollAnimationUpdate = (spring: Spring) => {
const val = spring.getCurrentValue();
this.scrollbar.current!.scrollTop(val);
this.scrollbar.current!.scrollTo?.({top: val});
};
scrollToFirstUnreadChannel = () => {
@@ -229,8 +187,8 @@ export class SidebarList extends React.PureComponent<Props, State> {
const top = element.offsetTop;
const bottom = top + element.offsetHeight;
const scrollTop = this.scrollbar.current!.getScrollTop();
const scrollHeight = this.scrollbar.current!.getClientHeight();
const scrollTop = this.scrollbar.current!.scrollTop;
const clientHeight = this.scrollbar.current!.clientHeight;
if (top < (scrollTop + categoryHeaderHeight)) {
// Scroll up to the item
@@ -246,10 +204,10 @@ export class SidebarList extends React.PureComponent<Props, State> {
}
this.scrollToPosition(scrollEnd);
} else if (bottom > scrollTop + scrollHeight) {
} else if (bottom > scrollTop + clientHeight) {
// Scroll down to the item
const margin = (scrollingToUnread || !this.state.showBottomUnread) ? scrollMargin : scrollMarginWithUnread;
const scrollEnd = (bottom - scrollHeight) + margin;
const scrollEnd = (bottom - clientHeight) + margin;
this.scrollToPosition(scrollEnd);
}
@@ -257,7 +215,7 @@ export class SidebarList extends React.PureComponent<Props, State> {
scrollToPosition = (scrollEnd: number) => {
// Stop the current animation before scrolling
this.scrollAnimation.setCurrentValue(this.scrollbar.current!.getScrollTop()).setAtRest();
this.scrollAnimation.setCurrentValue(this.scrollbar.current!.scrollTop).setAtRest();
this.scrollAnimation.setEndValue(scrollEnd);
};
@@ -281,7 +239,7 @@ export class SidebarList extends React.PureComponent<Props, State> {
if (firstUnreadChannel) {
const firstUnreadElement = this.channelRefs.get(firstUnreadChannel);
if (firstUnreadElement && ((firstUnreadElement.offsetTop + firstUnreadElement.offsetHeight) - scrollMargin - categoryHeaderHeight) < this.scrollbar.current!.getScrollTop()) {
if (firstUnreadElement && ((firstUnreadElement.offsetTop + firstUnreadElement.offsetHeight) - scrollMargin - categoryHeaderHeight) < this.scrollbar.current!.scrollTop) {
showTopUnread = true;
}
}
@@ -289,7 +247,7 @@ export class SidebarList extends React.PureComponent<Props, State> {
if (lastUnreadChannel) {
const lastUnreadElement = this.channelRefs.get(lastUnreadChannel);
if (lastUnreadElement && (lastUnreadElement.offsetTop + scrollMargin) > (this.scrollbar.current!.getScrollTop() + this.scrollbar.current!.getClientHeight())) {
if (lastUnreadElement && (lastUnreadElement.offsetTop + scrollMargin) > (this.scrollbar.current!.scrollTop + this.scrollbar.current!.clientHeight)) {
showBottomUnread = true;
}
}
@@ -466,20 +424,6 @@ export class SidebarList extends React.PureComponent<Props, State> {
this.props.actions.stopDragging();
};
showChannelListScrollbar = () => {
if (this.channelsListScrollTimeout !== null) {
clearTimeout(this.channelsListScrollTimeout);
}
this.setState({autoHide: false});
};
hideChannelListScrollbar = () => {
this.channelsListScrollTimeout = setTimeout(() => {
this.setState({autoHide: true});
}, 300);
};
render() {
const {categories} = this.props;
@@ -580,23 +524,12 @@ export class SidebarList extends React.PureComponent<Props, State> {
extraClass='nav-pills__unread-indicator-bottom'
content={below}
/>
<div
onPointerLeave={this.hideChannelListScrollbar}
onPointerOver={this.showChannelListScrollbar}
<Scrollbars
ref={this.scrollbar}
onScroll={this.onScroll}
>
<Scrollbars
ref={this.scrollbar}
autoHide={this.state.autoHide}
renderThumbHorizontal={renderThumbHorizontal}
renderThumbVertical={renderThumbVertical}
renderTrackVertical={renderTrackVertical}
renderView={renderView}
onScroll={this.onScroll}
style={scrollbarStyles}
>
{channelList}
</Scrollbars>
</div>
{channelList}
</Scrollbars>
</div>
</>
);
@@ -1,198 +1,235 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/team_members_dropdown should match snapshot for a bot with group-constrained team 1`] = `
<MenuWrapper
animationComponent={[Function]}
className=""
>
<button
aria-expanded="true"
className="dropdown-toggle theme color--link style--none"
id="teamMembersDropdown_bot"
type="button"
<Fragment>
<Menu
anchorOrigin={
Object {
"horizontal": "right",
"vertical": "bottom",
}
}
menu={
Object {
"aria-label": "Change the role of a team member",
"id": "teamMembersDropdown_bot_menu",
}
}
menuButton={
Object {
"children": <React.Fragment>
<span>
<Memo(MemoizedFormattedMessage)
defaultMessage="Team Admin"
id="team_members_dropdown.teamAdmin"
/>
</span>
<DropdownIcon />
</React.Fragment>,
"class": "dropdown-toggle theme color--link style--none",
"id": "teamMembersDropdown_bot",
}
}
transformOrigin={
Object {
"horizontal": "right",
"vertical": "top",
}
}
>
<span>
<MemoizedFormattedMessage
defaultMessage="Team Admin"
id="team_members_dropdown.teamAdmin"
/>
</span>
<DropdownIcon />
</button>
<div>
<Menu
ariaLabel="Change the role of a team member"
openLeft={true}
openUp={false}
>
<MenuItemAction
id="removeFromTeam"
onClick={[Function]}
show={true}
text={
<Memo(MemoizedFormattedMessage)
defaultMessage="Remove from Team"
id="team_members_dropdown.leave_team"
/>
}
/>
<MenuItemAction
onClick={[Function]}
show={true}
text={
<Memo(MemoizedFormattedMessage)
defaultMessage="Make Team Member"
id="team_members_dropdown.makeMember"
/>
}
/>
</Menu>
</div>
</MenuWrapper>
<MenuItem
id="removeFromTeam"
labels={
<Memo(MemoizedFormattedMessage)
defaultMessage="Remove from Team"
id="team_members_dropdown.leave_team"
/>
}
onClick={[Function]}
/>
<MenuItem
labels={
<Memo(MemoizedFormattedMessage)
defaultMessage="Make Team Member"
id="team_members_dropdown.makeMember"
/>
}
onClick={[Function]}
/>
</Menu>
</Fragment>
`;
exports[`components/team_members_dropdown should match snapshot for team_members_dropdown 1`] = `
<MenuWrapper
animationComponent={[Function]}
className=""
>
<button
aria-expanded="true"
className="dropdown-toggle theme color--link style--none"
id="teamMembersDropdown_username2"
type="button"
<Fragment>
<Menu
anchorOrigin={
Object {
"horizontal": "right",
"vertical": "bottom",
}
}
menu={
Object {
"aria-label": "Change the role of a team member",
"id": "teamMembersDropdown_username2_menu",
}
}
menuButton={
Object {
"children": <React.Fragment>
<span>
<Memo(MemoizedFormattedMessage)
defaultMessage="Team Admin"
id="team_members_dropdown.teamAdmin"
/>
</span>
<DropdownIcon />
</React.Fragment>,
"class": "dropdown-toggle theme color--link style--none",
"id": "teamMembersDropdown_username2",
}
}
transformOrigin={
Object {
"horizontal": "right",
"vertical": "top",
}
}
>
<span>
<MemoizedFormattedMessage
defaultMessage="Team Admin"
id="team_members_dropdown.teamAdmin"
/>
</span>
<DropdownIcon />
</button>
<div>
<Menu
ariaLabel="Change the role of a team member"
openLeft={true}
openUp={false}
>
<MenuItemAction
id="removeFromTeam"
onClick={[Function]}
show={true}
text={
<Memo(MemoizedFormattedMessage)
defaultMessage="Remove from Team"
id="team_members_dropdown.leave_team"
/>
}
/>
<MenuItemAction
onClick={[Function]}
show={true}
text={
<Memo(MemoizedFormattedMessage)
defaultMessage="Make Team Member"
id="team_members_dropdown.makeMember"
/>
}
/>
</Menu>
</div>
</MenuWrapper>
<MenuItem
id="removeFromTeam"
labels={
<Memo(MemoizedFormattedMessage)
defaultMessage="Remove from Team"
id="team_members_dropdown.leave_team"
/>
}
onClick={[Function]}
/>
<MenuItem
labels={
<Memo(MemoizedFormattedMessage)
defaultMessage="Make Team Member"
id="team_members_dropdown.makeMember"
/>
}
onClick={[Function]}
/>
</Menu>
</Fragment>
`;
exports[`components/team_members_dropdown should match snapshot opening dropdown upwards 1`] = `
<MenuWrapper
animationComponent={[Function]}
className=""
>
<button
aria-expanded="true"
className="dropdown-toggle theme color--link style--none"
id="teamMembersDropdown_username2"
type="button"
<Fragment>
<Menu
anchorOrigin={
Object {
"horizontal": "right",
"vertical": "top",
}
}
menu={
Object {
"aria-label": "Change the role of a team member",
"id": "teamMembersDropdown_username2_menu",
}
}
menuButton={
Object {
"children": <React.Fragment>
<span>
<Memo(MemoizedFormattedMessage)
defaultMessage="Team Admin"
id="team_members_dropdown.teamAdmin"
/>
</span>
<DropdownIcon />
</React.Fragment>,
"class": "dropdown-toggle theme color--link style--none",
"id": "teamMembersDropdown_username2",
}
}
transformOrigin={
Object {
"horizontal": "right",
"vertical": "bottom",
}
}
>
<span>
<MemoizedFormattedMessage
defaultMessage="Team Admin"
id="team_members_dropdown.teamAdmin"
/>
</span>
<DropdownIcon />
</button>
<div>
<Menu
ariaLabel="Change the role of a team member"
openLeft={true}
openUp={true}
>
<MenuItemAction
id="removeFromTeam"
onClick={[Function]}
show={true}
text={
<Memo(MemoizedFormattedMessage)
defaultMessage="Remove from Team"
id="team_members_dropdown.leave_team"
/>
}
/>
<MenuItemAction
onClick={[Function]}
show={true}
text={
<Memo(MemoizedFormattedMessage)
defaultMessage="Make Team Member"
id="team_members_dropdown.makeMember"
/>
}
/>
</Menu>
</div>
</MenuWrapper>
<MenuItem
id="removeFromTeam"
labels={
<Memo(MemoizedFormattedMessage)
defaultMessage="Remove from Team"
id="team_members_dropdown.leave_team"
/>
}
onClick={[Function]}
/>
<MenuItem
labels={
<Memo(MemoizedFormattedMessage)
defaultMessage="Make Team Member"
id="team_members_dropdown.makeMember"
/>
}
onClick={[Function]}
/>
</Menu>
</Fragment>
`;
exports[`components/team_members_dropdown should match snapshot with group-constrained team 1`] = `
<MenuWrapper
animationComponent={[Function]}
className=""
>
<button
aria-expanded="true"
className="dropdown-toggle theme color--link style--none"
id="teamMembersDropdown_username2"
type="button"
<Fragment>
<Menu
anchorOrigin={
Object {
"horizontal": "right",
"vertical": "bottom",
}
}
menu={
Object {
"aria-label": "Change the role of a team member",
"id": "teamMembersDropdown_username2_menu",
}
}
menuButton={
Object {
"children": <React.Fragment>
<span>
<Memo(MemoizedFormattedMessage)
defaultMessage="Team Admin"
id="team_members_dropdown.teamAdmin"
/>
</span>
<DropdownIcon />
</React.Fragment>,
"class": "dropdown-toggle theme color--link style--none",
"id": "teamMembersDropdown_username2",
}
}
transformOrigin={
Object {
"horizontal": "right",
"vertical": "top",
}
}
>
<span>
<MemoizedFormattedMessage
defaultMessage="Team Admin"
id="team_members_dropdown.teamAdmin"
/>
</span>
<DropdownIcon />
</button>
<div>
<Menu
ariaLabel="Change the role of a team member"
openLeft={true}
openUp={false}
>
<MenuItemAction
onClick={[Function]}
show={true}
text={
<Memo(MemoizedFormattedMessage)
defaultMessage="Make Team Member"
id="team_members_dropdown.makeMember"
/>
}
/>
</Menu>
</div>
</MenuWrapper>
<MenuItem
labels={
<Memo(MemoizedFormattedMessage)
defaultMessage="Make Team Member"
id="team_members_dropdown.makeMember"
/>
}
onClick={[Function]}
/>
</Menu>
</Fragment>
`;
@@ -13,9 +13,8 @@ import type {ActionResult} from 'mattermost-redux/types/actions';
import {isGuest, isAdmin, isSystemAdmin} from 'mattermost-redux/utils/user_utils';
import ConfirmModal from 'components/confirm_modal';
import * as Menu from 'components/menu';
import DropdownIcon from 'components/widgets/icons/fa_dropdown_icon';
import Menu from 'components/widgets/menu/menu';
import MenuWrapper from 'components/widgets/menu/menu_wrapper';
import {getHistory} from 'utils/browser_history';
@@ -250,10 +249,10 @@ class TeamMembersDropdown extends React.PureComponent<Props, State> {
}
const menuRemove = (
<Menu.ItemAction
<Menu.Item
id='removeFromTeam'
onClick={this.handleRemoveFromTeam}
text={
labels={
<FormattedMessage
id='team_members_dropdown.leave_team'
defaultMessage='Remove from Team'
@@ -262,9 +261,9 @@ class TeamMembersDropdown extends React.PureComponent<Props, State> {
/>
);
const menuMakeAdmin = (
<Menu.ItemAction
<Menu.Item
onClick={this.handleMakeAdmin}
text={
labels={
<FormattedMessage
id='team_members_dropdown.makeAdmin'
defaultMessage='Make Team Admin'
@@ -273,9 +272,9 @@ class TeamMembersDropdown extends React.PureComponent<Props, State> {
/>
);
const menuMakeMember = (
<Menu.ItemAction
<Menu.Item
onClick={this.handleMakeMember}
text={
labels={
<FormattedMessage
id='team_members_dropdown.makeMember'
defaultMessage='Make Team Member'
@@ -284,30 +283,38 @@ class TeamMembersDropdown extends React.PureComponent<Props, State> {
/>
);
return (
<MenuWrapper>
<button
id={`teamMembersDropdown_${user.username}`}
className='dropdown-toggle theme color--link style--none'
type='button'
aria-expanded='true'
<>
<Menu.Container
menuButton={{
id: `teamMembersDropdown_${user.username}`,
class: 'dropdown-toggle theme color--link style--none',
children: (
<>
<span>{currentRoles} </span>
<DropdownIcon/>
</>
),
}}
menu={{
id: `teamMembersDropdown_${user.username}_menu`,
'aria-label': intl.formatMessage({id: 'team_members_dropdown.menuAriaLabel', defaultMessage: 'Change the role of a team member'}),
}}
anchorOrigin={{
vertical: openUp ? 'top' : 'bottom',
horizontal: 'right',
}}
transformOrigin={{
vertical: openUp ? 'bottom' : 'top',
horizontal: 'right',
}}
>
<span>{currentRoles} </span>
<DropdownIcon/>
</button>
<div>
<Menu
openLeft={true}
openUp={openUp}
ariaLabel={intl.formatMessage({id: 'team_members_dropdown.menuAriaLabel', defaultMessage: 'Change the role of a team member'})}
>
{canRemoveFromTeam ? menuRemove : null}
{showMakeAdmin ? menuMakeAdmin : null}
{showMakeMember ? menuMakeMember : null}
</Menu>
{makeDemoteModal}
{serverError}
</div>
</MenuWrapper>
{canRemoveFromTeam ? menuRemove : null}
{showMakeAdmin ? menuMakeAdmin : null}
{showMakeMember ? menuMakeMember : null}
</Menu.Container>
{makeDemoteModal}
{serverError}
</>
);
}
}
@@ -1,56 +1,13 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/TeamMembersModal should match snapshot 1`] = `
<Modal
animation={true}
aria-labelledby="teamMemberModalLabel"
autoFocus={true}
backdrop={true}
bsClass="modal"
dialogClassName="a11y__modal more-modal"
dialogComponentClass={[Function]}
enforceFocus={true}
id="teamMembersModal"
keyboard={true}
manager={
ModalManager {
"add": [Function],
"containers": Array [],
"data": Array [],
"handleContainerOverflow": true,
"hideSiblingNodes": true,
"isTopModal": [Function],
"modals": Array [],
"remove": [Function],
}
}
onExited={[Function]}
onHide={[Function]}
renderBackdrop={[Function]}
restoreFocus={true}
role="none"
show={true}
>
<ModalHeader
bsClass="modal-header"
closeButton={true}
closeLabel="Close"
>
<ModalTitle
bsClass="modal-title"
componentClass="h1"
id="teamMemberModalLabel"
>
<MemoizedFormattedMessage
defaultMessage="{team} Members"
id="team_member_modal.members"
values={
Object {
"team": "display name",
}
}
/>
</ModalTitle>
<GenericModal
ariaLabelledby="teamMemberModalLabel"
bodyPadding={false}
className="more-modal"
compassDesign={true}
enforceFocus={false}
headerButton={
<Memo(TeamPermissionGate)
permissions={
Array [
@@ -66,20 +23,33 @@ exports[`components/TeamMembersModal should match snapshot 1`] = `
onClick={[Function]}
type="button"
>
<MemoizedFormattedMessage
<Memo(MemoizedFormattedMessage)
defaultMessage="Invite People"
id="team_member_modal.invitePeople"
/>
</button>
</Memo(TeamPermissionGate)>
</ModalHeader>
<ModalBody
bsClass="modal-body"
componentClass="div"
>
<Connect(MemberListTeam)
teamId="id"
}
id="teamMembersModal"
modalHeaderText={
<Memo(MemoizedFormattedMessage)
defaultMessage="{team} Members"
id="team_member_modal.members"
values={
Object {
"team": "display name",
}
}
/>
</ModalBody>
</Modal>
}
modalHeaderTextId="teamMemberModalLabel"
modalLocation="top"
onExited={[Function]}
onHide={[Function]}
show={true}
>
<Connect(MemberListTeam)
teamId="id"
/>
</GenericModal>
`;
@@ -0,0 +1,6 @@
#teamMembersModal {
.GenericModal__header {
display: flex;
justify-content: center;
}
}
@@ -3,7 +3,8 @@
import {shallow} from 'enzyme';
import React from 'react';
import {Modal} from 'react-bootstrap';
import {GenericModal} from '@mattermost/components';
import {TestHelper} from 'utils/test_helper';
@@ -39,9 +40,9 @@ describe('components/TeamMembersModal', () => {
/>,
);
const modalProps = wrapper.find(Modal).first().props();
const modalProps = wrapper.find(GenericModal).first().props();
if (modalProps.onExited) {
modalProps.onExited(document.createElement('div'));
modalProps.onExited();
}
expect(baseProps.onExited).toHaveBeenCalledTimes(1);
@@ -2,9 +2,9 @@
// See LICENSE.txt for license information.
import React from 'react';
import {Modal} from 'react-bootstrap';
import {FormattedMessage} from 'react-intl';
import {GenericModal} from '@mattermost/components';
import type {Team} from '@mattermost/types/teams';
import Permissions from 'mattermost-redux/constants/permissions';
@@ -18,6 +18,8 @@ import {ModalIdentifiers} from 'utils/constants';
import type {ModalData} from 'types/actions';
import './team_members_modal.scss';
type Props = {
currentTeam?: Team;
onExited: () => void;
@@ -76,28 +78,25 @@ export default class TeamMembersModal extends React.PureComponent<Props, State>
}
return (
<Modal
dialogClassName='a11y__modal more-modal'
<GenericModal
id='teamMembersModal'
className='more-modal'
compassDesign={true}
show={this.state.show}
onHide={this.handleHide}
onExited={this.handleExit}
role='none'
aria-labelledby='teamMemberModalLabel'
id='teamMembersModal'
>
<Modal.Header closeButton={true}>
<Modal.Title
componentClass='h1'
id='teamMemberModalLabel'
>
<FormattedMessage
id='team_member_modal.members'
defaultMessage='{team} Members'
values={{
team: teamDisplayName,
}}
/>
</Modal.Title>
modalHeaderTextId='teamMemberModalLabel'
modalHeaderText={
<FormattedMessage
id='team_member_modal.members'
defaultMessage='{team} Members'
values={{
team: teamDisplayName,
}}
/>
}
ariaLabelledby='teamMemberModalLabel'
headerButton={
<TeamPermissionGate
teamId={this.props.currentTeam?.id}
permissions={[Permissions.ADD_USER_TO_TEAM, Permissions.INVITE_GUEST]}
@@ -114,13 +113,15 @@ export default class TeamMembersModal extends React.PureComponent<Props, State>
/>
</button>
</TeamPermissionGate>
</Modal.Header>
<Modal.Body>
<MemberListTeam
teamId={this.props.currentTeam?.id}
/>
</Modal.Body>
</Modal>
}
enforceFocus={false}
modalLocation='top'
bodyPadding={false}
>
<MemberListTeam
teamId={this.props.currentTeam?.id}
/>
</GenericModal>
);
}
}
@@ -5,7 +5,6 @@ import classNames from 'classnames';
import React from 'react';
import {DragDropContext, Droppable} from 'react-beautiful-dnd';
import type {DroppableProvided, DropResult} from 'react-beautiful-dnd';
import Scrollbars from 'react-custom-scrollbars';
import {injectIntl, FormattedMessage} from 'react-intl';
import type {WrappedComponentProps} from 'react-intl';
import type {RouteComponentProps} from 'react-router-dom';
@@ -14,6 +13,7 @@ import type {Team} from '@mattermost/types/teams';
import Permissions from 'mattermost-redux/constants/permissions';
import Scrollbars from 'components/common/scrollbars';
import SystemPermissionGate from 'components/permissions_gates/system_permission_gate';
import TeamButton from 'components/team_sidebar/components/team_button';
@@ -36,33 +36,6 @@ type State = {
teamsOrder: Team[];
}
export function renderView(props: Props) {
return (
<div
{...props}
className='scrollbar--view'
/>
);
}
export function renderThumbHorizontal(props: Props) {
return (
<div
{...props}
className='scrollbar--horizontal'
/>
);
}
export function renderThumbVertical(props: Props) {
return (
<div
{...props}
className='scrollbar--vertical'
/>
);
}
export class TeamSidebar extends React.PureComponent<Props, State> {
constructor(props: Props) {
super(props);
@@ -317,17 +290,10 @@ export class TeamSidebar extends React.PureComponent<Props, State> {
role='navigation'
aria-labelledby='teamSidebarWrapper'
>
<div
className='team-wrapper'
id='teamSidebarWrapper'
>
<Scrollbars
autoHide={true}
autoHideTimeout={500}
autoHideDuration={500}
renderThumbHorizontal={renderThumbHorizontal}
renderThumbVertical={renderThumbVertical}
renderView={renderView}
<Scrollbars>
<div
className='team-wrapper'
id='teamSidebarWrapper'
>
<DragDropContext
onDragEnd={this.onDragEnd}
@@ -350,8 +316,8 @@ export class TeamSidebar extends React.PureComponent<Props, State> {
</Droppable>
</DragDropContext>
{joinableTeams}
</Scrollbars>
</div>
</div>
</Scrollbars>
{plugins}
</div>
);
@@ -208,7 +208,6 @@ body.app__body #root {
#channel_view,
.product-wrapper {
grid-area: center;
overflow: visible;
}
.product-wrapper {
+14 -16
View File
@@ -692,10 +692,6 @@
height: 40px;
padding: 0 16px;
}
&.filter-row--full {
width: 100%;
}
}
.member-count {
@@ -762,7 +758,7 @@
flex: 20;
}
>div {
>div:not([data-simplebar]) {
overflow: auto;
min-height: 100%;
}
@@ -986,24 +982,30 @@
.filtered-user-list {
display: flex;
width: 100%;
height: calc(90vh - 120px);
flex-direction: column;
>div {
flex: 1 1 auto;
.filter-row,
.filter-controls,
.more-modal__dropdown {
flex: 0;
}
.multi-select__wrapper {
display: flex;
height: 1px;
min-height: 0;
flex-direction: column;
flex-grow: 500;
.more-modal__list {
overflow: initial;
}
}
.more-modal__list {
height: 1px;
flex-grow: 500;
position: relative;
overflow: clip;
min-height: 0;
flex: 1;
}
.filter-button {
@@ -1067,10 +1069,6 @@
font-size: 1.25em;
text-align: center;
}
.filter-row--full {
flex-grow: 0;
}
}
.filter-control {
@@ -22,28 +22,6 @@ body {
scrollbar-shadow-color: #2d2c4d;
scrollbar-track-color: rgba(var(--center-channel-bg-rgb), 0.1);
}
.ps {
> .ps__scrollbar-y-rail {
> .ps__scrollbar-y {
width: 6px !important;
}
}
&:hover {
> .ps__scrollbar-y-rail {
&:hover {
background: transparent;
}
}
}
}
}
.scrollbar--horizontal,
.scrollbar--vertical {
border-radius: 2px;
background-color: rgba(var(--sidebar-text-rgb), 0.32);
}
.scrollbar--vertical--RHS {
@@ -57,15 +35,3 @@ body {
.scrollbar--thumb--RHS {
background-color: rgba(var(--center-channel-color-rgb), 0.32);
}
.scrollbar--view {
.browser--ie & {
margin: 0 !important;
-ms-overflow-style: none;
overflow-x: hidden !important;
.scrollbar--vertical {
visibility: hidden;
}
}
}
@@ -4,25 +4,6 @@
$sidebarHeightTransitionDuration: 0.18s;
$sidebarOpacityAnimationDuration: 0.15s;
#TeamSidebar {
z-index: 13;
display: flex;
width: 64px;
height: 100%;
flex-direction: column;
background-color: var(--sidebar-header-bg);
text-align: center;
.TeamSidebarWrapper {
position: relative;
overflow: hidden;
height: 100%;
flex: 1 1 auto;
background-color: rgba(0, 0, 0, 0.2);
-webkit-overflow-scrolling: touch;
}
}
#SidebarContainer {
position: relative;
z-index: 16;
@@ -31,8 +12,10 @@ $sidebarOpacityAnimationDuration: 0.15s;
min-width: 240px;
max-width: 240px;
height: 100%;
min-height: 0;
flex-direction: column;
background-color: var(--sidebar-bg);
overflow-x: visible;
@media screen and (min-width: 769px) {
width: var(--overrideLhsWidth, 264px);
@@ -486,32 +469,11 @@ $sidebarOpacityAnimationDuration: 0.15s;
.SidebarNavContainer {
position: relative;
flex: 1 1 auto;
.scrollbar--view {
z-index: unset !important;
margin-top: 8px;
}
.scrollbar--view ~ div {
z-index: 2;
div {
z-index: 2;
}
}
.scrollbar--verticalTrack {
top: 2px;
right: 2px;
bottom: 2px;
border-radius: 3px;
pointer-events: none;
}
.scrollbar--vertical {
pointer-events: all;
}
display: flex;
overflow: clip;
min-height: 0;
flex: 1;
flex-direction: column;
}
.SidebarCategory_newLabel {
@@ -1,8 +1,8 @@
.team-sidebar {
z-index: 17;
display: flex;
overflow: hidden;
width: 65px;
height: 100%;
flex-direction: column;
background-color: var(--sidebar-header-bg);
text-align: center;
@@ -27,7 +27,8 @@
position: relative;
overflow: hidden;
height: 100%;
flex: 1 1 auto;
flex: 1;
padding-top: 4px;
-webkit-overflow-scrolling: touch;
.draggable-team-container {
@@ -129,10 +130,6 @@
}
}
.scrollbar--view {
padding-top: 4px;
}
.team-container a:hover {
text-decoration: none;
}
@@ -620,7 +620,9 @@
.admin-sidebar {
z-index: 10;
display: flex;
overflow: clip;
width: 220px;
min-height: 0;
flex-direction: column;
background: #111;
grid-area: lhs;
@@ -705,6 +707,7 @@
height: calc(100% - 68px);
padding-bottom: 20px;
margin-top: 1px;
overflow-x: hidden;
ul {
padding-bottom: 20px;
+24 -57
View File
@@ -116,7 +116,6 @@
"react-beautiful-dnd": "13.1.1",
"react-bootstrap": "github:mattermost/react-bootstrap#7d8660f06188a6433bb0f69b5816a97dc9ebe48c",
"react-color": "2.19.3",
"react-custom-scrollbars": "4.2.1",
"react-day-picker": "8.3.6",
"react-dom": "17.0.2",
"react-intl": "*",
@@ -139,6 +138,7 @@
"semver": "7.6.3",
"serialize-error": "11.0.3",
"shallow-equals": "1.0.0",
"simplebar-react": "3.3.2",
"smooth-scroll-into-view-if-needed": "2.0.2",
"stream-browserify": "3.0.0",
"styled-components": "5.3.7",
@@ -415,20 +415,6 @@
"react": "*"
}
},
"channels/node_modules/react-custom-scrollbars": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/react-custom-scrollbars/-/react-custom-scrollbars-4.2.1.tgz",
"integrity": "sha512-VtJTUvZ7kPh/auZWIbBRceGPkE30XBYe+HktFxuMWBR2eVQQ+Ur6yFJMoaYcNpyGq22uYJ9Wx4UAEcC0K+LNPQ==",
"dependencies": {
"dom-css": "^2.0.0",
"prop-types": "^15.5.10",
"raf": "^3.1.0"
},
"peerDependencies": {
"react": "^0.14.0 || ^15.0.0 || ^16.0.0",
"react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0"
}
},
"channels/node_modules/react-redux": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.4.tgz",
@@ -8274,11 +8260,6 @@
"node": ">=0.4.0"
}
},
"node_modules/add-px-to-style": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/add-px-to-style/-/add-px-to-style-1.0.0.tgz",
"integrity": "sha512-YMyxSlXpPjD8uWekCQGuN40lV4bnZagUwqa2m/uFv1z/tNImSk9fnXVMUI5qwME/zzI3MMQRvjZ+69zyfSSyew=="
},
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@@ -11900,16 +11881,6 @@
"utila": "~0.4"
}
},
"node_modules/dom-css": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/dom-css/-/dom-css-2.1.0.tgz",
"integrity": "sha512-w9kU7FAbaSh3QKijL6n59ofAhkkmMJ31GclJIz/vyQdjogfyxcB6Zf8CZyibOERI5o0Hxz30VmJS7+7r5fEj2Q==",
"dependencies": {
"add-px-to-style": "1.0.0",
"prefix-style": "2.0.1",
"to-camel-case": "1.0.0"
}
},
"node_modules/dom-helpers": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
@@ -22228,7 +22199,8 @@
"node_modules/performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
"dev": true
},
"node_modules/phin": {
"version": "2.9.3",
@@ -22658,11 +22630,6 @@
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
"node_modules/prefix-style": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/prefix-style/-/prefix-style-2.0.1.tgz",
"integrity": "sha512-gdr1MBNVT0drzTq95CbSNdsrBDoHGlb2aDJP/FoY+1e+jSDPOb1Cv554gH2MGiSr2WTcXi/zu+NaFzfcHQkfBQ=="
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -22981,6 +22948,7 @@
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
"dev": true,
"dependencies": {
"performance-now": "^2.1.0"
}
@@ -24912,6 +24880,26 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/simplebar-core": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/simplebar-core/-/simplebar-core-1.3.2.tgz",
"integrity": "sha512-qKgTTuTqapjsFGkNhCjyPhysnbZGpQqNmjk0nOYjFN5ordC/Wjvg+RbYCyMSnW60l/Z0ZS82GbNltly6PMUH1w==",
"dependencies": {
"lodash": "^4.17.21",
"lodash-es": "^4.17.21"
}
},
"node_modules/simplebar-react": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/simplebar-react/-/simplebar-react-3.3.2.tgz",
"integrity": "sha512-ZsgcQhKLtt5ra0BRIJeApfkTBQCa1vUPA/WXI4HcYReFt+oCEOvdVz6rR/XsGJcKxTlCRPmdGx1uJIUChupo+A==",
"dependencies": {
"simplebar-core": "^1.3.2"
},
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/sisteransi": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
@@ -26602,19 +26590,6 @@
"node": ">= 0.4"
}
},
"node_modules/to-camel-case": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/to-camel-case/-/to-camel-case-1.0.0.tgz",
"integrity": "sha512-nD8pQi5H34kyu1QDMFjzEIYqk0xa9Alt6ZfrdEMuHCFOfTLhDG5pgTu/aAM9Wt9lXILwlXmWP43b8sav0GNE8Q==",
"dependencies": {
"to-space-case": "^1.0.0"
}
},
"node_modules/to-no-case": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz",
"integrity": "sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg=="
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -26626,14 +26601,6 @@
"node": ">=8.0"
}
},
"node_modules/to-space-case": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz",
"integrity": "sha512-rLdvwXZ39VOn1IxGL3V6ZstoTbwLRckQmn/U8ZDLuWwIXNpuZDhQ3AiRUlhTbOXFVE9C+dR51wM0CBDhk31VcA==",
"dependencies": {
"to-no-case": "^1.0.0"
}
},
"node_modules/toggle-selection": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",