[MM-59299] Investigate further breaking down chunks of components/node_modules for initial load (#27845)

Этот коммит содержится в:
M-ZubairAhmed
2024-09-04 05:55:47 +00:00
коммит произвёл GitHub
родитель e104575505
Коммит 7232feecf7
19 изменённых файлов: 213 добавлений и 191 удалений

Просмотреть файл

@@ -13,6 +13,7 @@ exports[`components/FileUploadOverlay should match snapshot when file upload is
<img <img
alt="Files" alt="Files"
className="overlay__files" className="overlay__files"
loading="lazy"
src="" src=""
/> />
<span> <span>
@@ -28,6 +29,7 @@ exports[`components/FileUploadOverlay should match snapshot when file upload is
<img <img
alt="Logo" alt="Logo"
className="overlay__logo" className="overlay__logo"
loading="lazy"
src="" src=""
/> />
</div> </div>
@@ -48,6 +50,7 @@ exports[`components/FileUploadOverlay should match snapshot when file upload is
<img <img
alt="Files" alt="Files"
className="overlay__files" className="overlay__files"
loading="lazy"
src="" src=""
/> />
<span> <span>
@@ -63,6 +66,7 @@ exports[`components/FileUploadOverlay should match snapshot when file upload is
<img <img
alt="Logo" alt="Logo"
className="overlay__logo" className="overlay__logo"
loading="lazy"
src="" src=""
/> />
</div> </div>
@@ -83,6 +87,7 @@ exports[`components/FileUploadOverlay should match snapshot when file upload is
<img <img
alt="Files" alt="Files"
className="overlay__files" className="overlay__files"
loading="lazy"
src="" src=""
/> />
<span> <span>
@@ -98,6 +103,7 @@ exports[`components/FileUploadOverlay should match snapshot when file upload is
<img <img
alt="Logo" alt="Logo"
className="overlay__logo" className="overlay__logo"
loading="lazy"
src="" src=""
/> />
</div> </div>

Просмотреть файл

@@ -2,7 +2,7 @@
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import classNames from 'classnames'; import classNames from 'classnames';
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import React, {lazy, useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {FormattedMessage, useIntl} from 'react-intl'; import {FormattedMessage, useIntl} from 'react-intl';
import {useDispatch, useSelector} from 'react-redux'; import {useDispatch, useSelector} from 'react-redux';
@@ -24,9 +24,9 @@ import {makeGetDraft} from 'selectors/rhs';
import {connectionErrorCount} from 'selectors/views/system'; import {connectionErrorCount} from 'selectors/views/system';
import LocalStorageStore from 'stores/local_storage_store'; import LocalStorageStore from 'stores/local_storage_store';
import {makeAsyncComponent} from 'components/async_load';
import AutoHeightSwitcher from 'components/common/auto_height_switcher'; import AutoHeightSwitcher from 'components/common/auto_height_switcher';
import useDidUpdate from 'components/common/hooks/useDidUpdate'; import useDidUpdate from 'components/common/hooks/useDidUpdate';
import FileLimitStickyBanner from 'components/file_limit_sticky_banner';
import MessageSubmitError from 'components/message_submit_error'; import MessageSubmitError from 'components/message_submit_error';
import MsgTyping from 'components/msg_typing'; import MsgTyping from 'components/msg_typing';
import RhsSuggestionList from 'components/suggestion/rhs_suggestion_list'; import RhsSuggestionList from 'components/suggestion/rhs_suggestion_list';
@@ -66,6 +66,8 @@ import useUploadFiles from './use_upload_files';
import './advanced_text_editor.scss'; import './advanced_text_editor.scss';
const FileLimitStickyBanner = makeAsyncComponent('FileLimitStickyBanner', lazy(() => import('components/file_limit_sticky_banner')));
function isDraftEmpty(draft: PostDraft) { function isDraftEmpty(draft: PostDraft) {
return draft.message === '' && draft.fileInfos.length === 0 && draft.uploadsInProgress.length === 0; return draft.message === '' && draft.fileInfos.length === 0 && draft.uploadsInProgress.length === 0;
} }

Просмотреть файл

@@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import React from 'react'; import React, {lazy} from 'react';
import {hot} from 'react-hot-loader/root'; import {hot} from 'react-hot-loader/root';
import {Provider} from 'react-redux'; import {Provider} from 'react-redux';
import {Router} from 'react-router-dom'; import {Router} from 'react-router-dom';
@@ -11,7 +11,7 @@ import store from 'stores/redux_store';
import {makeAsyncComponent} from 'components/async_load'; import {makeAsyncComponent} from 'components/async_load';
import {getHistory} from 'utils/browser_history'; import {getHistory} from 'utils/browser_history';
const LazyRoot = React.lazy(() => import('components/root')); const LazyRoot = lazy(() => import('components/root'));
const Root = makeAsyncComponent('Root', LazyRoot); const Root = makeAsyncComponent('Root', LazyRoot);

Просмотреть файл

@@ -2,43 +2,34 @@
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import classNames from 'classnames'; import classNames from 'classnames';
import React from 'react'; import React, {lazy} from 'react';
import {Route, Switch, Redirect} from 'react-router-dom'; import {Route, Switch, Redirect} from 'react-router-dom';
import {makeAsyncComponent} from 'components/async_load'; import {makeAsyncComponent} from 'components/async_load';
import ChannelIdentifierRouter from 'components/channel_layout/channel_identifier_router'; import ChannelIdentifierRouter from 'components/channel_layout/channel_identifier_router';
import PlaybookRunner from 'components/channel_layout/playbook_runner';
import LoadingScreen from 'components/loading_screen'; import LoadingScreen from 'components/loading_screen';
import PermalinkView from 'components/permalink_view';
import {IDENTIFIER_PATH_PATTERN, ID_PATH_PATTERN, TEAM_NAME_PATH_PATTERN} from 'utils/path'; import {IDENTIFIER_PATH_PATTERN, ID_PATH_PATTERN, TEAM_NAME_PATH_PATTERN} from 'utils/path';
import type {OwnProps, PropsFromRedux} from './index'; import type {OwnProps, PropsFromRedux} from './index';
const LazyChannelHeaderMobile = makeAsyncComponent( const ChannelHeaderMobile = makeAsyncComponent('ChannelHeaderMobile', lazy(() => import('components/channel_header_mobile')));
'LazyChannelHeaderMobile', const GlobalThreads = makeAsyncComponent('GlobalThreads', lazy(() => import('components/threading/global_threads')),
React.lazy(() => import('components/channel_header_mobile')),
);
const LazyGlobalThreads = makeAsyncComponent(
'LazyGlobalThreads',
React.lazy(() => import('components/threading/global_threads')),
( (
<div className='app__content'> <div className='app__content'>
<LoadingScreen/> <LoadingScreen/>
</div> </div>
), ),
); );
const Drafts = makeAsyncComponent('Drafts', lazy(() => import('components/drafts')),
const LazyDrafts = makeAsyncComponent(
'LazyDrafts',
React.lazy(() => import('components/drafts')),
( (
<div className='app__content'> <div className='app__content'>
<LoadingScreen/> <LoadingScreen/>
</div> </div>
), ),
); );
const PermalinkView = makeAsyncComponent('PermalinkView', lazy(() => import('components/permalink_view')));
const PlaybookRunner = makeAsyncComponent('PlaybookRunner', lazy(() => import('components/channel_layout/playbook_runner')));
type Props = PropsFromRedux & OwnProps; type Props = PropsFromRedux & OwnProps;
@@ -85,11 +76,13 @@ export default class CenterChannel extends React.PureComponent<Props, State> {
})} })}
> >
{isMobileView && ( {isMobileView && (
<div className='row header'> <>
<div id='navbar_wrapper'> <div className='row header'>
<LazyChannelHeaderMobile/> <div id='navbar_wrapper'>
<ChannelHeaderMobile/>
</div>
</div> </div>
</div> </>
)} )}
<div className='row main'> <div className='row main'>
<Switch> <Switch>
@@ -114,12 +107,12 @@ export default class CenterChannel extends React.PureComponent<Props, State> {
{isCollapsedThreadsEnabled ? ( {isCollapsedThreadsEnabled ? (
<Route <Route
path={`/:team(${TEAM_NAME_PATH_PATTERN})/threads/:threadIdentifier(${ID_PATH_PATTERN})?`} path={`/:team(${TEAM_NAME_PATH_PATTERN})/threads/:threadIdentifier(${ID_PATH_PATTERN})?`}
component={LazyGlobalThreads} component={GlobalThreads}
/> />
) : null} ) : null}
<Route <Route
path={`/:team(${TEAM_NAME_PATH_PATTERN})/drafts`} path={`/:team(${TEAM_NAME_PATH_PATTERN})/drafts`}
component={LazyDrafts} component={Drafts}
/> />
<Redirect to={lastChannelPath}/> <Redirect to={lastChannelPath}/>

Просмотреть файл

@@ -2,7 +2,7 @@
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import classNames from 'classnames'; import classNames from 'classnames';
import React, {useEffect} from 'react'; import React, {lazy, useEffect} from 'react';
import {useDispatch, useSelector} from 'react-redux'; import {useDispatch, useSelector} from 'react-redux';
import {cleanUpStatusAndProfileFetchingPoll} from 'mattermost-redux/actions/status_profile_polling'; import {cleanUpStatusAndProfileFetchingPoll} from 'mattermost-redux/actions/status_profile_polling';
@@ -10,10 +10,9 @@ import {getIsUserStatusesConfigEnabled} from 'mattermost-redux/selectors/entitie
import {addVisibleUsersInCurrentChannelToStatusPoll} from 'actions/status_actions'; import {addVisibleUsersInCurrentChannelToStatusPoll} from 'actions/status_actions';
import {makeAsyncComponent} from 'components/async_load';
import CenterChannel from 'components/channel_layout/center_channel'; import CenterChannel from 'components/channel_layout/center_channel';
import LoadingScreen from 'components/loading_screen'; import LoadingScreen from 'components/loading_screen';
import ProductNoticesModal from 'components/product_notices_modal';
import ResetStatusModal from 'components/reset_status_modal';
import Sidebar from 'components/sidebar'; import Sidebar from 'components/sidebar';
import CRTPostsChannelResetWatcher from 'components/threading/channel_threads/posts_channel_reset_watcher'; import CRTPostsChannelResetWatcher from 'components/threading/channel_threads/posts_channel_reset_watcher';
import UnreadsStatusHandler from 'components/unreads_status_handler'; import UnreadsStatusHandler from 'components/unreads_status_handler';
@@ -22,6 +21,9 @@ import Pluggable from 'plugins/pluggable';
import {Constants} from 'utils/constants'; import {Constants} from 'utils/constants';
import {isInternetExplorer, isEdge} from 'utils/user_agent'; import {isInternetExplorer, isEdge} from 'utils/user_agent';
const ProductNoticesModal = makeAsyncComponent('ProductNoticesModal', lazy(() => import('components/product_notices_modal')));
const ResetStatusModal = makeAsyncComponent('ResetStatusModal', lazy(() => import('components/reset_status_modal')));
const BODY_CLASS_FOR_CHANNEL = ['app__body', 'channel-view']; const BODY_CLASS_FOR_CHANNEL = ['app__body', 'channel-view'];
type Props = { type Props = {

Просмотреть файл

@@ -8,7 +8,7 @@ exports[`components/channel_view Should match snapshot if channel is archived 1`
<FileUploadOverlay <FileUploadOverlay
overlayType="center" overlayType="center"
/> />
<withRouter(Connect(injectIntl(ChannelHeader))) <ChannelHeader
channelId="channelId" channelId="channelId"
channelIsArchived={true} channelIsArchived={true}
deactivatedChannel={false} deactivatedChannel={false}
@@ -66,7 +66,7 @@ exports[`components/channel_view Should match snapshot if channel is deactivated
<FileUploadOverlay <FileUploadOverlay
overlayType="center" overlayType="center"
/> />
<withRouter(Connect(injectIntl(ChannelHeader))) <ChannelHeader
channelId="channelId" channelId="channelId"
channelIsArchived={false} channelIsArchived={false}
deactivatedChannel={true} deactivatedChannel={true}
@@ -123,7 +123,7 @@ exports[`components/channel_view Should match snapshot with base props 1`] = `
<FileUploadOverlay <FileUploadOverlay
overlayType="center" overlayType="center"
/> />
<withRouter(Connect(injectIntl(ChannelHeader))) <ChannelHeader
channelId="channelId" channelId="channelId"
channelIsArchived={false} channelIsArchived={false}
deactivatedChannel={false} deactivatedChannel={false}
@@ -152,7 +152,7 @@ exports[`components/channel_view Should match snapshot with base props 1`] = `
data-testid="post-create" data-testid="post-create"
id="post-create" id="post-create"
> >
<Memo(AdvancedCreatePost) /> <AdvancedCreatePost />
</div> </div>
</div> </div>
`; `;

Просмотреть файл

@@ -1,15 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import React from 'react'; import React, {lazy} from 'react';
import {FormattedMessage} from 'react-intl'; import {FormattedMessage} from 'react-intl';
import type {RouteComponentProps} from 'react-router-dom'; import type {RouteComponentProps} from 'react-router-dom';
import AdvancedCreatePost from 'components/advanced_create_post'; import {makeAsyncComponent} from 'components/async_load';
import ChannelBookmarks from 'components/channel_bookmarks';
import ChannelHeader from 'components/channel_header';
import deferComponentRender from 'components/deferComponentRender'; import deferComponentRender from 'components/deferComponentRender';
import FileUploadOverlay from 'components/file_upload_overlay';
import FormattedMarkdownMessage from 'components/formatted_markdown_message'; import FormattedMarkdownMessage from 'components/formatted_markdown_message';
import PostView from 'components/post_view'; import PostView from 'components/post_view';
@@ -17,6 +14,11 @@ import WebSocketClient from 'client/web_websocket_client';
import type {PropsFromRedux} from './index'; import type {PropsFromRedux} from './index';
const ChannelHeader = makeAsyncComponent('ChannelHeader', lazy(() => import('components/channel_header')));
const FileUploadOverlay = makeAsyncComponent('FileUploadOverlay', lazy(() => import('components/file_upload_overlay')));
const ChannelBookmarks = makeAsyncComponent('ChannelBookmarks', lazy(() => import('components/channel_bookmarks')));
const AdvancedCreatePost = makeAsyncComponent('AdvancedCreatePost', lazy(() => import('components/advanced_create_post')));
export type Props = PropsFromRedux & RouteComponentProps<{ export type Props = PropsFromRedux & RouteComponentProps<{
postid?: string; postid?: string;
}>; }>;

Просмотреть файл

@@ -29,6 +29,7 @@ const FileUploadOverlay = (props: Props) => {
className='overlay__files' className='overlay__files'
src={fileOverlayImage} src={fileOverlayImage}
alt='Files' alt='Files'
loading='lazy'
/> />
<span> <span>
<i <i
@@ -44,6 +45,7 @@ const FileUploadOverlay = (props: Props) => {
className='overlay__logo' className='overlay__logo'
src={overlayLogoImage} src={overlayLogoImage}
alt='Logo' alt='Logo'
loading='lazy'
/> />
</div> </div>
</div> </div>

Просмотреть файл

@@ -0,0 +1,40 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {lazy} from 'react';
import type {RouteComponentProps} from 'react-router-dom';
import {Route} from 'react-router-dom';
import type {Theme} from 'mattermost-redux/selectors/entities/preferences';
import {makeAsyncComponent} from 'components/async_load';
import CompassThemeProvider from 'components/compass_theme_provider/compass_theme_provider';
import LoggedIn from 'components/logged_in';
const OnBoardingTaskList = makeAsyncComponent('OnboardingTaskList', lazy(() => import('components/onboarding_tasklist')));
type Props = {
component: React.ComponentType<RouteComponentProps<any>>;
path: string | string[];
theme?: Theme; // the routes that send the theme are the ones that will actually need to show the onboarding tasklist
};
export default function LoggedInRoute(props: Props) {
const {component: Component, theme, ...rest} = props;
return (
<Route
{...rest}
render={(routeProps) => (
<LoggedIn {...routeProps}>
{theme && (
<CompassThemeProvider theme={theme}>
<OnBoardingTaskList/>
</CompassThemeProvider>
)}
<Component {...(routeProps)}/>
</LoggedIn>
)}
/>
);
}

Просмотреть файл

@@ -3,7 +3,7 @@
import classNames from 'classnames'; import classNames from 'classnames';
import deepEqual from 'fast-deep-equal'; import deepEqual from 'fast-deep-equal';
import React from 'react'; import React, {lazy} from 'react';
import {Route, Switch, Redirect} from 'react-router-dom'; import {Route, Switch, Redirect} from 'react-router-dom';
import type {RouteComponentProps} from 'react-router-dom'; import type {RouteComponentProps} from 'react-router-dom';
@@ -13,42 +13,31 @@ import {setSystemEmojis} from 'mattermost-redux/actions/emojis';
import {setUrl} from 'mattermost-redux/actions/general'; import {setUrl} from 'mattermost-redux/actions/general';
import {Client4} from 'mattermost-redux/client'; import {Client4} from 'mattermost-redux/client';
import {rudderAnalytics, RudderTelemetryHandler} from 'mattermost-redux/client/rudder'; import {rudderAnalytics, RudderTelemetryHandler} from 'mattermost-redux/client/rudder';
import type {Theme} from 'mattermost-redux/selectors/entities/preferences';
import {measurePageLoadTelemetry, temporarilySetPageLoadContext, trackEvent, trackSelectorMetrics} from 'actions/telemetry_actions.jsx'; import {measurePageLoadTelemetry, temporarilySetPageLoadContext, trackEvent, trackSelectorMetrics} from 'actions/telemetry_actions.jsx';
import BrowserStore from 'stores/browser_store'; import BrowserStore from 'stores/browser_store';
import AccessProblem from 'components/access_problem';
import AnnouncementBarController from 'components/announcement_bar';
import AppBar from 'components/app_bar/app_bar';
import {makeAsyncComponent} from 'components/async_load'; import {makeAsyncComponent} from 'components/async_load';
import CloudEffects from 'components/cloud_effects';
import CompassThemeProvider from 'components/compass_theme_provider/compass_theme_provider';
import OpenPluginInstallPost from 'components/custom_open_plugin_install_post_renderer'; import OpenPluginInstallPost from 'components/custom_open_plugin_install_post_renderer';
import GlobalHeader from 'components/global_header/global_header'; import GlobalHeader from 'components/global_header/global_header';
import {HFRoute} from 'components/header_footer_route/header_footer_route'; import {HFRoute} from 'components/header_footer_route/header_footer_route';
import {HFTRoute, LoggedInHFTRoute} from 'components/header_footer_template_route'; import {HFTRoute, LoggedInHFTRoute} from 'components/header_footer_template_route';
import InitialLoadingScreen from 'components/initial_loading_screen'; import InitialLoadingScreen from 'components/initial_loading_screen';
import MobileViewWatcher from 'components/mobile_view_watcher'; import LoggedIn from 'components/logged_in';
import ModalController from 'components/modal_controller'; import LoggedInRoute from 'components/logged_in_route';
import LaunchingWorkspace, {LAUNCHING_WORKSPACE_FULLSCREEN_Z_INDEX} from 'components/preparing_workspace/launching_workspace'; import {LAUNCHING_WORKSPACE_FULLSCREEN_Z_INDEX} from 'components/preparing_workspace/launching_workspace';
import {Animations} from 'components/preparing_workspace/steps'; import {Animations} from 'components/preparing_workspace/steps';
import SidebarRight from 'components/sidebar_right'; import SidebarMobileRightMenu from 'components/sidebar_mobile_right_menu';
import SidebarRightMenu from 'components/sidebar_right_menu';
import SystemNotice from 'components/system_notice';
import TeamSidebar from 'components/team_sidebar';
import WindowSizeObserver from 'components/window_size_observer/WindowSizeObserver';
import webSocketClient from 'client/web_websocket_client'; import webSocketClient from 'client/web_websocket_client';
import {initializePlugins} from 'plugins'; import {initializePlugins} from 'plugins';
import Pluggable from 'plugins/pluggable';
import A11yController from 'utils/a11y_controller'; import A11yController from 'utils/a11y_controller';
import {PageLoadContext} from 'utils/constants'; import {PageLoadContext} from 'utils/constants';
import {EmojiIndicesByAlias} from 'utils/emoji'; import {EmojiIndicesByAlias} from 'utils/emoji';
import {TEAM_NAME_PATH_PATTERN} from 'utils/path'; import {TEAM_NAME_PATH_PATTERN} from 'utils/path';
import {getSiteURL} from 'utils/url'; import {getSiteURL} from 'utils/url';
import * as UserAgent from 'utils/user_agent'; import {isAndroidWeb, isChromebook, isDesktopApp, isIosWeb} from 'utils/user_agent';
import * as Utils from 'utils/utils'; import {applyTheme, isTextDroppableEvent} from 'utils/utils';
import LuxonController from './luxon_controller'; import LuxonController from './luxon_controller';
import PerformanceReporterController from './performance_reporter_controller'; import PerformanceReporterController from './performance_reporter_controller';
@@ -59,68 +48,36 @@ import type {PropsFromRedux} from './index';
import 'plugins/export.js'; import 'plugins/export.js';
const LazyErrorPage = React.lazy(() => import('components/error_page')); const MobileViewWatcher = makeAsyncComponent('MobileViewWatcher', lazy(() => import('components/mobile_view_watcher')));
const LazyLogin = React.lazy(() => import('components/login/login')); const WindowSizeObserver = makeAsyncComponent('WindowSizeObserver', lazy(() => import('components/window_size_observer/WindowSizeObserver')));
const LazyAdminConsole = React.lazy(() => import('components/admin_console')); const ErrorPage = makeAsyncComponent('ErrorPage', lazy(() => import('components/error_page')));
const LazyLoggedIn = React.lazy(() => import('components/logged_in')); const Login = makeAsyncComponent('LoginController', lazy(() => import('components/login/login')));
const LazyPasswordResetSendLink = React.lazy(() => import('components/password_reset_send_link')); const AccessProblem = makeAsyncComponent('AccessProblem', lazy(() => import('components/access_problem')));
const LazyPasswordResetForm = React.lazy(() => import('components/password_reset_form')); const PasswordResetSendLink = makeAsyncComponent('PasswordResedSendLink', lazy(() => import('components/password_reset_send_link')));
const LazySignup = React.lazy(() => import('components/signup/signup')); const PasswordResetForm = makeAsyncComponent('PasswordResetForm', lazy(() => import('components/password_reset_form')));
const LazyTermsOfService = React.lazy(() => import('components/terms_of_service')); const Signup = makeAsyncComponent('SignupController', lazy(() => import('components/signup/signup')));
const LazyShouldVerifyEmail = React.lazy(() => import('components/should_verify_email/should_verify_email')); const ShouldVerifyEmail = makeAsyncComponent('ShouldVerifyEmail', lazy(() => import('components/should_verify_email/should_verify_email')));
const LazyDoVerifyEmail = React.lazy(() => import('components/do_verify_email/do_verify_email')); const DoVerifyEmail = makeAsyncComponent('DoVerifyEmail', lazy(() => import('components/do_verify_email/do_verify_email')));
const LazyClaimController = React.lazy(() => import('components/claim')); const ClaimController = makeAsyncComponent('ClaimController', lazy(() => import('components/claim')));
const LazyLinkingLandingPage = React.lazy(() => import('components/linking_landing_page')); const TermsOfService = makeAsyncComponent('TermsOfService', lazy(() => import('components/terms_of_service')));
const LazySelectTeam = React.lazy(() => import('components/select_team')); const LinkingLandingPage = makeAsyncComponent('LinkingLandingPage', lazy(() => import('components/linking_landing_page')));
const LazyAuthorize = React.lazy(() => import('components/authorize')); const AdminConsole = makeAsyncComponent('AdminConsole', lazy(() => import('components/admin_console')));
const LazyCreateTeam = React.lazy(() => import('components/create_team')); const SelectTeam = makeAsyncComponent('SelectTeam', lazy(() => import('components/select_team')));
const LazyMfa = React.lazy(() => import('components/mfa/mfa_controller')); const Authorize = makeAsyncComponent('Authorize', lazy(() => import('components/authorize')));
const LazyPreparingWorkspace = React.lazy(() => import('components/preparing_workspace')); const CreateTeam = makeAsyncComponent('CreateTeam', lazy(() => import('components/create_team')));
const LazyTeamController = React.lazy(() => import('components/team_controller')); const Mfa = makeAsyncComponent('Mfa', lazy(() => import('components/mfa/mfa_controller')));
const LazyOnBoardingTaskList = React.lazy(() => import('components/onboarding_tasklist')); const PreparingWorkspace = makeAsyncComponent('PreparingWorkspace', lazy(() => import('components/preparing_workspace')));
const Pluggable = makeAsyncComponent('Pluggable', lazy(() => import('plugins/pluggable')));
const CreateTeam = makeAsyncComponent('CreateTeam', LazyCreateTeam); const LaunchingWorkspace = makeAsyncComponent('LaunchingWorkspace', lazy(() => import('components/preparing_workspace/launching_workspace')));
const ErrorPage = makeAsyncComponent('ErrorPage', LazyErrorPage); const CompassThemeProvider = makeAsyncComponent('CompassThemeProvider', lazy(() => import('components/compass_theme_provider/compass_theme_provider')));
const TermsOfService = makeAsyncComponent('TermsOfService', LazyTermsOfService); const TeamController = makeAsyncComponent('TeamController', lazy(() => import('components/team_controller')));
const Login = makeAsyncComponent('LoginController', LazyLogin); const AnnouncementBarController = makeAsyncComponent('AnnouncementBarController', lazy(() => import('components/announcement_bar')));
const AdminConsole = makeAsyncComponent('AdminConsole', LazyAdminConsole); const SystemNotice = makeAsyncComponent('SystemNotice', lazy(() => import('components/system_notice')));
const LoggedIn = makeAsyncComponent('LoggedIn', LazyLoggedIn); const CloudEffects = makeAsyncComponent('CloudEffects', lazy(() => import('components/cloud_effects')));
const PasswordResetSendLink = makeAsyncComponent('PasswordResedSendLink', LazyPasswordResetSendLink); const TeamSidebar = makeAsyncComponent('TeamSidebar', lazy(() => import('components/team_sidebar')));
const PasswordResetForm = makeAsyncComponent('PasswordResetForm', LazyPasswordResetForm); const SidebarRight = makeAsyncComponent('SidebarRight', lazy(() => import('components/sidebar_right')));
const Signup = makeAsyncComponent('SignupController', LazySignup); const ModalController = makeAsyncComponent('ModalController', lazy(() => import('components/modal_controller')));
const ShouldVerifyEmail = makeAsyncComponent('ShouldVerifyEmail', LazyShouldVerifyEmail); const AppBar = makeAsyncComponent('AppBar', lazy(() => import('components/app_bar/app_bar')));
const DoVerifyEmail = makeAsyncComponent('DoVerifyEmail', LazyDoVerifyEmail);
const ClaimController = makeAsyncComponent('ClaimController', LazyClaimController);
const LinkingLandingPage = makeAsyncComponent('LinkingLandingPage', LazyLinkingLandingPage);
const SelectTeam = makeAsyncComponent('SelectTeam', LazySelectTeam);
const Authorize = makeAsyncComponent('Authorize', LazyAuthorize);
const Mfa = makeAsyncComponent('Mfa', LazyMfa);
const PreparingWorkspace = makeAsyncComponent('PreparingWorkspace', LazyPreparingWorkspace);
const TeamController = makeAsyncComponent('TeamController', LazyTeamController);
const OnBoardingTaskList = makeAsyncComponent('OnboardingTaskList', LazyOnBoardingTaskList);
type LoggedInRouteProps = {
component: React.ComponentType<RouteComponentProps<any>>;
path: string | string[];
theme?: Theme; // the routes that send the theme are the ones that will actually need to show the onboarding tasklist
};
function LoggedInRoute(props: LoggedInRouteProps) {
const {component: Component, theme, ...rest} = props;
return (
<Route
{...rest}
render={(routeProps) => (
<LoggedIn {...routeProps}>
{theme && <CompassThemeProvider theme={theme}>
<OnBoardingTaskList/>
</CompassThemeProvider>}
<Component {...(routeProps)}/>
</LoggedIn>
)}
/>
);
}
const noop = () => {}; const noop = () => {};
@@ -138,7 +95,6 @@ export default class Root extends React.PureComponent<Props, State> {
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
// Redux
setUrl(getSiteURL()); setUrl(getSiteURL());
// Disable auth header to enable CSRF check // Disable auth header to enable CSRF check
@@ -146,24 +102,6 @@ export default class Root extends React.PureComponent<Props, State> {
setSystemEmojis(new Set(EmojiIndicesByAlias.keys())); setSystemEmojis(new Set(EmojiIndicesByAlias.keys()));
// Force logout of all tabs if one tab is logged out
window.addEventListener('storage', this.handleLogoutLoginSignal);
// Prevent drag and drop files from navigating away from the app
document.addEventListener('drop', (e) => {
if (e.dataTransfer && e.dataTransfer.items.length > 0 && e.dataTransfer.items[0].kind === 'file') {
e.preventDefault();
e.stopPropagation();
}
});
document.addEventListener('dragover', (e) => {
if (!Utils.isTextDroppableEvent(e) && !document.body.classList.contains('focalboard-body')) {
e.preventDefault();
e.stopPropagation();
}
});
this.state = { this.state = {
shouldMountAppRoutes: false, shouldMountAppRoutes: false,
}; };
@@ -251,23 +189,23 @@ export default class Root extends React.PureComponent<Props, State> {
this.showLandingPageIfNecessary(); this.showLandingPageIfNecessary();
Utils.applyTheme(this.props.theme); applyTheme(this.props.theme);
}; };
private showLandingPageIfNecessary = () => { private showLandingPageIfNecessary = () => {
// We have nothing to redirect to if we're already on Desktop App // We have nothing to redirect to if we're already on Desktop App
// Chromebook has no Desktop App to switch to // Chromebook has no Desktop App to switch to
if (UserAgent.isDesktopApp() || UserAgent.isChromebook()) { if (isDesktopApp() || isChromebook()) {
return; return;
} }
// Nothing to link to if we've removed the Android App download link // Nothing to link to if we've removed the Android App download link
if (UserAgent.isAndroidWeb() && !this.props.androidDownloadLink) { if (isAndroidWeb() && !this.props.androidDownloadLink) {
return; return;
} }
// Nothing to link to if we've removed the iOS App download link // Nothing to link to if we've removed the iOS App download link
if (UserAgent.isIosWeb() && !this.props.iosDownloadLink) { if (isIosWeb() && !this.props.iosDownloadLink) {
return; return;
} }
@@ -312,7 +250,7 @@ export default class Root extends React.PureComponent<Props, State> {
componentDidUpdate(prevProps: Props, prevState: State) { componentDidUpdate(prevProps: Props, prevState: State) {
if (!deepEqual(prevProps.theme, this.props.theme)) { if (!deepEqual(prevProps.theme, this.props.theme)) {
Utils.applyTheme(this.props.theme); applyTheme(this.props.theme);
} }
if (this.props.location.pathname === '/') { if (this.props.location.pathname === '/') {
@@ -384,6 +322,20 @@ export default class Root extends React.PureComponent<Props, State> {
} }
}; };
handleDropEvent = (e: DragEvent) => {
if (e.dataTransfer && e.dataTransfer.items.length > 0 && e.dataTransfer.items[0].kind === 'file') {
e.preventDefault();
e.stopPropagation();
}
};
handleDragOverEvent = (e: DragEvent) => {
if (!isTextDroppableEvent(e) && !document.body.classList.contains('focalboard-body')) {
e.preventDefault();
e.stopPropagation();
}
};
componentDidMount() { componentDidMount() {
temporarilySetPageLoadContext(PageLoadContext.PAGE_LOAD); temporarilySetPageLoadContext(PageLoadContext.PAGE_LOAD);
@@ -394,10 +346,20 @@ export default class Root extends React.PureComponent<Props, State> {
measurePageLoadTelemetry(); measurePageLoadTelemetry();
trackSelectorMetrics(); trackSelectorMetrics();
// Force logout of all tabs if one tab is logged out
window.addEventListener('storage', this.handleLogoutLoginSignal);
// Prevent drag and drop files from navigating away from the app
document.addEventListener('drop', this.handleDropEvent);
document.addEventListener('dragover', this.handleDragOverEvent);
} }
componentWillUnmount() { componentWillUnmount() {
window.removeEventListener('storage', this.handleLogoutLoginSignal); window.removeEventListener('storage', this.handleLogoutLoginSignal);
document.removeEventListener('drop', this.handleDropEvent);
document.removeEventListener('dragover', this.handleDragOverEvent);
} }
handleLogoutLoginSignal = (e: StorageEvent) => { handleLogoutLoginSignal = (e: StorageEvent) => {
@@ -600,7 +562,7 @@ export default class Root extends React.PureComponent<Props, State> {
</div> </div>
<Pluggable pluggableName='Global'/> <Pluggable pluggableName='Global'/>
<AppBar/> <AppBar/>
<SidebarRightMenu/> <SidebarMobileRightMenu/>
</CompassThemeProvider> </CompassThemeProvider>
</Switch> </Switch>
</RootProvider> </RootProvider>

Просмотреть файл

@@ -85,7 +85,7 @@ exports[`components/sidebar should match snapshot when direct channels modal is
onDragStart={[Function]} onDragStart={[Function]}
/> />
<Connect(DataPrefetch) /> <Connect(DataPrefetch) />
<Connect(MoreDirectChannels) <MoreDirectChannels
isExistingChannel={false} isExistingChannel={false}
onModalDismissed={[Function]} onModalDismissed={[Function]}
/> />

Просмотреть файл

@@ -9,7 +9,7 @@ import type {DeepPartial} from '@mattermost/types/utilities';
import {Preferences} from 'mattermost-redux/constants'; import {Preferences} from 'mattermost-redux/constants';
import mergeObjects from 'packages/mattermost-redux/test/merge_objects'; import mergeObjects from 'packages/mattermost-redux/test/merge_objects';
import {renderWithContext, screen} from 'tests/react_testing_utils'; import {renderWithContext, screen, waitFor} from 'tests/react_testing_utils';
import Constants, {ModalIdentifiers} from 'utils/constants'; import Constants, {ModalIdentifiers} from 'utils/constants';
import {TestHelper} from 'utils/test_helper'; import {TestHelper} from 'utils/test_helper';
@@ -164,7 +164,7 @@ describe('components/sidebar', () => {
}, },
}; };
test('should not render unreads category when disabled by user preference', () => { test('should not render unreads category when disabled by user preference', async () => {
const testState = { const testState = {
entities: { entities: {
channels: { channels: {
@@ -185,10 +185,12 @@ describe('components/sidebar', () => {
mergeObjects(baseState, testState), mergeObjects(baseState, testState),
); );
expect(screen.queryByText('UNREADS')).not.toBeInTheDocument(); await waitFor(() => {
expect(screen.queryByText('UNREADS')).not.toBeInTheDocument();
});
}); });
test('should render unreads category when there are unread channels', () => { test('should render unreads category when there are unread channels', async () => {
const testState: DeepPartial<GlobalState> = { const testState: DeepPartial<GlobalState> = {
entities: { entities: {
channels: { channels: {
@@ -209,10 +211,12 @@ describe('components/sidebar', () => {
mergeObjects(baseState, testState), mergeObjects(baseState, testState),
); );
expect(screen.queryByText('UNREADS')).toBeInTheDocument(); await waitFor(() => {
expect(screen.queryByText('UNREADS')).toBeInTheDocument();
});
}); });
test('should not render unreads category when there are no unread channels', () => { test('should not render unreads category when there are no unread channels', async () => {
const testState: DeepPartial<GlobalState> = { const testState: DeepPartial<GlobalState> = {
entities: { entities: {
preferences: { preferences: {
@@ -228,10 +232,12 @@ describe('components/sidebar', () => {
mergeObjects(baseState, testState), mergeObjects(baseState, testState),
); );
expect(screen.queryByText('UNREADS')).not.toBeInTheDocument(); await waitFor(() => {
expect(screen.queryByText('UNREADS')).not.toBeInTheDocument();
});
}); });
test('should render unreads category when there are no unread channels but the current channel was previously unread', () => { test('should render unreads category when there are no unread channels but the current channel was previously unread', async () => {
const testState: DeepPartial<GlobalState> = { const testState: DeepPartial<GlobalState> = {
entities: { entities: {
preferences: { preferences: {
@@ -252,7 +258,9 @@ describe('components/sidebar', () => {
mergeObjects(baseState, testState), mergeObjects(baseState, testState),
); );
expect(screen.queryByText('UNREADS')).toBeInTheDocument(); await waitFor(() => {
expect(screen.queryByText('UNREADS')).toBeInTheDocument();
});
}); });
}); });
}); });

Просмотреть файл

@@ -2,34 +2,36 @@
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import classNames from 'classnames'; import classNames from 'classnames';
import React from 'react'; import React, {lazy} from 'react';
import {trackEvent} from 'actions/telemetry_actions'; import {trackEvent} from 'actions/telemetry_actions';
import BrowseChannels from 'components/browse_channels'; import {makeAsyncComponent} from 'components/async_load';
import CreateUserGroupsModal from 'components/create_user_groups_modal';
import DataPrefetch from 'components/data_prefetch'; import DataPrefetch from 'components/data_prefetch';
import EditCategoryModal from 'components/edit_category_modal';
import InvitationModal from 'components/invitation_modal';
import KeyboardShortcutsModal from 'components/keyboard_shortcuts/keyboard_shortcuts_modal/keyboard_shortcuts_modal';
import MoreDirectChannels from 'components/more_direct_channels';
import NewChannelModal from 'components/new_channel_modal/new_channel_modal';
import ResizableLhs from 'components/resizable_sidebar/resizable_lhs'; import ResizableLhs from 'components/resizable_sidebar/resizable_lhs';
import UserSettingsModal from 'components/user_settings/modal'; import SidebarHeader from 'components/sidebar/sidebar_header';
import Pluggable from 'plugins/pluggable'; import Pluggable from 'plugins/pluggable';
import Constants, {ModalIdentifiers, RHSStates} from 'utils/constants'; import Constants, {ModalIdentifiers, RHSStates} from 'utils/constants';
import * as Keyboard from 'utils/keyboard'; import {isKeyPressed, cmdOrCtrlPressed} from 'utils/keyboard';
import * as Utils from 'utils/utils'; import {localizeMessage} from 'utils/utils';
import type {ModalData} from 'types/actions'; import type {ModalData} from 'types/actions';
import type {RhsState} from 'types/store/rhs'; import type {RhsState} from 'types/store/rhs';
import ChannelNavigator from './channel_navigator'; import ChannelNavigator from './channel_navigator';
import MobileSidebarHeader from './mobile_sidebar_header';
import SidebarHeader from './sidebar_header';
import SidebarList from './sidebar_list'; import SidebarList from './sidebar_list';
const MobileSidebarHeader = makeAsyncComponent('MobileSidebarHeader', lazy(() => import('./mobile_sidebar_header')));
const MoreDirectChannels = makeAsyncComponent('MoreDirectChannels', lazy(() => import('components/more_direct_channels')));
const BrowseChannels = makeAsyncComponent('BrowseChannels', lazy(() => import('components/browse_channels')));
const EditCategoryModal = makeAsyncComponent('EditCategoryModal', lazy(() => import('components/edit_category_modal')));
const CreateUserGroupsModal = makeAsyncComponent('CreateUserGroupsModal', lazy(() => import('components/create_user_groups_modal')));
const InvitationModal = makeAsyncComponent('InvitationModal', lazy(() => import('components/invitation_modal')));
const KeyboardShortcutsModal = makeAsyncComponent('KeyboardShortcutsModal', lazy(() => import('components/keyboard_shortcuts/keyboard_shortcuts_modal/keyboard_shortcuts_modal')));
const NewChannelModal = makeAsyncComponent('NewChannelModal', lazy(() => import('components/new_channel_modal/new_channel_modal')));
const UserSettingsModal = makeAsyncComponent('UserSettingsModal', lazy(() => import('components/user_settings/modal')));
type Props = { type Props = {
teamId: string; teamId: string;
canCreatePublicChannel: boolean; canCreatePublicChannel: boolean;
@@ -95,15 +97,15 @@ export default class Sidebar extends React.PureComponent<Props, State> {
}; };
handleKeyDownEvent = (event: KeyboardEvent) => { handleKeyDownEvent = (event: KeyboardEvent) => {
if (Keyboard.isKeyPressed(event, Constants.KeyCodes.ESCAPE)) { if (isKeyPressed(event, Constants.KeyCodes.ESCAPE)) {
this.props.actions.clearChannelSelection(); this.props.actions.clearChannelSelection();
return; return;
} }
const ctrlOrMetaKeyPressed = Keyboard.cmdOrCtrlPressed(event, true); const ctrlOrMetaKeyPressed = cmdOrCtrlPressed(event, true);
if (ctrlOrMetaKeyPressed) { if (ctrlOrMetaKeyPressed) {
if (Keyboard.isKeyPressed(event, Constants.KeyCodes.FORWARD_SLASH)) { if (isKeyPressed(event, Constants.KeyCodes.FORWARD_SLASH)) {
event.preventDefault(); event.preventDefault();
if (this.props.isKeyBoardShortcutModalOpen) { if (this.props.isKeyBoardShortcutModalOpen) {
this.props.actions.closeModal(ModalIdentifiers.KEYBOARD_SHORTCUTS_MODAL); this.props.actions.closeModal(ModalIdentifiers.KEYBOARD_SHORTCUTS_MODAL);
@@ -113,7 +115,7 @@ export default class Sidebar extends React.PureComponent<Props, State> {
dialogType: KeyboardShortcutsModal, dialogType: KeyboardShortcutsModal,
}); });
} }
} else if (Keyboard.isKeyPressed(event, Constants.KeyCodes.A) && event.shiftKey) { } else if (isKeyPressed(event, Constants.KeyCodes.A) && event.shiftKey) {
event.preventDefault(); event.preventDefault();
this.props.actions.openModal({ this.props.actions.openModal({
@@ -225,7 +227,7 @@ export default class Sidebar extends React.PureComponent<Props, State> {
return (<div/>); return (<div/>);
} }
const ariaLabel = Utils.localizeMessage('accessibility.sections.lhsNavigator', 'channel navigator region'); const ariaLabel = localizeMessage('accessibility.sections.lhsNavigator', 'channel navigator region');
return ( return (
<ResizableLhs <ResizableLhs

Просмотреть файл

@@ -3,7 +3,7 @@
exports[`SidebarList should match snapshot 1`] = ` exports[`SidebarList should match snapshot 1`] = `
<Fragment> <Fragment>
<GlobalThreadsLink /> <GlobalThreadsLink />
<Memo(DraftsLink) /> <DraftsLink />
<div <div
aria-label="channel sidebar region" aria-label="channel sidebar region"
className="SidebarNavContainer a11y__region" className="SidebarNavContainer a11y__region"

Просмотреть файл

@@ -3,7 +3,7 @@
import classNames from 'classnames'; import classNames from 'classnames';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import React from 'react'; import React, {lazy} from 'react';
import type {CSSProperties} from 'react'; import type {CSSProperties} from 'react';
import {DragDropContext, Droppable} from 'react-beautiful-dnd'; import {DragDropContext, Droppable} from 'react-beautiful-dnd';
import type {DropResult, DragStart, BeforeCapture} from 'react-beautiful-dnd'; import type {DropResult, DragStart, BeforeCapture} from 'react-beautiful-dnd';
@@ -20,20 +20,21 @@ import {General} from 'mattermost-redux/constants';
import {trackEvent} from 'actions/telemetry_actions'; import {trackEvent} from 'actions/telemetry_actions';
import DraftsLink from 'components/drafts/drafts_link/drafts_link'; import {makeAsyncComponent} from 'components/async_load';
import GlobalThreadsLink from 'components/threading/global_threads_link'; import SidebarCategory from 'components/sidebar/sidebar_category';
import * as ChannelUtils from 'utils/channel_utils'; import {findNextUnreadChannelId} from 'utils/channel_utils';
import {Constants, DraggingStates, DraggingStateTypes} from 'utils/constants'; import {Constants, DraggingStates, DraggingStateTypes} from 'utils/constants';
import * as Keyboard from 'utils/keyboard'; import {isKeyPressed, cmdOrCtrlPressed} from 'utils/keyboard';
import * as Utils from 'utils/utils'; import {localizeMessage, mod} from 'utils/utils';
import type {DraggingState} from 'types/store'; import type {DraggingState} from 'types/store';
import type {StaticPage} from 'types/store/lhs'; import type {StaticPage} from 'types/store/lhs';
import SidebarCategory from '../sidebar_category'; const DraftsLink = makeAsyncComponent('DraftsLink', lazy(() => import('components/drafts/drafts_link/drafts_link')));
import UnreadChannelIndicator from '../unread_channel_indicator'; const GlobalThreadsLink = makeAsyncComponent('GlobalThreadsLink', lazy(() => import('components/threading/global_threads_link')));
import UnreadChannels from '../unread_channels'; const UnreadChannelIndicator = makeAsyncComponent('UnreadChannelIndicator', lazy(() => import('../unread_channel_indicator')));
const UnreadChannels = makeAsyncComponent('UnreadChannels', lazy(() => import('../unread_channels')));
export function renderView(props: React.HTMLProps<HTMLDivElement>) { export function renderView(props: React.HTMLProps<HTMLDivElement>) {
return ( return (
@@ -318,7 +319,7 @@ export default class SidebarList extends React.PureComponent<Props, State> {
}; };
navigateChannelShortcut = (e: KeyboardEvent) => { navigateChannelShortcut = (e: KeyboardEvent) => {
if (e.altKey && !e.shiftKey && !e.ctrlKey && !e.metaKey && (Keyboard.isKeyPressed(e, Constants.KeyCodes.UP) || Keyboard.isKeyPressed(e, Constants.KeyCodes.DOWN))) { if (e.altKey && !e.shiftKey && !e.ctrlKey && !e.metaKey && (isKeyPressed(e, Constants.KeyCodes.UP) || isKeyPressed(e, Constants.KeyCodes.DOWN))) {
e.preventDefault(); e.preventDefault();
const staticPageIds = this.getDisplayedStaticPageIds(); const staticPageIds = this.getDisplayedStaticPageIds();
@@ -328,24 +329,24 @@ export default class SidebarList extends React.PureComponent<Props, State> {
const curIndex = allIds.indexOf(curSelectedId); const curIndex = allIds.indexOf(curSelectedId);
let nextIndex; let nextIndex;
if (Keyboard.isKeyPressed(e, Constants.KeyCodes.DOWN)) { if (isKeyPressed(e, Constants.KeyCodes.DOWN)) {
nextIndex = curIndex + 1; nextIndex = curIndex + 1;
} else { } else {
nextIndex = curIndex - 1; nextIndex = curIndex - 1;
} }
const nextId = allIds[Utils.mod(nextIndex, allIds.length)]; const nextId = allIds[mod(nextIndex, allIds.length)];
this.navigateById(nextId); this.navigateById(nextId);
if (nextIndex >= staticPageIds.length) { if (nextIndex >= staticPageIds.length) {
this.scrollToChannel(nextId); this.scrollToChannel(nextId);
} }
} else if (Keyboard.cmdOrCtrlPressed(e) && e.shiftKey && Keyboard.isKeyPressed(e, Constants.KeyCodes.K)) { } else if (cmdOrCtrlPressed(e) && e.shiftKey && isKeyPressed(e, Constants.KeyCodes.K)) {
this.props.handleOpenMoreDirectChannelsModal(e); this.props.handleOpenMoreDirectChannelsModal(e);
} }
}; };
navigateUnreadChannelShortcut = (e: KeyboardEvent) => { navigateUnreadChannelShortcut = (e: KeyboardEvent) => {
if (e.altKey && e.shiftKey && !e.ctrlKey && !e.metaKey && (Keyboard.isKeyPressed(e, Constants.KeyCodes.UP) || Keyboard.isKeyPressed(e, Constants.KeyCodes.DOWN))) { if (e.altKey && e.shiftKey && !e.ctrlKey && !e.metaKey && (isKeyPressed(e, Constants.KeyCodes.UP) || isKeyPressed(e, Constants.KeyCodes.DOWN))) {
e.preventDefault(); e.preventDefault();
const allChannelIds = this.getDisplayedChannelIds(); const allChannelIds = this.getDisplayedChannelIds();
@@ -360,13 +361,13 @@ export default class SidebarList extends React.PureComponent<Props, State> {
} }
let direction = 0; let direction = 0;
if (Keyboard.isKeyPressed(e, Constants.KeyCodes.UP)) { if (isKeyPressed(e, Constants.KeyCodes.UP)) {
direction = -1; direction = -1;
} else { } else {
direction = 1; direction = 1;
} }
const nextIndex = ChannelUtils.findNextUnreadChannelId( const nextIndex = findNextUnreadChannelId(
this.props.currentChannelId, this.props.currentChannelId,
allChannelIds, allChannelIds,
unreadChannelIds, unreadChannelIds,
@@ -546,7 +547,7 @@ export default class SidebarList extends React.PureComponent<Props, State> {
/> />
); );
const ariaLabel = Utils.localizeMessage('accessibility.sections.lhsList', 'channel sidebar region'); const ariaLabel = localizeMessage('accessibility.sections.lhsList', 'channel sidebar region');
return ( return (

Просмотреть файл

@@ -11,7 +11,7 @@ import {getIsMobileView} from 'selectors/views/browser';
import type {GlobalState} from 'types/store'; import type {GlobalState} from 'types/store';
import SidebarRightMenu from './sidebar_right_menu'; import SidebarMobileRightMenu from './sidebar_mobile_right_menu';
function mapStateToProps(state: GlobalState) { function mapStateToProps(state: GlobalState) {
const config = getConfig(state); const config = getConfig(state);
@@ -27,4 +27,4 @@ function mapStateToProps(state: GlobalState) {
}; };
} }
export default connect(mapStateToProps)(SidebarRightMenu); export default connect(mapStateToProps)(SidebarMobileRightMenu);

Просмотреть файл

@@ -131,7 +131,9 @@ describe('identifyElementRegion', () => {
}, },
); );
expect(identifyElementRegion(screen.getAllByText(channel.display_name)[0])).toEqual('channel_sidebar'); await waitFor(() => {
expect(identifyElementRegion(screen.getAllByText(channel.display_name)[0])).toEqual('channel_sidebar');
});
expect(identifyElementRegion(screen.getAllByText(channel.display_name)[1])).toEqual('channel_header'); expect(identifyElementRegion(screen.getAllByText(channel.display_name)[1])).toEqual('channel_header');
expect(identifyElementRegion(screen.getAllByText(channel.header)[0])).toEqual('channel_header'); expect(identifyElementRegion(screen.getAllByText(channel.header)[0])).toEqual('channel_header');