[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 lens: 57135
licenseManagement: 41817 licenseManagement: 41817
licensing: 29004 licensing: 29004
links: 44490 links: 8000
lists: 22900 lists: 22900
logsDataAccess: 16759 logsDataAccess: 16759
logsExplorer: 60000 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'; import { i18n } from '@kbn/i18n';
export const LinksStrings = { export const LinksStrings = {
getDescription: () =>
i18n.translate('links.description', {
defaultMessage: 'Use links to navigate to commonly used dashboards and websites.',
}),
embeddable: { embeddable: {
getUnsupportedLinkTypeError: () => getUnsupportedLinkTypeError: () =>
i18n.translate('links.embeddable.unsupportedLinkTypeError', { i18n.translate('links.embeddable.unsupportedLinkTypeError', {

View file

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