[links] lazy load links actions (#208100)

Page load bundle is reduced by
* lazy load add panel action
* avoid importing `deserialize_from_library.ts` in page load bundle
* avoid loading `links_strings.ts` in page load bundle

---------

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Nathan Reese 2025-02-03 19:40:26 -07:00 committed by GitHub
parent 8bf657ec9d
commit 0fee502ffc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 97 additions and 92 deletions

View file

@ -96,7 +96,7 @@ pageLoadAssetSize:
lens: 57135
licenseManagement: 41817
licensing: 29004
links: 44490
links: 8000
lists: 22900
logsDataAccess: 16759
logsExplorer: 60000

View file

@ -0,0 +1,52 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import type { EmbeddableApiContext } from '@kbn/presentation-publishing';
import { IncompatibleActionError } from '@kbn/ui-actions-plugin/public';
import { ADD_PANEL_ANNOTATION_GROUP } from '@kbn/embeddable-plugin/public';
import type { ActionDefinition } from '@kbn/ui-actions-plugin/public/actions';
import { apiIsPresentationContainer } from '@kbn/presentation-containers';
import {
apiPublishesDescription,
apiPublishesTitle,
apiPublishesSavedObjectId,
} from '@kbn/presentation-publishing';
import type { LinksParentApi } from '../types';
import { APP_ICON, APP_NAME, CONTENT_ID } from '../../common';
import { ADD_LINKS_PANEL_ACTION_ID } from './constants';
import { openEditorFlyout } from '../editor/open_editor_flyout';
export const isParentApiCompatible = (parentApi: unknown): parentApi is LinksParentApi =>
apiIsPresentationContainer(parentApi) &&
apiPublishesSavedObjectId(parentApi) &&
apiPublishesTitle(parentApi) &&
apiPublishesDescription(parentApi);
export const addLinksPanelAction: ActionDefinition<EmbeddableApiContext> = {
id: ADD_LINKS_PANEL_ACTION_ID,
getIconType: () => APP_ICON,
order: 10,
isCompatible: async ({ embeddable }) => {
return isParentApiCompatible(embeddable);
},
execute: async ({ embeddable }) => {
if (!isParentApiCompatible(embeddable)) throw new IncompatibleActionError();
const runtimeState = await openEditorFlyout({
parentDashboard: embeddable,
});
if (!runtimeState) return;
await embeddable.addNewPanel({
panelType: CONTENT_ID,
initialState: runtimeState,
});
},
grouping: [ADD_PANEL_ANNOTATION_GROUP],
getDisplayName: () => APP_NAME,
};

View file

@ -1,22 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { apiIsPresentationContainer } from '@kbn/presentation-containers';
import {
apiPublishesDescription,
apiPublishesTitle,
apiPublishesSavedObjectId,
} from '@kbn/presentation-publishing';
import { LinksParentApi } from '../types';
export const isParentApiCompatible = (parentApi: unknown): parentApi is LinksParentApi =>
apiIsPresentationContainer(parentApi) &&
apiPublishesSavedObjectId(parentApi) &&
apiPublishesTitle(parentApi) &&
apiPublishesDescription(parentApi);

View file

@ -0,0 +1,10 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
export const ADD_LINKS_PANEL_ACTION_ID = 'create_links_panel';

View file

@ -1,45 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { EmbeddableApiContext } from '@kbn/presentation-publishing';
import { ADD_PANEL_TRIGGER, IncompatibleActionError } from '@kbn/ui-actions-plugin/public';
import { ADD_PANEL_ANNOTATION_GROUP } from '@kbn/embeddable-plugin/public';
import { APP_ICON, APP_NAME, CONTENT_ID } from '../../common';
import { uiActions } from '../services/kibana_services';
const ADD_LINKS_PANEL_ACTION_ID = 'create_links_panel';
export const registerCreateLinksPanelAction = () => {
uiActions.registerAction<EmbeddableApiContext>({
id: ADD_LINKS_PANEL_ACTION_ID,
getIconType: () => APP_ICON,
order: 10,
isCompatible: async ({ embeddable }) => {
const { isParentApiCompatible } = await import('./compatibility_check');
return isParentApiCompatible(embeddable);
},
execute: async ({ embeddable }) => {
const { isParentApiCompatible } = await import('./compatibility_check');
if (!isParentApiCompatible(embeddable)) throw new IncompatibleActionError();
const { openEditorFlyout } = await import('../editor/open_editor_flyout');
const runtimeState = await openEditorFlyout({
parentDashboard: embeddable,
});
if (!runtimeState) return;
await embeddable.addNewPanel({
panelType: CONTENT_ID,
initialState: runtimeState,
});
},
grouping: [ADD_PANEL_ANNOTATION_GROUP],
getDisplayName: () => APP_NAME,
});
uiActions.attachAction(ADD_PANEL_TRIGGER, ADD_LINKS_PANEL_ACTION_ID);
};

View file

@ -10,10 +10,6 @@
import { i18n } from '@kbn/i18n';
export const LinksStrings = {
getDescription: () =>
i18n.translate('links.description', {
defaultMessage: 'Use links to navigate to commonly used dashboards and websites.',
}),
embeddable: {
getUnsupportedLinkTypeError: () =>
i18n.translate('links.embeddable.unsupportedLinkTypeError', {

View file

@ -49,7 +49,7 @@ import {
linksSerializeStateIsByReference,
} from '../lib/deserialize_from_library';
import { serializeLinksAttributes } from '../lib/serialize_attributes';
import { isParentApiCompatible } from '../actions/compatibility_check';
import { isParentApiCompatible } from '../actions/add_links_panel_action';
export const LinksContext = createContext<LinksApi | null>(null);

View file

@ -7,6 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { i18n } from '@kbn/i18n';
import {
ContentManagementPublicSetup,
ContentManagementPublicStart,
@ -23,14 +24,14 @@ import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import { VisualizationsSetup } from '@kbn/visualizations-plugin/public';
import { UiActionsPublicStart } from '@kbn/ui-actions-plugin/public/plugin';
import { ADD_PANEL_TRIGGER } from '@kbn/ui-actions-plugin/public';
import { LinksRuntimeState } from './types';
import { APP_ICON, APP_NAME, CONTENT_ID, LATEST_VERSION } from '../common';
import { LinksCrudTypes } from '../common/content_management';
import { LinksStrings } from './components/links_strings';
import { getLinksClient } from './content_management/links_content_management_client';
import { setKibanaServices, untilPluginStartServicesReady } from './services/kibana_services';
import { registerCreateLinksPanelAction } from './actions/create_links_panel_action';
import { deserializeLinksSavedObject } from './lib/deserialize_from_library';
import { setKibanaServices } from './services/kibana_services';
import { ADD_LINKS_PANEL_ACTION_ID } from './actions/constants';
export interface LinksSetupDependencies {
embeddable: EmbeddableSetup;
visualizations: VisualizationsSetup;
@ -63,6 +64,7 @@ export class LinksPlugin
plugins.embeddable.registerAddFromLibraryType({
onAdd: async (container, savedObject) => {
const { deserializeLinksSavedObject } = await import('./lib/deserialize_from_library');
const initialState = await deserializeLinksSavedObject(savedObject);
container.addNewPanel<LinksRuntimeState>({
panelType: CONTENT_ID,
@ -84,7 +86,9 @@ export class LinksPlugin
name: CONTENT_ID,
title: APP_NAME,
icon: APP_ICON,
description: LinksStrings.getDescription(),
description: i18n.translate('links.description', {
defaultMessage: 'Use links to navigate to commonly used dashboards and websites.',
}),
stage: 'production',
appExtensions: {
visualizations: {
@ -100,7 +104,11 @@ export class LinksPlugin
title,
editor: {
onEdit: async (savedObjectId: string) => {
const { openEditorFlyout } = await import('./editor/open_editor_flyout');
const [{ openEditorFlyout }, { deserializeLinksSavedObject }] =
await Promise.all([
import('./editor/open_editor_flyout'),
import('./lib/deserialize_from_library'),
]);
const linksSavedObject = await getLinksClient().get(savedObjectId);
const initialState = await deserializeLinksSavedObject(linksSavedObject.item);
await openEditorFlyout({ initialState });
@ -122,20 +130,26 @@ export class LinksPlugin
public start(core: CoreStart, plugins: LinksStartDependencies) {
setKibanaServices(core, plugins);
untilPluginStartServicesReady().then(() => {
registerCreateLinksPanelAction();
plugins.dashboard.registerDashboardPanelPlacementSetting(
CONTENT_ID,
async (runtimeState?: LinksRuntimeState) => {
if (!runtimeState) return {};
const isHorizontal = runtimeState.layout === 'horizontal';
const width = isHorizontal ? DASHBOARD_GRID_COLUMN_COUNT : 8;
const height = isHorizontal ? 4 : (runtimeState.links?.length ?? 1 * 3) + 4;
return { width, height, strategy: PanelPlacementStrategy.placeAtTop };
}
);
});
plugins.uiActions.addTriggerActionAsync(
ADD_PANEL_TRIGGER,
ADD_LINKS_PANEL_ACTION_ID,
async () => {
const { addLinksPanelAction } = await import('./actions/add_links_panel_action');
return addLinksPanelAction;
}
);
plugins.dashboard.registerDashboardPanelPlacementSetting(
CONTENT_ID,
async (runtimeState?: LinksRuntimeState) => {
if (!runtimeState) return {};
const isHorizontal = runtimeState.layout === 'horizontal';
const width = isHorizontal ? DASHBOARD_GRID_COLUMN_COUNT : 8;
const height = isHorizontal ? 4 : (runtimeState.links?.length ?? 1 * 3) + 4;
return { width, height, strategy: PanelPlacementStrategy.placeAtTop };
}
);
return {};
}