Lazy load reporting (#80492) (#80805)

* perf: ️ load dynamically reporting management section

* refactor: 💡 remove JSX from main plugin entry file

* perf: ️ lazy-load CSV sharing panel React component

* perf: ️ lazy-load screen capture sharing panel React components

* feat: 🎸 show spinner while shring panels are loading
This commit is contained in:
Vadim Dalecky 2020-10-16 16:00:38 +02:00 committed by GitHub
parent 1b8c092fdb
commit bb5968c2d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 152 additions and 42 deletions

View file

@ -0,0 +1,22 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import * as React from 'react';
import { EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui';
export const PanelSpinner: React.FC = (props) => {
return (
<>
<EuiSpacer />
<EuiFlexGroup justifyContent="spaceAround">
<EuiFlexItem grow={false}>
<EuiLoadingSpinner size="l" />
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
</>
);
};

View file

@ -13,7 +13,7 @@ import { toMountPoint } from '../../../../../src/plugins/kibana_react/public';
import { BaseParams } from '../../common/types';
import { ReportingAPIClient } from '../lib/reporting_api_client';
interface Props {
export interface Props {
apiClient: ReportingAPIClient;
toasts: ToastsSetup;
reportType: string;

View file

@ -0,0 +1,24 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import * as React from 'react';
import { lazy, Suspense, FC } from 'react';
import { PanelSpinner } from './panel_spinner';
import type { Props } from './reporting_panel_content';
const LazyComponent = lazy(() =>
import('./reporting_panel_content').then(({ ReportingPanelContent }) => ({
default: ReportingPanelContent,
}))
);
export const ReportingPanelContent: FC<Omit<Props, 'intl'>> = (props) => {
return (
<Suspense fallback={<PanelSpinner />}>
<LazyComponent {...props} />
</Suspense>
);
};

View file

@ -12,7 +12,7 @@ import { BaseParams } from '../../common/types';
import { ReportingAPIClient } from '../lib/reporting_api_client';
import { ReportingPanelContent } from './reporting_panel_content';
interface Props {
export interface Props {
apiClient: ReportingAPIClient;
toasts: ToastsSetup;
reportType: string;

View file

@ -0,0 +1,24 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import * as React from 'react';
import { lazy, Suspense, FC } from 'react';
import { PanelSpinner } from './panel_spinner';
import type { Props } from './screen_capture_panel_content';
const LazyComponent = lazy(() =>
import('./screen_capture_panel_content').then(({ ScreenCapturePanelContent }) => ({
default: ScreenCapturePanelContent,
}))
);
export const ScreenCapturePanelContent: FC<Props> = (props) => {
return (
<Suspense fallback={<PanelSpinner />}>
<LazyComponent {...props} />
</Suspense>
);
};

View file

@ -0,0 +1,42 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import * as React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { I18nProvider } from '@kbn/i18n/react';
import { CoreSetup, CoreStart } from 'src/core/public';
import { Observable } from 'rxjs';
import { ReportListing } from './components/report_listing';
import { ManagementAppMountParams } from '../../../../src/plugins/management/public';
import { ILicense } from '../../licensing/public';
import { ClientConfigType } from './plugin';
import { ReportingAPIClient } from './lib/reporting_api_client';
export async function mountManagementSection(
coreSetup: CoreSetup,
coreStart: CoreStart,
license$: Observable<ILicense>,
pollConfig: ClientConfigType['poll'],
apiClient: ReportingAPIClient,
params: ManagementAppMountParams
) {
render(
<I18nProvider>
<ReportListing
toasts={coreSetup.notifications.toasts}
license$={license$}
pollConfig={pollConfig}
redirect={coreStart.application.navigateToApp}
apiClient={apiClient}
/>
</I18nProvider>,
params.element
);
return () => {
unmountComponentAtNode(params.element);
};
}

View file

@ -5,9 +5,6 @@
*/
import { i18n } from '@kbn/i18n';
import { I18nProvider } from '@kbn/i18n/react';
import React from 'react';
import ReactDOM from 'react-dom';
import * as Rx from 'rxjs';
import { catchError, filter, map, mergeMap, takeUntil } from 'rxjs/operators';
import {
@ -17,21 +14,21 @@ import {
Plugin,
PluginInitializerContext,
} from 'src/core/public';
import { UiActionsSetup } from 'src/plugins/ui_actions/public';
import { UiActionsSetup, UiActionsStart } from 'src/plugins/ui_actions/public';
import { CONTEXT_MENU_TRIGGER } from '../../../../src/plugins/embeddable/public';
import {
FeatureCatalogueCategory,
HomePublicPluginSetup,
HomePublicPluginStart,
} from '../../../../src/plugins/home/public';
import { ManagementSetup } from '../../../../src/plugins/management/public';
import { SharePluginSetup } from '../../../../src/plugins/share/public';
import { LicensingPluginSetup } from '../../licensing/public';
import { ManagementSetup, ManagementStart } from '../../../../src/plugins/management/public';
import { SharePluginSetup, SharePluginStart } from '../../../../src/plugins/share/public';
import { LicensingPluginSetup, LicensingPluginStart } from '../../licensing/public';
import { durationToNumber } from '../common/schema_utils';
import { JobId, ReportingConfigType } from '../common/types';
import { JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY } from '../constants';
import { JobSummarySet } from './';
import { getGeneralErrorToast } from './components';
import { ReportListing } from './components/report_listing';
import { ReportingAPIClient } from './lib/reporting_api_client';
import { ReportingNotifierStreamHandler as StreamHandler } from './lib/stream_handler';
import { GetCsvReportPanelAction } from './panel_actions/get_csv_panel_action';
@ -60,7 +57,25 @@ function handleError(notifications: NotificationsSetup, err: Error): Rx.Observab
return Rx.of({ completed: [], failed: [] });
}
export class ReportingPublicPlugin implements Plugin<void, void> {
export interface ReportingPublicPluginSetupDendencies {
home: HomePublicPluginSetup;
management: ManagementSetup;
licensing: LicensingPluginSetup;
uiActions: UiActionsSetup;
share: SharePluginSetup;
}
export interface ReportingPublicPluginStartDendencies {
home: HomePublicPluginStart;
management: ManagementStart;
licensing: LicensingPluginStart;
uiActions: UiActionsStart;
share: SharePluginStart;
}
export class ReportingPublicPlugin
implements
Plugin<void, void, ReportingPublicPluginSetupDendencies, ReportingPublicPluginStartDendencies> {
private config: ClientConfigType;
private readonly stop$ = new Rx.ReplaySubject(1);
private readonly title = i18n.translate('xpack.reporting.management.reportingTitle', {
@ -76,19 +91,7 @@ export class ReportingPublicPlugin implements Plugin<void, void> {
public setup(
core: CoreSetup,
{
home,
management,
licensing,
uiActions,
share,
}: {
home: HomePublicPluginSetup;
management: ManagementSetup;
licensing: LicensingPluginSetup;
uiActions: UiActionsSetup;
share: SharePluginSetup;
}
{ home, management, licensing, uiActions, share }: ReportingPublicPluginSetupDendencies
) {
const {
http,
@ -119,24 +122,19 @@ export class ReportingPublicPlugin implements Plugin<void, void> {
title: this.title,
order: 1,
mount: async (params) => {
const [start] = await getStartServices();
params.setBreadcrumbs([{ text: this.breadcrumbText }]);
ReactDOM.render(
<I18nProvider>
<ReportListing
toasts={toasts}
license$={license$}
pollConfig={this.config.poll}
redirect={start.application.navigateToApp}
apiClient={apiClient}
/>
</I18nProvider>,
params.element
const [[start], { mountManagementSection }] = await Promise.all([
getStartServices(),
import('./mount_management_section'),
]);
return await mountManagementSection(
core,
start,
license$,
this.config.poll,
apiClient,
params
);
return () => {
ReactDOM.unmountComponentAtNode(params.element);
};
},
});

View file

@ -11,7 +11,7 @@ import { IUiSettingsClient, ToastsSetup } from 'src/core/public';
import { ShareContext } from '../../../../../src/plugins/share/public';
import { LicensingPluginSetup } from '../../../licensing/public';
import { JobParamsCSV, SearchRequest } from '../../server/export_types/csv/types';
import { ReportingPanelContent } from '../components/reporting_panel_content';
import { ReportingPanelContent } from '../components/reporting_panel_content_lazy';
import { checkLicense } from '../lib/license_check';
import { ReportingAPIClient } from '../lib/reporting_api_client';

View file

@ -13,7 +13,7 @@ import { LicensingPluginSetup } from '../../../licensing/public';
import { LayoutParams } from '../../common/types';
import { JobParamsPNG } from '../../server/export_types/png/types';
import { JobParamsPDF } from '../../server/export_types/printable_pdf/types';
import { ScreenCapturePanelContent } from '../components/screen_capture_panel_content';
import { ScreenCapturePanelContent } from '../components/screen_capture_panel_content_lazy';
import { checkLicense } from '../lib/license_check';
import { ReportingAPIClient } from '../lib/reporting_api_client';