diff --git a/examples/embeddable_examples/public/app/presentation_container_example/components/add_button.tsx b/examples/embeddable_examples/public/app/presentation_container_example/components/add_button.tsx
index 570cd50f44ea2..a2f11f55cd757 100644
--- a/examples/embeddable_examples/public/app/presentation_container_example/components/add_button.tsx
+++ b/examples/embeddable_examples/public/app/presentation_container_example/components/add_button.tsx
@@ -24,36 +24,24 @@ export function AddButton({ pageApi, uiActions }: { pageApi: unknown; uiActions:
id: ADD_PANEL_TRIGGER,
},
};
- const actionsPromises = uiActions.getTriggerActions(ADD_PANEL_TRIGGER).map(async (action) => {
- return {
- isCompatible: await action.isCompatible(actionContext),
- action,
- };
- });
- Promise.all(actionsPromises).then((actions) => {
- if (cancelled) {
- return;
- }
+ uiActions.getTriggerCompatibleActions(ADD_PANEL_TRIGGER, actionContext).then((actions) => {
+ if (cancelled) return;
- const nextItems = actions
- .filter(
- ({ action, isCompatible }) => isCompatible && action.id !== 'ACTION_CREATE_ESQL_CHART'
- )
- .map(({ action }) => {
- return (
- {
- action.execute(actionContext);
- setIsPopoverOpen(false);
- }}
- >
- {action.getDisplayName(actionContext)}
-
- );
- });
+ const nextItems = actions.map((action) => {
+ return (
+ {
+ action.execute(actionContext);
+ setIsPopoverOpen(false);
+ }}
+ >
+ {action.getDisplayName(actionContext)}
+
+ );
+ });
setItems(nextItems);
});
diff --git a/src/platform/plugins/private/presentation_panel/public/index.ts b/src/platform/plugins/private/presentation_panel/public/index.ts
index 50a896e144283..a33dd20d1ccd5 100644
--- a/src/platform/plugins/private/presentation_panel/public/index.ts
+++ b/src/platform/plugins/private/presentation_panel/public/index.ts
@@ -13,7 +13,8 @@ export function plugin() {
return new PresentationPanelPlugin();
}
-export { getEditPanelAction, ACTION_CUSTOMIZE_PANEL } from './panel_actions';
+export { ACTION_CUSTOMIZE_PANEL } from './panel_actions/customize_panel_action/constants';
+export { ACTION_EDIT_PANEL } from './panel_actions/edit_panel_action/constants';
export { PresentationPanel } from './panel_component';
export type { PresentationPanelProps } from './panel_component/types';
export { PresentationPanelError } from './panel_component/presentation_panel_error';
diff --git a/src/platform/plugins/private/presentation_panel/public/kibana_services.ts b/src/platform/plugins/private/presentation_panel/public/kibana_services.ts
index e8d27541700d1..87deceee1880e 100644
--- a/src/platform/plugins/private/presentation_panel/public/kibana_services.ts
+++ b/src/platform/plugins/private/presentation_panel/public/kibana_services.ts
@@ -9,8 +9,8 @@
import { BehaviorSubject } from 'rxjs';
-import { CoreStart } from '@kbn/core/public';
-import { PresentationPanelStartDependencies } from './plugin';
+import type { CoreStart } from '@kbn/core/public';
+import type { PresentationPanelStartDependencies } from './plugin';
export let core: CoreStart;
export let uiActions: PresentationPanelStartDependencies['uiActions'];
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/constants.ts b/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/constants.ts
new file mode 100644
index 0000000000000..bc8bc08721957
--- /dev/null
+++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/constants.ts
@@ -0,0 +1,11 @@
+/*
+ * 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 ACTION_CUSTOMIZE_PANEL = 'ACTION_CUSTOMIZE_PANEL';
+export const CUSTOM_TIME_RANGE_BADGE = 'CUSTOM_TIME_RANGE_BADGE';
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/custom_time_range_badge.tsx b/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/custom_time_range_badge.tsx
index 7a0cd6ae7ab08..506c4c10d0424 100644
--- a/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/custom_time_range_badge.tsx
+++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/custom_time_range_badge.tsx
@@ -10,6 +10,7 @@
import { PrettyDuration } from '@elastic/eui';
import {
Action,
+ ActionExecutionMeta,
FrequentCompatibilityChangeAction,
IncompatibleActionError,
} from '@kbn/ui-actions-plugin/public';
@@ -17,10 +18,8 @@ import React from 'react';
import { UI_SETTINGS } from '@kbn/data-plugin/common';
import { apiPublishesTimeRange, EmbeddableApiContext } from '@kbn/presentation-publishing';
-import { core } from '../../kibana_services';
-import { customizePanelAction } from '../panel_actions';
-
-export const CUSTOM_TIME_RANGE_BADGE = 'CUSTOM_TIME_RANGE_BADGE';
+import { ACTION_CUSTOMIZE_PANEL, CUSTOM_TIME_RANGE_BADGE } from './constants';
+import { core, uiActions } from '../../kibana_services';
export class CustomTimeRangeBadge
implements Action, FrequentCompatibilityChangeAction
@@ -69,8 +68,9 @@ export class CustomTimeRangeBadge
});
}
- public async execute({ embeddable }: EmbeddableApiContext) {
- customizePanelAction.execute({ embeddable });
+ public async execute(context: ActionExecutionMeta & EmbeddableApiContext) {
+ const action = await uiActions.getAction(ACTION_CUSTOMIZE_PANEL);
+ action.execute(context);
}
public getIconType() {
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/customize_panel_action.tsx b/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/customize_panel_action.tsx
index 997ce1d70ad5b..66ac3ff49b99e 100644
--- a/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/customize_panel_action.tsx
+++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/customize_panel_action.tsx
@@ -26,8 +26,7 @@ import {
} from '@kbn/presentation-publishing';
import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public';
import { openCustomizePanelFlyout } from './open_customize_panel';
-
-export const ACTION_CUSTOMIZE_PANEL = 'ACTION_CUSTOMIZE_PANEL';
+import { ACTION_CUSTOMIZE_PANEL } from './constants';
export type CustomizePanelActionApi = CanAccessViewMode &
Partial<
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/filters_details.tsx b/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/filters_details.tsx
index b9849d3c80a5a..e8973792a17c9 100644
--- a/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/filters_details.tsx
+++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/filters_details.tsx
@@ -16,8 +16,8 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { hasEditCapabilities } from '@kbn/presentation-publishing';
import { FilterItems } from '@kbn/unified-search-plugin/public';
-import { editPanelAction } from '../panel_actions';
import { CustomizePanelActionApi } from './customize_panel_action';
+import { executeEditPanelAction } from '../edit_panel_action/execute_edit_action';
export const filterDetailsActionStrings = {
getQueryTitle: () =>
@@ -75,7 +75,7 @@ export function FiltersDetails({ editMode, api }: FiltersDetailsProps) {
editPanelAction.execute({ embeddable: api })}
+ onClick={() => executeEditPanelAction(api)}
aria-label={i18n.translate(
'presentationPanel.action.customizePanel.flyout.optionsMenuForm.editQueryButtonAriaLabel',
{
@@ -112,7 +112,7 @@ export function FiltersDetails({ editMode, api }: FiltersDetailsProps) {
editPanelAction.execute({ embeddable: api })}
+ onClick={() => executeEditPanelAction(api)}
aria-label={i18n.translate(
'presentationPanel.action.customizePanel.flyout.optionsMenuForm.editFiltersButtonAriaLabel',
{
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/edit_panel_action/constants.ts b/src/platform/plugins/private/presentation_panel/public/panel_actions/edit_panel_action/constants.ts
new file mode 100644
index 0000000000000..991262a111169
--- /dev/null
+++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/edit_panel_action/constants.ts
@@ -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 ACTION_EDIT_PANEL = 'editPanel';
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/edit_panel_action/edit_panel_action.ts b/src/platform/plugins/private/presentation_panel/public/panel_actions/edit_panel_action/edit_panel_action.ts
index 6b42f8162c2ce..9e1efc2ec928b 100644
--- a/src/platform/plugins/private/presentation_panel/public/panel_actions/edit_panel_action/edit_panel_action.ts
+++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/edit_panel_action/edit_panel_action.ts
@@ -23,8 +23,7 @@ import {
FrequentCompatibilityChangeAction,
IncompatibleActionError,
} from '@kbn/ui-actions-plugin/public';
-
-export const ACTION_EDIT_PANEL = 'editPanel';
+import { ACTION_EDIT_PANEL } from './constants';
export type EditPanelActionApi = CanAccessViewMode & HasEditCapabilities;
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/edit_panel_action/execute_edit_action.ts b/src/platform/plugins/private/presentation_panel/public/panel_actions/edit_panel_action/execute_edit_action.ts
new file mode 100644
index 0000000000000..7cd4818f9f091
--- /dev/null
+++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/edit_panel_action/execute_edit_action.ts
@@ -0,0 +1,27 @@
+/*
+ * 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 { ActionExecutionMeta } from '@kbn/ui-actions-plugin/public';
+import type { EmbeddableApiContext } from '@kbn/presentation-publishing';
+import { CONTEXT_MENU_TRIGGER } from '../triggers';
+import { ACTION_EDIT_PANEL } from './constants';
+import { uiActions } from '../../kibana_services';
+
+export async function executeEditPanelAction(api: unknown) {
+ try {
+ const action = await uiActions.getAction(ACTION_EDIT_PANEL);
+ action.execute({
+ embeddable: api,
+ trigger: { id: CONTEXT_MENU_TRIGGER },
+ } as EmbeddableApiContext & ActionExecutionMeta);
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.warn('Unable to execute edit action, Error: ', error.message);
+ }
+}
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/index.ts b/src/platform/plugins/private/presentation_panel/public/panel_actions/index.ts
index 54a0d8e9d5a9e..43a3ac116cf93 100644
--- a/src/platform/plugins/private/presentation_panel/public/panel_actions/index.ts
+++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/index.ts
@@ -7,15 +7,6 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
-export {
- ACTION_CUSTOMIZE_PANEL,
- CustomizePanelAction,
- CustomTimeRangeBadge,
-} from './customize_panel_action';
-export { EditPanelAction } from './edit_panel_action/edit_panel_action';
-export { InspectPanelAction } from './inspect_panel_action/inspect_panel_action';
-export { getEditPanelAction } from './panel_actions';
-export { RemovePanelAction } from './remove_panel_action/remove_panel_action';
export {
contextMenuTrigger,
CONTEXT_MENU_TRIGGER,
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/inspect_panel_action/constants.ts b/src/platform/plugins/private/presentation_panel/public/panel_actions/inspect_panel_action/constants.ts
new file mode 100644
index 0000000000000..4ff56cf345200
--- /dev/null
+++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/inspect_panel_action/constants.ts
@@ -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 ACTION_INSPECT_PANEL = 'openInspector';
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/inspect_panel_action/inspect_panel_action.ts b/src/platform/plugins/private/presentation_panel/public/panel_actions/inspect_panel_action/inspect_panel_action.ts
index 9c70b049d84bd..2b0417c6a10b2 100644
--- a/src/platform/plugins/private/presentation_panel/public/panel_actions/inspect_panel_action/inspect_panel_action.ts
+++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/inspect_panel_action/inspect_panel_action.ts
@@ -17,10 +17,9 @@ import {
HasParentApi,
} from '@kbn/presentation-publishing';
import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public';
+import { ACTION_INSPECT_PANEL } from './constants';
import { inspector } from '../../kibana_services';
-export const ACTION_INSPECT_PANEL = 'openInspector';
-
export type InspectPanelActionApi = HasInspectorAdapters &
Partial;
const isApiCompatible = (api: unknown | null): api is InspectPanelActionApi => {
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/panel_actions.ts b/src/platform/plugins/private/presentation_panel/public/panel_actions/panel_actions.ts
deleted file mode 100644
index 9390bd3940518..0000000000000
--- a/src/platform/plugins/private/presentation_panel/public/panel_actions/panel_actions.ts
+++ /dev/null
@@ -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 { uiActions } from '../kibana_services';
-import { CustomizePanelAction, CustomTimeRangeBadge } from './customize_panel_action';
-import { EditPanelAction } from './edit_panel_action/edit_panel_action';
-import { InspectPanelAction } from './inspect_panel_action/inspect_panel_action';
-import { RemovePanelAction } from './remove_panel_action/remove_panel_action';
-import { CONTEXT_MENU_TRIGGER, PANEL_BADGE_TRIGGER } from './triggers';
-
-// export these actions to make them accessible in this plugin.
-export let customizePanelAction: CustomizePanelAction;
-export let editPanelAction: EditPanelAction;
-
-export const getEditPanelAction = () => editPanelAction;
-
-export const registerActions = () => {
- editPanelAction = new EditPanelAction();
- customizePanelAction = new CustomizePanelAction();
-
- const removePanel = new RemovePanelAction();
- const inspectPanel = new InspectPanelAction();
- const timeRangeBadge = new CustomTimeRangeBadge();
-
- uiActions.registerAction(removePanel);
- uiActions.attachAction(CONTEXT_MENU_TRIGGER, removePanel.id);
-
- uiActions.registerAction(timeRangeBadge);
- uiActions.attachAction(PANEL_BADGE_TRIGGER, timeRangeBadge.id);
-
- uiActions.registerAction(inspectPanel);
- uiActions.attachAction(CONTEXT_MENU_TRIGGER, inspectPanel.id);
-
- uiActions.registerAction(editPanelAction);
- uiActions.attachAction(CONTEXT_MENU_TRIGGER, editPanelAction.id);
-
- uiActions.registerAction(customizePanelAction);
- uiActions.attachAction(CONTEXT_MENU_TRIGGER, customizePanelAction.id);
-};
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/register_actions.ts b/src/platform/plugins/private/presentation_panel/public/panel_actions/register_actions.ts
new file mode 100644
index 0000000000000..134e74d4ea1d3
--- /dev/null
+++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/register_actions.ts
@@ -0,0 +1,50 @@
+/*
+ * 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 { uiActions } from '../kibana_services';
+import { ACTION_EDIT_PANEL } from './edit_panel_action/constants';
+import { ACTION_INSPECT_PANEL } from './inspect_panel_action/constants';
+import { ACTION_REMOVE_PANEL } from './remove_panel_action/constants';
+import {
+ ACTION_CUSTOMIZE_PANEL,
+ CUSTOM_TIME_RANGE_BADGE,
+} from './customize_panel_action/constants';
+import { CONTEXT_MENU_TRIGGER, PANEL_BADGE_TRIGGER } from './triggers';
+
+export const registerActions = () => {
+ uiActions.registerActionAsync(ACTION_REMOVE_PANEL, async () => {
+ const { RemovePanelAction } = await import('../panel_component/panel_module');
+ return new RemovePanelAction();
+ });
+ uiActions.attachAction(CONTEXT_MENU_TRIGGER, ACTION_REMOVE_PANEL);
+
+ uiActions.registerActionAsync(CUSTOM_TIME_RANGE_BADGE, async () => {
+ const { CustomTimeRangeBadge } = await import('../panel_component/panel_module');
+ return new CustomTimeRangeBadge();
+ });
+ uiActions.attachAction(PANEL_BADGE_TRIGGER, CUSTOM_TIME_RANGE_BADGE);
+
+ uiActions.registerActionAsync(ACTION_INSPECT_PANEL, async () => {
+ const { InspectPanelAction } = await import('../panel_component/panel_module');
+ return new InspectPanelAction();
+ });
+ uiActions.attachAction(CONTEXT_MENU_TRIGGER, ACTION_INSPECT_PANEL);
+
+ uiActions.registerActionAsync(ACTION_EDIT_PANEL, async () => {
+ const { EditPanelAction } = await import('../panel_component/panel_module');
+ return new EditPanelAction();
+ });
+ uiActions.attachAction(CONTEXT_MENU_TRIGGER, ACTION_EDIT_PANEL);
+
+ uiActions.registerActionAsync(ACTION_CUSTOMIZE_PANEL, async () => {
+ const { CustomizePanelAction } = await import('../panel_component/panel_module');
+ return new CustomizePanelAction();
+ });
+ uiActions.attachAction(CONTEXT_MENU_TRIGGER, ACTION_CUSTOMIZE_PANEL);
+};
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/remove_panel_action/constants.ts b/src/platform/plugins/private/presentation_panel/public/panel_actions/remove_panel_action/constants.ts
new file mode 100644
index 0000000000000..18704cd1d1e49
--- /dev/null
+++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/remove_panel_action/constants.ts
@@ -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 ACTION_REMOVE_PANEL = 'deletePanel';
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/remove_panel_action/remove_panel_action.ts b/src/platform/plugins/private/presentation_panel/public/panel_actions/remove_panel_action/remove_panel_action.ts
index 335fda267a800..a4f5338989915 100644
--- a/src/platform/plugins/private/presentation_panel/public/panel_actions/remove_panel_action/remove_panel_action.ts
+++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/remove_panel_action/remove_panel_action.ts
@@ -18,10 +18,8 @@ import {
PublishesViewMode,
} from '@kbn/presentation-publishing';
import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public';
-
import { getContainerParentFromAPI, PresentationContainer } from '@kbn/presentation-containers';
-
-export const ACTION_REMOVE_PANEL = 'deletePanel';
+import { ACTION_REMOVE_PANEL } from './constants';
export type RemovePanelActionApi = PublishesViewMode &
HasUniqueId &
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/types.ts b/src/platform/plugins/private/presentation_panel/public/panel_actions/types.ts
index 16dd069327cc8..315c4a2c08cd8 100644
--- a/src/platform/plugins/private/presentation_panel/public/panel_actions/types.ts
+++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/types.ts
@@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
-import { EmbeddableApiContext } from '@kbn/presentation-publishing';
+import type { EmbeddableApiContext } from '@kbn/presentation-publishing';
import { Action } from '@kbn/ui-actions-plugin/public';
export type AnyApiAction = Action;
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_component/panel_header/presentation_panel_hover_actions.tsx b/src/platform/plugins/private/presentation_panel/public/panel_component/panel_header/presentation_panel_hover_actions.tsx
index 86798006a250a..59ac3b46ac954 100644
--- a/src/platform/plugins/private/presentation_panel/public/panel_component/panel_header/presentation_panel_hover_actions.tsx
+++ b/src/platform/plugins/private/presentation_panel/public/panel_component/panel_header/presentation_panel_hover_actions.tsx
@@ -243,10 +243,11 @@ export const PresentationPanelHoverActions = ({
(async () => {
// subscribe to any frequently changing context menu actions
- const frequentlyChangingActions = uiActions.getFrequentlyChangingActionsForTrigger(
+ const frequentlyChangingActions = await uiActions.getFrequentlyChangingActionsForTrigger(
CONTEXT_MENU_TRIGGER,
apiContext
);
+ if (canceled) return;
for (const frequentlyChangingAction of frequentlyChangingActions) {
if ((quickActionIds as readonly string[]).includes(frequentlyChangingAction.id)) {
@@ -265,10 +266,12 @@ export const PresentationPanelHoverActions = ({
}
// subscribe to any frequently changing notification actions
- const frequentlyChangingNotifications = uiActions.getFrequentlyChangingActionsForTrigger(
- PANEL_NOTIFICATION_TRIGGER,
- apiContext
- );
+ const frequentlyChangingNotifications =
+ await uiActions.getFrequentlyChangingActionsForTrigger(
+ PANEL_NOTIFICATION_TRIGGER,
+ apiContext
+ );
+ if (canceled) return;
for (const frequentlyChangingNotification of frequentlyChangingNotifications) {
if (
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_component/panel_header/use_presentation_panel_header_actions.tsx b/src/platform/plugins/private/presentation_panel/public/panel_component/panel_header/use_presentation_panel_header_actions.tsx
index 062cc4d1b58a4..b0aead09c8286 100644
--- a/src/platform/plugins/private/presentation_panel/public/panel_component/panel_header/use_presentation_panel_header_actions.tsx
+++ b/src/platform/plugins/private/presentation_panel/public/panel_component/panel_header/use_presentation_panel_header_actions.tsx
@@ -80,10 +80,11 @@ export const usePresentationPanelHeaderActions = <
const apiContext = { embeddable: api };
// subscribe to any frequently changing badge actions
- const frequentlyChangingBadges = uiActions.getFrequentlyChangingActionsForTrigger(
+ const frequentlyChangingBadges = await uiActions.getFrequentlyChangingActionsForTrigger(
PANEL_BADGE_TRIGGER,
apiContext
);
+ if (canceled) return;
for (const badge of frequentlyChangingBadges) {
subscriptions.add(
badge.subscribeToCompatibilityChanges(apiContext, (isCompatible, action) =>
@@ -93,10 +94,12 @@ export const usePresentationPanelHeaderActions = <
}
// subscribe to any frequently changing notification actions
- const frequentlyChangingNotifications = uiActions.getFrequentlyChangingActionsForTrigger(
- PANEL_NOTIFICATION_TRIGGER,
- apiContext
- );
+ const frequentlyChangingNotifications =
+ await uiActions.getFrequentlyChangingActionsForTrigger(
+ PANEL_NOTIFICATION_TRIGGER,
+ apiContext
+ );
+ if (canceled) return;
for (const notification of frequentlyChangingNotifications) {
if (!disabledNotifications.includes(notification.id))
subscriptions.add(
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_component/panel_module.ts b/src/platform/plugins/private/presentation_panel/public/panel_component/panel_module.ts
new file mode 100644
index 0000000000000..ea60ccf846b8d
--- /dev/null
+++ b/src/platform/plugins/private/presentation_panel/public/panel_component/panel_module.ts
@@ -0,0 +1,16 @@
+/*
+ * 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 { PresentationPanelInternal } from './presentation_panel_internal';
+export { PresentationPanelErrorInternal } from './presentation_panel_error_internal';
+export { RemovePanelAction } from '../panel_actions/remove_panel_action/remove_panel_action';
+export { CustomTimeRangeBadge } from '../panel_actions/customize_panel_action';
+export { CustomizePanelAction } from '../panel_actions/customize_panel_action';
+export { EditPanelAction } from '../panel_actions/edit_panel_action/edit_panel_action';
+export { InspectPanelAction } from '../panel_actions/inspect_panel_action/inspect_panel_action';
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel.tsx b/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel.tsx
index 5de70f3a9b4cf..4d2c60f951eed 100644
--- a/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel.tsx
+++ b/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel.tsx
@@ -16,8 +16,7 @@ import React from 'react';
import useAsync from 'react-use/lib/useAsync';
import { css } from '@emotion/react';
import { untilPluginStartServicesReady } from '../kibana_services';
-import { PresentationPanelError } from './presentation_panel_error';
-import { DefaultPresentationPanelApi, PresentationPanelProps } from './types';
+import type { DefaultPresentationPanelApi, PresentationPanelProps } from './types';
import { getErrorLoadingPanel } from './presentation_panel_strings';
export const PresentationPanel = <
@@ -30,7 +29,7 @@ export const PresentationPanel = <
) => {
const { Component, hidePanelChrome, ...passThroughProps } = props;
const { euiTheme } = useEuiTheme();
- const { loading, value, error } = useAsync(async () => {
+ const { loading, value } = useAsync(async () => {
if (hidePanelChrome) {
return {
unwrappedComponent: isPromise(Component) ? await Component : Component,
@@ -38,15 +37,31 @@ export const PresentationPanel = <
}
const startServicesPromise = untilPluginStartServicesReady();
- const modulePromise = await import('./presentation_panel_internal');
const componentPromise = isPromise(Component) ? Component : Promise.resolve(Component);
- const [, unwrappedComponent, panelModule] = await Promise.all([
+ const results = await Promise.allSettled([
startServicesPromise,
componentPromise,
- modulePromise,
+ import('./panel_module'),
]);
- const Panel = panelModule.PresentationPanelInternal;
- return { Panel, unwrappedComponent };
+
+ let loadErrorReason: string | undefined;
+ for (const result of results) {
+ if (result.status === 'rejected') {
+ loadErrorReason = result.reason;
+ break;
+ }
+ }
+
+ return {
+ loadErrorReason,
+ Panel:
+ results[2].status === 'fulfilled' ? results[2].value?.PresentationPanelInternal : undefined,
+ PanelError:
+ results[2].status === 'fulfilled'
+ ? results[2].value?.PresentationPanelErrorInternal
+ : undefined,
+ unwrappedComponent: results[1].status === 'fulfilled' ? results[1].value : undefined,
+ };
// Ancestry chain is expected to use 'key' attribute to reset DOM and state
// when unwrappedComponent needs to be re-loaded
@@ -66,9 +81,10 @@ export const PresentationPanel = <
);
const Panel = value?.Panel;
+ const PanelError = value?.PanelError;
const UnwrappedComponent = value?.unwrappedComponent;
const shouldHavePanel = !hidePanelChrome;
- if (error || (shouldHavePanel && !Panel) || !UnwrappedComponent) {
+ if (value?.loadErrorReason || (shouldHavePanel && !Panel) || !UnwrappedComponent) {
return (
-
+ {PanelError ? (
+
+ ) : (
+ value?.loadErrorReason ?? getErrorLoadingPanel()
+ )}
);
}
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_error.tsx b/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_error.tsx
index 4973e2599ddad..ee2a16b5a8052 100644
--- a/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_error.tsx
+++ b/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_error.tsx
@@ -7,100 +7,21 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
-import { EuiButtonEmpty, EuiEmptyPrompt, EuiText } from '@elastic/eui';
-import React, { useEffect, useMemo, useState } from 'react';
-
-import { ErrorLike } from '@kbn/expressions-plugin/common';
-import { useStateFromPublishingSubject } from '@kbn/presentation-publishing';
-import { renderSearchError } from '@kbn/search-errors';
-import { Markdown } from '@kbn/shared-ux-markdown';
-import { Subscription } from 'rxjs';
-import { i18n } from '@kbn/i18n';
-import { useErrorTextStyle } from '@kbn/react-hooks';
-import { editPanelAction } from '../panel_actions/panel_actions';
-import { getErrorCallToAction } from './presentation_panel_strings';
-import { DefaultPresentationPanelApi } from './types';
-
-export const PresentationPanelError = ({
- api,
- error,
-}: {
- error: ErrorLike;
- api?: DefaultPresentationPanelApi;
-}) => {
- const errorTextStyle = useErrorTextStyle();
-
- const [isEditable, setIsEditable] = useState(false);
- const handleErrorClick = useMemo(
- () => (isEditable ? () => editPanelAction?.execute({ embeddable: api }) : undefined),
- [api, isEditable]
- );
- const label = useMemo(
- () => (isEditable ? editPanelAction?.getDisplayName({ embeddable: api }) : ''),
- [api, isEditable]
- );
-
- const panelTitle = useStateFromPublishingSubject(api?.panelTitle);
- const ariaLabel = useMemo(
- () => (panelTitle ? getErrorCallToAction(panelTitle) : label),
- [label, panelTitle]
- );
-
- // Get initial editable state from action and subscribe to changes.
- useEffect(() => {
- if (!editPanelAction?.couldBecomeCompatible({ embeddable: api })) return;
-
- let canceled = false;
- const subscription = new Subscription();
- (async () => {
- const initiallyCompatible = await editPanelAction?.isCompatible({ embeddable: api });
- if (canceled) return;
- setIsEditable(initiallyCompatible);
-
- subscription.add(
- editPanelAction?.subscribeToCompatibilityChanges({ embeddable: api }, (isCompatible) => {
- if (!canceled) setIsEditable(isCompatible);
- })
- );
- })();
-
- return () => {
- canceled = true;
- subscription.unsubscribe();
+import React from 'react';
+import { dynamic } from '@kbn/shared-ux-utility';
+import { PanelLoader } from '@kbn/panel-loader';
+import type { PresentationPanelErrorProps } from './presentation_panel_error_internal';
+
+const Component = dynamic(
+ async () => {
+ const { PresentationPanelErrorInternal } = await import('./panel_module');
+ return {
+ default: PresentationPanelErrorInternal,
};
- }, [api]);
-
- const searchErrorDisplay = renderSearchError(error);
-
- const actions = searchErrorDisplay?.actions ?? [];
- if (isEditable) {
- actions.push(
-
- {label}
-
- );
- }
+ },
+ { fallback: }
+);
- return (
-
-
- {error.message?.length
- ? error.message
- : i18n.translate('presentationPanel.emptyErrorMessage', {
- defaultMessage: 'Error',
- })}
-
-
- )
- }
- data-test-subj="embeddableStackError"
- iconType="warning"
- iconColor="danger"
- layout="vertical"
- actions={actions}
- />
- );
-};
+export function PresentationPanelError(props: PresentationPanelErrorProps) {
+ return ;
+}
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_error.test.tsx b/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_error_internal.test.tsx
similarity index 73%
rename from src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_error.test.tsx
rename to src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_error_internal.test.tsx
index 010b0ec579303..336b5af25fd8a 100644
--- a/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_error.test.tsx
+++ b/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_error_internal.test.tsx
@@ -9,16 +9,16 @@
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
-import { PresentationPanelError } from './presentation_panel_error';
+import { PresentationPanelErrorInternal } from './presentation_panel_error_internal';
-describe('PresentationPanelError', () => {
+describe('PresentationPanelErrorInternal', () => {
test('should display error', async () => {
- render();
+ render();
await waitFor(() => screen.getByTestId('errorMessageMarkdown'));
});
test('should display error with empty message', async () => {
- render();
+ render();
await waitFor(() => screen.getByTestId('errorMessageMarkdown'));
});
});
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_error_internal.tsx b/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_error_internal.tsx
new file mode 100644
index 0000000000000..7f485621820c2
--- /dev/null
+++ b/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_error_internal.tsx
@@ -0,0 +1,133 @@
+/*
+ * 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 { EuiButtonEmpty, EuiEmptyPrompt, EuiText } from '@elastic/eui';
+import React, { useEffect, useMemo, useState } from 'react';
+
+import { ErrorLike } from '@kbn/expressions-plugin/common';
+import { EmbeddableApiContext, useStateFromPublishingSubject } from '@kbn/presentation-publishing';
+import { renderSearchError } from '@kbn/search-errors';
+import { Markdown } from '@kbn/shared-ux-markdown';
+import { Subscription } from 'rxjs';
+import { i18n } from '@kbn/i18n';
+import { useErrorTextStyle } from '@kbn/react-hooks';
+import { ActionExecutionMeta } from '@kbn/ui-actions-plugin/public';
+import { getErrorCallToAction } from './presentation_panel_strings';
+import { DefaultPresentationPanelApi } from './types';
+import { uiActions } from '../kibana_services';
+import { executeEditPanelAction } from '../panel_actions/edit_panel_action/execute_edit_action';
+import { ACTION_EDIT_PANEL } from '../panel_actions/edit_panel_action/constants';
+import { CONTEXT_MENU_TRIGGER } from '../panel_actions';
+
+export interface PresentationPanelErrorProps {
+ error: ErrorLike;
+ api?: DefaultPresentationPanelApi;
+}
+
+export const PresentationPanelErrorInternal = ({ api, error }: PresentationPanelErrorProps) => {
+ const errorTextStyle = useErrorTextStyle();
+
+ const [isEditable, setIsEditable] = useState(false);
+ const handleErrorClick = useMemo(
+ () => (isEditable ? () => executeEditPanelAction(api) : undefined),
+ [api, isEditable]
+ );
+
+ const [label, setLabel] = useState('');
+ useEffect(() => {
+ if (!isEditable) {
+ setLabel('');
+ return;
+ }
+
+ const canceled = false;
+ uiActions
+ .getAction(ACTION_EDIT_PANEL)
+ .then((action) => {
+ if (canceled) return;
+ setLabel(
+ action?.getDisplayName({
+ embeddable: api,
+ trigger: { id: CONTEXT_MENU_TRIGGER },
+ } as EmbeddableApiContext & ActionExecutionMeta)
+ );
+ })
+ .catch(() => {
+ // ignore action not found
+ });
+ }, [api, isEditable]);
+
+ const panelTitle = useStateFromPublishingSubject(api?.panelTitle);
+ const ariaLabel = useMemo(
+ () => (panelTitle ? getErrorCallToAction(panelTitle) : label),
+ [label, panelTitle]
+ );
+
+ // Get initial editable state from action and subscribe to changes.
+ useEffect(() => {
+ let canceled = false;
+ const subscription = new Subscription();
+ (async () => {
+ const editPanelAction = await uiActions.getAction(ACTION_EDIT_PANEL);
+ if (canceled || !editPanelAction?.couldBecomeCompatible?.({ embeddable: api })) return;
+
+ const initiallyCompatible = await editPanelAction?.isCompatible({
+ embeddable: api,
+ trigger: { id: CONTEXT_MENU_TRIGGER },
+ } as EmbeddableApiContext & ActionExecutionMeta);
+ if (canceled) return;
+ setIsEditable(initiallyCompatible);
+
+ subscription.add(
+ editPanelAction?.subscribeToCompatibilityChanges?.({ embeddable: api }, (isCompatible) => {
+ if (!canceled) setIsEditable(isCompatible);
+ })
+ );
+ })();
+
+ return () => {
+ canceled = true;
+ subscription.unsubscribe();
+ };
+ }, [api]);
+
+ const searchErrorDisplay = renderSearchError(error);
+
+ const actions = searchErrorDisplay?.actions ?? [];
+ if (isEditable) {
+ actions.push(
+
+ {label}
+
+ );
+ }
+
+ return (
+
+
+ {error.message?.length
+ ? error.message
+ : i18n.translate('presentationPanel.emptyErrorMessage', {
+ defaultMessage: 'Error',
+ })}
+
+
+ )
+ }
+ data-test-subj="embeddableStackError"
+ iconType="warning"
+ iconColor="danger"
+ layout="vertical"
+ actions={actions}
+ />
+ );
+};
diff --git a/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_internal.tsx b/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_internal.tsx
index c2a2e82406dde..5ba645e245803 100644
--- a/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_internal.tsx
+++ b/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_internal.tsx
@@ -18,7 +18,7 @@ import classNames from 'classnames';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { PresentationPanelHeader } from './panel_header/presentation_panel_header';
import { PresentationPanelHoverActions } from './panel_header/presentation_panel_hover_actions';
-import { PresentationPanelError } from './presentation_panel_error';
+import { PresentationPanelErrorInternal } from './presentation_panel_error_internal';
import { DefaultPresentationPanelApi, PresentationPanelInternalProps } from './types';
export const PresentationPanelInternal = <
@@ -147,7 +147,7 @@ export const PresentationPanelInternal = <
data-test-subj="embeddableError"
justifyContent="center"
>
-
+
)}
{!initialLoadComplete && }
diff --git a/src/platform/plugins/private/presentation_panel/public/plugin.ts b/src/platform/plugins/private/presentation_panel/public/plugin.ts
index 797da79d0907b..c683795ba6513 100644
--- a/src/platform/plugins/private/presentation_panel/public/plugin.ts
+++ b/src/platform/plugins/private/presentation_panel/public/plugin.ts
@@ -15,7 +15,7 @@ import { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss
import { UiActionsStart } from '@kbn/ui-actions-plugin/public';
import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import { setKibanaServices } from './kibana_services';
-import { registerActions } from './panel_actions/panel_actions';
+import { registerActions } from './panel_actions/register_actions';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface PresentationPanelSetup {}
diff --git a/src/platform/plugins/private/presentation_panel/tsconfig.json b/src/platform/plugins/private/presentation_panel/tsconfig.json
index f607e5bbb2169..c7a770c9522c1 100644
--- a/src/platform/plugins/private/presentation_panel/tsconfig.json
+++ b/src/platform/plugins/private/presentation_panel/tsconfig.json
@@ -28,7 +28,8 @@
"@kbn/panel-loader",
"@kbn/search-errors",
"@kbn/shared-ux-markdown",
- "@kbn/react-hooks"
+ "@kbn/react-hooks",
+ "@kbn/shared-ux-utility"
],
"exclude": ["target/**/*"]
}
diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_actions/filters_notification_popover.test.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_actions/filters_notification_popover.test.tsx
index d42c7d939a36b..4f0d8813dc2f3 100644
--- a/src/platform/plugins/shared/dashboard/public/dashboard_actions/filters_notification_popover.test.tsx
+++ b/src/platform/plugins/shared/dashboard/public/dashboard_actions/filters_notification_popover.test.tsx
@@ -10,7 +10,7 @@
import { AggregateQuery, Filter, FilterStateStore, Query } from '@kbn/es-query';
import { I18nProvider } from '@kbn/i18n-react';
import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl';
-import { render, screen } from '@testing-library/react';
+import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { BehaviorSubject } from 'rxjs';
@@ -44,8 +44,10 @@ const mockedEditPanelAction = {
execute: jest.fn(),
isCompatible: jest.fn().mockResolvedValue(true),
};
-jest.mock('@kbn/presentation-panel-plugin/public', () => ({
- getEditPanelAction: () => mockedEditPanelAction,
+jest.mock('../services/kibana_services', () => ({
+ uiActionsService: {
+ getAction: async () => mockedEditPanelAction,
+ },
}));
describe('filters notification popover', () => {
@@ -133,6 +135,8 @@ describe('filters notification popover', () => {
await renderAndOpenPopover();
const editButton = await screen.findByTestId('filtersNotificationModal__editButton');
await userEvent.click(editButton);
- expect(mockedEditPanelAction.execute).toHaveBeenCalled();
+ await waitFor(() => {
+ expect(mockedEditPanelAction.execute).toHaveBeenCalled();
+ });
});
});
diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_actions/filters_notification_popover.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_actions/filters_notification_popover.tsx
index b3eb88acb8d3d..b300d838c355d 100644
--- a/src/platform/plugins/shared/dashboard/public/dashboard_actions/filters_notification_popover.tsx
+++ b/src/platform/plugins/shared/dashboard/public/dashboard_actions/filters_notification_popover.tsx
@@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
-import React, { useMemo, useState } from 'react';
+import React, { useCallback, useMemo, useState } from 'react';
import {
EuiButton,
@@ -23,13 +23,17 @@ import {
import { css } from '@emotion/react';
import { AggregateQuery, getAggregateQueryMode, isOfQueryType } from '@kbn/es-query';
-import { getEditPanelAction } from '@kbn/presentation-panel-plugin/public';
+import { ACTION_EDIT_PANEL } from '@kbn/presentation-panel-plugin/public';
import { FilterItems } from '@kbn/unified-search-plugin/public';
import {
+ EmbeddableApiContext,
apiCanLockHoverActions,
getViewModeSubject,
useBatchedOptionalPublishingSubjects,
} from '@kbn/presentation-publishing';
+import { ActionExecutionMeta } from '@kbn/ui-actions-plugin/public';
+import { CONTEXT_MENU_TRIGGER } from '@kbn/embeddable-plugin/public';
+import { uiActionsService } from '../services/kibana_services';
import { dashboardFilterNotificationActionStrings } from './_dashboard_actions_strings';
import { FiltersNotificationActionApi } from './filters_notification_action';
@@ -37,12 +41,23 @@ export function FiltersNotificationPopover({ api }: { api: FiltersNotificationAc
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const [disableEditbutton, setDisableEditButton] = useState(false);
- const editPanelAction = getEditPanelAction();
-
const filters = useMemo(() => api.filters$?.value, [api]);
const displayName = dashboardFilterNotificationActionStrings.getDisplayName();
const canEditUnifiedSearch = api.canEditUnifiedSearch?.() ?? true;
+ const executeEditAction = useCallback(async () => {
+ try {
+ const action = await uiActionsService.getAction(ACTION_EDIT_PANEL);
+ action.execute({
+ embeddable: api,
+ trigger: { id: CONTEXT_MENU_TRIGGER },
+ } as EmbeddableApiContext & ActionExecutionMeta);
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.warn('Unable to execute edit action, Error: ', error.message);
+ }
+ }, [api]);
+
const { queryString, queryLanguage } = useMemo(() => {
const query = api.query$?.value;
if (!query) return {};
@@ -141,7 +156,7 @@ export function FiltersNotificationPopover({ api }: { api: FiltersNotificationAc
data-test-subj={'filtersNotificationModal__editButton'}
size="s"
fill
- onClick={() => editPanelAction.execute({ embeddable: api })}
+ onClick={executeEditAction}
>
{dashboardFilterNotificationActionStrings.getEditButtonTitle()}
diff --git a/src/platform/plugins/shared/discover/public/application/main/state_management/discover_state.ts b/src/platform/plugins/shared/discover/public/application/main/state_management/discover_state.ts
index 56d1ca44c3853..eb1b2b5a4dd57 100644
--- a/src/platform/plugins/shared/discover/public/application/main/state_management/discover_state.ts
+++ b/src/platform/plugins/shared/discover/public/application/main/state_management/discover_state.ts
@@ -335,7 +335,7 @@ export function getDiscoverStateContainer({
services.dataViews.clearInstanceCache(prevDataView.id);
- updateFiltersReferences({
+ await updateFiltersReferences({
prevDataView,
nextDataView,
services,
diff --git a/src/platform/plugins/shared/discover/public/application/main/state_management/utils/update_filter_references.ts b/src/platform/plugins/shared/discover/public/application/main/state_management/utils/update_filter_references.ts
index 557844624fa64..76ebca4461e6f 100644
--- a/src/platform/plugins/shared/discover/public/application/main/state_management/utils/update_filter_references.ts
+++ b/src/platform/plugins/shared/discover/public/application/main/state_management/utils/update_filter_references.ts
@@ -15,7 +15,7 @@ import { ActionExecutionContext } from '@kbn/ui-actions-plugin/public';
import type { DataView } from '@kbn/data-views-plugin/public';
import { DiscoverServices } from '../../../../build_services';
-export const updateFiltersReferences = ({
+export const updateFiltersReferences = async ({
prevDataView,
nextDataView,
services: { uiActions },
@@ -25,7 +25,7 @@ export const updateFiltersReferences = ({
services: DiscoverServices;
}) => {
const trigger = uiActions.getTrigger(UPDATE_FILTER_REFERENCES_TRIGGER);
- const action = uiActions.getAction(UPDATE_FILTER_REFERENCES_ACTION);
+ const action = await uiActions.getAction(UPDATE_FILTER_REFERENCES_ACTION);
action?.execute({
trigger,
fromDataView: prevDataView.id,
diff --git a/src/platform/plugins/shared/presentation_util/public/components/floating_actions/floating_actions.tsx b/src/platform/plugins/shared/presentation_util/public/components/floating_actions/floating_actions.tsx
index 4fcc0ef869c96..33d03058810c9 100644
--- a/src/platform/plugins/shared/presentation_util/public/components/floating_actions/floating_actions.tsx
+++ b/src/platform/plugins/shared/presentation_util/public/components/floating_actions/floating_actions.tsx
@@ -50,7 +50,7 @@ export const FloatingActions: FC = ({
useEffect(() => {
if (!api) return;
- let mounted = true;
+ let canceled = false;
const context = {
embeddable: api,
trigger: panelHoverTrigger,
@@ -74,7 +74,7 @@ export const FloatingActions: FC = ({
const subscriptions = new Subscription();
const handleActionCompatibilityChange = (isCompatible: boolean, action: Action) => {
- if (!mounted) return;
+ if (canceled) return;
setFloatingActions((currentActions) => {
const newActions: FloatingActionItem[] = currentActions
?.filter((current) => current.id !== action.id)
@@ -88,13 +88,12 @@ export const FloatingActions: FC = ({
(async () => {
const actions = await getActions();
- if (!mounted) return;
+ if (canceled) return;
setFloatingActions(actions);
- const frequentlyChangingActions = uiActionsService.getFrequentlyChangingActionsForTrigger(
- PANEL_HOVER_TRIGGER,
- context
- );
+ const frequentlyChangingActions =
+ await uiActionsService.getFrequentlyChangingActionsForTrigger(PANEL_HOVER_TRIGGER, context);
+ if (canceled) return;
for (const action of frequentlyChangingActions) {
subscriptions.add(
@@ -104,7 +103,7 @@ export const FloatingActions: FC = ({
})();
return () => {
- mounted = false;
+ canceled = true;
subscriptions.unsubscribe();
};
}, [api, viewMode, disabledActions]);
diff --git a/src/platform/plugins/shared/ui_actions/public/mocks.ts b/src/platform/plugins/shared/ui_actions/public/mocks.ts
index 411247ff5c695..08bfbb4a76325 100644
--- a/src/platform/plugins/shared/ui_actions/public/mocks.ts
+++ b/src/platform/plugins/shared/ui_actions/public/mocks.ts
@@ -33,6 +33,7 @@ const createStartContract = (): Start => {
attachAction: jest.fn(),
unregisterAction: jest.fn(),
addTriggerAction: jest.fn(),
+ addTriggerActionAsync: jest.fn(),
clear: jest.fn(),
detachAction: jest.fn(),
executeTriggerActions: jest.fn(),
@@ -41,14 +42,16 @@ const createStartContract = (): Start => {
hasAction: jest.fn(),
getTrigger: jest.fn(),
hasTrigger: jest.fn(),
- getTriggerActions: jest.fn((id: string) => []),
+ getTriggerActions: jest.fn(async (id: string) => []),
getTriggerCompatibleActions: jest.fn((triggerId: string, context: object) =>
Promise.resolve([] as Array>)
),
getFrequentlyChangingActionsForTrigger: jest.fn(
- (triggerId: string, context: object) => [] as Array>
+ async (triggerId: string, context: object) =>
+ [] as Array>
),
registerAction: jest.fn(),
+ registerActionAsync: jest.fn(),
registerTrigger: jest.fn(),
};
diff --git a/src/platform/plugins/shared/ui_actions/public/service/ui_actions_service.test.ts b/src/platform/plugins/shared/ui_actions/public/service/ui_actions_service.test.ts
index 0fa8301a7321f..215cc8752041b 100644
--- a/src/platform/plugins/shared/ui_actions/public/service/ui_actions_service.test.ts
+++ b/src/platform/plugins/shared/ui_actions/public/service/ui_actions_service.test.ts
@@ -128,7 +128,7 @@ describe('UiActionsService', () => {
isCompatible: async () => true,
};
- test('returns actions set on trigger', () => {
+ test('returns actions set on trigger', async () => {
const service = new UiActionsService();
service.registerAction(action1);
@@ -139,19 +139,19 @@ describe('UiActionsService', () => {
title: 'baz',
});
- const list0 = service.getTriggerActions(FOO_TRIGGER);
+ const list0 = await service.getTriggerActions(FOO_TRIGGER);
expect(list0).toHaveLength(0);
service.addTriggerAction(FOO_TRIGGER, action1);
- const list1 = service.getTriggerActions(FOO_TRIGGER);
+ const list1 = await service.getTriggerActions(FOO_TRIGGER);
expect(list1).toHaveLength(1);
expect(list1[0]).toBeInstanceOf(ActionInternal);
expect(list1[0].id).toBe(action1.id);
service.addTriggerAction(FOO_TRIGGER, action2);
- const list2 = service.getTriggerActions(FOO_TRIGGER);
+ const list2 = await service.getTriggerActions(FOO_TRIGGER);
expect(list2).toHaveLength(2);
expect(!!list2.find(({ id }: { id: string }) => id === 'action1')).toBe(true);
@@ -171,7 +171,8 @@ describe('UiActionsService', () => {
service.registerAction(helloWorldAction);
expect(actions.size - length).toBe(1);
- expect(actions.get(helloWorldAction.id)!.id).toBe(helloWorldAction.id);
+ const action = await actions.get(helloWorldAction.id)?.();
+ expect(action?.id).toBe(helloWorldAction.id);
});
test('getTriggerCompatibleActions returns attached actions', async () => {
@@ -288,7 +289,7 @@ describe('UiActionsService', () => {
expect(trigger2.id).toBe(FOO_TRIGGER);
});
- test('forked service preserves trigger-to-actions mapping', () => {
+ test('forked service preserves trigger-to-actions mapping', async () => {
const service1 = new UiActionsService();
service1.registerTrigger({
@@ -299,8 +300,8 @@ describe('UiActionsService', () => {
const service2 = service1.fork();
- const actions1 = service1.getTriggerActions(FOO_TRIGGER);
- const actions2 = service2.getTriggerActions(FOO_TRIGGER);
+ const actions1 = await service1.getTriggerActions(FOO_TRIGGER);
+ const actions2 = await service2.getTriggerActions(FOO_TRIGGER);
expect(actions1).toHaveLength(1);
expect(actions2).toHaveLength(1);
@@ -308,7 +309,7 @@ describe('UiActionsService', () => {
expect(actions2[0].id).toBe(testAction1.id);
});
- test('new attachments in fork do not appear in original service', () => {
+ test('new attachments in fork do not appear in original service', async () => {
const service1 = new UiActionsService();
service1.registerTrigger({
@@ -320,16 +321,16 @@ describe('UiActionsService', () => {
const service2 = service1.fork();
- expect(service1.getTriggerActions(FOO_TRIGGER)).toHaveLength(1);
- expect(service2.getTriggerActions(FOO_TRIGGER)).toHaveLength(1);
+ expect(await service1.getTriggerActions(FOO_TRIGGER)).toHaveLength(1);
+ expect(await service2.getTriggerActions(FOO_TRIGGER)).toHaveLength(1);
service2.addTriggerAction(FOO_TRIGGER, testAction2);
- expect(service1.getTriggerActions(FOO_TRIGGER)).toHaveLength(1);
- expect(service2.getTriggerActions(FOO_TRIGGER)).toHaveLength(2);
+ expect(await service1.getTriggerActions(FOO_TRIGGER)).toHaveLength(1);
+ expect(await service2.getTriggerActions(FOO_TRIGGER)).toHaveLength(2);
});
- test('new attachments in original service do not appear in fork', () => {
+ test('new attachments in original service do not appear in fork', async () => {
const service1 = new UiActionsService();
service1.registerTrigger({
@@ -341,13 +342,13 @@ describe('UiActionsService', () => {
const service2 = service1.fork();
- expect(service1.getTriggerActions(FOO_TRIGGER)).toHaveLength(1);
- expect(service2.getTriggerActions(FOO_TRIGGER)).toHaveLength(1);
+ expect(await service1.getTriggerActions(FOO_TRIGGER)).toHaveLength(1);
+ expect(await service2.getTriggerActions(FOO_TRIGGER)).toHaveLength(1);
service1.addTriggerAction(FOO_TRIGGER, testAction2);
- expect(service1.getTriggerActions(FOO_TRIGGER)).toHaveLength(2);
- expect(service2.getTriggerActions(FOO_TRIGGER)).toHaveLength(1);
+ expect(await service1.getTriggerActions(FOO_TRIGGER)).toHaveLength(2);
+ expect(await service2.getTriggerActions(FOO_TRIGGER)).toHaveLength(1);
});
});
@@ -372,7 +373,7 @@ describe('UiActionsService', () => {
});
});
- test('can register action', () => {
+ test('can register action', async () => {
const actions: ActionRegistry = new Map();
const service = new UiActionsService({ actions });
@@ -381,13 +382,13 @@ describe('UiActionsService', () => {
order: 13,
} as unknown as ActionDefinition);
- expect(actions.get(ACTION_HELLO_WORLD)).toMatchObject({
+ expect(await actions.get(ACTION_HELLO_WORLD)?.()).toMatchObject({
id: ACTION_HELLO_WORLD,
order: 13,
});
});
- test('can attach an action to a trigger', () => {
+ test('can attach an action to a trigger', async () => {
const service = new UiActionsService();
const trigger: Trigger = {
@@ -401,13 +402,13 @@ describe('UiActionsService', () => {
service.registerTrigger(trigger);
service.addTriggerAction(MY_TRIGGER, action);
- const actions = service.getTriggerActions(trigger.id);
+ const actions = await service.getTriggerActions(trigger.id);
expect(actions.length).toBe(1);
expect(actions[0].id).toBe(ACTION_HELLO_WORLD);
});
- test('can detach an action from a trigger', () => {
+ test('can detach an action from a trigger', async () => {
const service = new UiActionsService();
const trigger: Trigger = {
@@ -423,7 +424,7 @@ describe('UiActionsService', () => {
service.addTriggerAction(trigger.id, action);
service.detachAction(trigger.id, action.id);
- const actions2 = service.getTriggerActions(trigger.id);
+ const actions2 = await service.getTriggerActions(trigger.id);
expect(actions2).toEqual([]);
});
diff --git a/src/platform/plugins/shared/ui_actions/public/service/ui_actions_service.ts b/src/platform/plugins/shared/ui_actions/public/service/ui_actions_service.ts
index 6c6e176c665f6..581be20979cb5 100644
--- a/src/platform/plugins/shared/ui_actions/public/service/ui_actions_service.ts
+++ b/src/platform/plugins/shared/ui_actions/public/service/ui_actions_service.ts
@@ -8,6 +8,7 @@
*/
import type { Trigger } from '@kbn/ui-actions-browser/src/triggers';
+import { asyncMap } from '@kbn/std';
import { TriggerRegistry, ActionRegistry, TriggerToActionsRegistry } from '../types';
import {
ActionInternal,
@@ -70,6 +71,11 @@ export class UiActionsService {
return trigger.contract;
};
+ /**
+ * @deprecated
+ *
+ * Use `plugins.uiActions.registerActionAsync` instead.
+ */
public readonly registerAction = (
definition: ActionDefinition
): Action => {
@@ -79,11 +85,25 @@ export class UiActionsService {
const action = new ActionInternal(definition);
- this.actions.set(action.id, action as unknown as ActionInternal