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); + this.actions.set(action.id, async () => action as unknown as ActionInternal); return action; }; + public readonly registerActionAsync = ( + id: string, + getDefinition: () => Promise> + ) => { + if (this.actions.has(id)) { + throw new Error(`Action [action.id = ${id}] already registered.`); + } + + this.actions.set(id, async () => { + const action = new ActionInternal(await getDefinition()); + return action as unknown as ActionInternal; + }); + }; + public readonly unregisterAction = (actionId: string): void => { if (!this.actions.has(actionId)) { throw new Error(`Action [action.id = ${actionId}] is not registered.`); @@ -130,40 +150,56 @@ export class UiActionsService { }; /** - * `addTriggerAction` is similar to `attachAction` as it attaches action to a - * trigger, but it also registers the action, if it has not been registered, yet. + * @deprecated + * + * Use `plugins.uiActions.addTriggerActionAsync` instead. */ public readonly addTriggerAction = (triggerId: string, action: ActionDefinition): void => { if (!this.actions.has(action.id)) this.registerAction(action); this.attachAction(triggerId, action.id); }; - public readonly getAction = (id: string): Action => { - if (!this.actions.has(id)) { + /** + * `addTriggerAction` is similar to `attachAction` as it attaches action to a + * trigger, but it also registers the action, if it has not been registered, yet. + */ + public readonly addTriggerActionAsync = ( + triggerId: string, + actionId: string, + getDefinition: () => Promise> + ): void => { + if (!this.actions.has(actionId)) this.registerActionAsync(actionId, getDefinition); + this.attachAction(triggerId, actionId); + }; + + public readonly getAction = async (id: string): Promise => { + const getAction = this.actions.get(id); + if (!getAction) { throw new Error(`Action [action.id = ${id}] not registered.`); } - return this.actions.get(id)! as Action; + return (await getAction()) as Action; }; - public readonly getTriggerActions = (triggerId: string): Action[] => { + public readonly getTriggerActions = async (triggerId: string): Promise => { // This line checks if trigger exists, otherwise throws. this.getTrigger!(triggerId); - const actionIds = this.triggerToActions.get(triggerId); + const actionIds = this.triggerToActions.get(triggerId) ?? []; - const actions = actionIds! - .map((actionId) => this.actions.get(actionId) as ActionInternal) - .filter(Boolean); + const actions = await asyncMap( + actionIds, + async (actionId) => (await this.actions.get(actionId)?.()) as ActionInternal + ); - return actions as Action[]; + return actions.filter(Boolean); }; public readonly getTriggerCompatibleActions = async ( triggerId: string, context: object ): Promise => { - const actions = this.getTriggerActions!(triggerId); + const actions = await this.getTriggerActions(triggerId); const isCompatibles = await Promise.all( actions.map((action) => action.isCompatible({ @@ -180,11 +216,11 @@ export class UiActionsService { }, []); }; - public readonly getFrequentlyChangingActionsForTrigger = ( + public readonly getFrequentlyChangingActionsForTrigger = async ( triggerId: string, context: object - ): FrequentCompatibilityChangeAction[] => { - return this.getTriggerActions!(triggerId).filter((action) => { + ): Promise => { + return (await this.getTriggerActions(triggerId)).filter((action) => { return ( Boolean(action.subscribeToCompatibilityChanges) && action.couldBecomeCompatible?.({ diff --git a/src/platform/plugins/shared/ui_actions/public/tests/get_trigger_actions.test.ts b/src/platform/plugins/shared/ui_actions/public/tests/get_trigger_actions.test.ts index 2b786ec2a08cb..2146b405c5df3 100644 --- a/src/platform/plugins/shared/ui_actions/public/tests/get_trigger_actions.test.ts +++ b/src/platform/plugins/shared/ui_actions/public/tests/get_trigger_actions.test.ts @@ -24,7 +24,7 @@ const action2: ActionDefinition = { execute: async () => {}, }; -test('returns actions set on trigger', () => { +test('returns actions set on trigger', async () => { const { setup, doStart } = uiActionsPluginMock.createPlugin(); setup.registerAction(action1); setup.registerAction(action2); @@ -35,19 +35,19 @@ test('returns actions set on trigger', () => { }); const start = doStart(); - const list0 = start.getTriggerActions('trigger'); + const list0 = await start.getTriggerActions('trigger'); expect(list0).toHaveLength(0); setup.addTriggerAction('trigger', action1); - const list1 = start.getTriggerActions('trigger'); + const list1 = await start.getTriggerActions('trigger'); expect(list1).toHaveLength(1); expect(list1[0]).toBeInstanceOf(ActionInternal); expect(list1[0].id).toBe(action1.id); setup.addTriggerAction('trigger', action2); - const list2 = start.getTriggerActions('trigger'); + const list2 = await start.getTriggerActions('trigger'); expect(list2).toHaveLength(2); expect(!!list2.find(({ id }: { id: string }) => id === 'action1')).toBe(true); diff --git a/src/platform/plugins/shared/ui_actions/public/types.ts b/src/platform/plugins/shared/ui_actions/public/types.ts index 9c3b0f6de0602..82570f13a286a 100644 --- a/src/platform/plugins/shared/ui_actions/public/types.ts +++ b/src/platform/plugins/shared/ui_actions/public/types.ts @@ -14,7 +14,7 @@ import { ActionInternal } from './actions/action_internal'; import { TriggerInternal } from './triggers/trigger_internal'; export type TriggerRegistry = Map>; -export type ActionRegistry = Map; +export type ActionRegistry = Map Promise>; export type TriggerToActionsRegistry = Map; export interface VisualizeFieldContext { diff --git a/src/platform/plugins/shared/ui_actions/tsconfig.json b/src/platform/plugins/shared/ui_actions/tsconfig.json index 0363fbbeb0d7b..3241dc65aaa2b 100644 --- a/src/platform/plugins/shared/ui_actions/tsconfig.json +++ b/src/platform/plugins/shared/ui_actions/tsconfig.json @@ -15,6 +15,7 @@ "@kbn/expressions-plugin", "@kbn/react-kibana-context-render", "@kbn/react-kibana-mount", + "@kbn/std", ], "exclude": [ "target/**/*", diff --git a/src/platform/plugins/shared/ui_actions_enhanced/public/dynamic_actions/dynamic_action_manager.test.ts b/src/platform/plugins/shared/ui_actions_enhanced/public/dynamic_actions/dynamic_action_manager.test.ts index 47ce60fe76412..4d8f49ea34a5b 100644 --- a/src/platform/plugins/shared/ui_actions_enhanced/public/dynamic_actions/dynamic_action_manager.test.ts +++ b/src/platform/plugins/shared/ui_actions_enhanced/public/dynamic_actions/dynamic_action_manager.test.ts @@ -314,7 +314,7 @@ describe('DynamicActionManager', () => { await manager.createEvent(action, ['VALUE_CLICK_TRIGGER']); - const createdAction = actions.values().next().value; + const createdAction = await actions.values().next().value(); expect(createdAction.grouping).toBe(dynamicActionGrouping); }); @@ -477,7 +477,7 @@ describe('DynamicActionManager', () => { expect(actions.size).toBe(1); - const registeredAction1 = actions.values().next().value; + const registeredAction1 = await actions.values().next().value(); expect(registeredAction1.getDisplayName()).toBe('Action 3'); @@ -491,7 +491,7 @@ describe('DynamicActionManager', () => { expect(actions.size).toBe(1); - const registeredAction2 = actions.values().next().value; + const registeredAction2 = await actions.values().next().value(); expect(registeredAction2.getDisplayName()).toBe('foo'); }); @@ -600,7 +600,7 @@ describe('DynamicActionManager', () => { expect(actions.size).toBe(1); - const registeredAction1 = actions.values().next().value; + const registeredAction1 = await actions.values().next().value(); expect(registeredAction1.getDisplayName()).toBe('Action 3'); @@ -614,7 +614,7 @@ describe('DynamicActionManager', () => { expect(actions.size).toBe(1); - const registeredAction2 = actions.values().next().value; + const registeredAction2 = await actions.values().next().value(); expect(registeredAction2.getDisplayName()).toBe('Action 3'); }); @@ -733,10 +733,10 @@ describe('DynamicActionManager', () => { await manager.start(); - expect(uiActions.getTriggerActions('VALUE_CLICK_TRIGGER')).toHaveLength(2); + expect(await uiActions.getTriggerActions('VALUE_CLICK_TRIGGER')).toHaveLength(2); expect(await storage.list()).toEqual([event1, event3, event2]); await manager.stop(); - expect(uiActions.getTriggerActions('VALUE_CLICK_TRIGGER')).toHaveLength(0); + expect(await uiActions.getTriggerActions('VALUE_CLICK_TRIGGER')).toHaveLength(0); }); }); diff --git a/x-pack/platform/plugins/private/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_factory.tsx b/x-pack/platform/plugins/private/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_factory.tsx index 45187737ad09d..8b3e153ed8cb6 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_factory.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_factory.tsx @@ -278,7 +278,10 @@ export const getFieldStatsChartEmbeddableFactory = ( } }; - const addFilters = (filters: Filter[], actionId: string = ACTION_GLOBAL_APPLY_FILTER) => { + const addFilters = async ( + filters: Filter[], + actionId: string = ACTION_GLOBAL_APPLY_FILTER + ) => { if (!pluginStart.uiActions) { toasts.addWarning(ERROR_MSG.APPLY_FILTER_ERR); return; @@ -298,7 +301,7 @@ export const getFieldStatsChartEmbeddableFactory = ( filters, }; try { - const action = pluginStart.uiActions.getAction(actionId); + const action = await pluginStart.uiActions.getAction(actionId); action.execute(executeContext); } catch (error) { toasts.addWarning(ERROR_MSG.APPLY_FILTER_ERR); diff --git a/x-pack/platform/plugins/shared/lens/public/data_views_service/service.ts b/x-pack/platform/plugins/shared/lens/public/data_views_service/service.ts index 217d621b0ee98..275217cd66881 100644 --- a/x-pack/platform/plugins/shared/lens/public/data_views_service/service.ts +++ b/x-pack/platform/plugins/shared/lens/public/data_views_service/service.ts @@ -115,7 +115,7 @@ export const createIndexPatternService = ({ applyImmediately: true, }); 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, diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/loader.ts b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/loader.ts index 56ec53b7664fd..ce440f2ebe670 100644 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/loader.ts +++ b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/loader.ts @@ -227,7 +227,7 @@ export function renameIndexPattern({ }; } -export function triggerActionOnIndexPatternChange({ +export async function triggerActionOnIndexPatternChange({ state, layerId, uiActions, @@ -243,7 +243,7 @@ export function triggerActionOnIndexPatternChange({ const toDataView = indexPatternId; 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, diff --git a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx index e83323a36fadf..6b6facc06f339 100644 --- a/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx +++ b/x-pack/platform/plugins/shared/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx @@ -165,7 +165,7 @@ export function LayerPanels( ); const onRemoveLayer = useCallback( - (layerToRemoveId: string) => { + async (layerToRemoveId: string) => { const datasourcePublicAPI = props.framePublicAPI.datasourceLayers?.[layerToRemoveId]; const datasourceId = datasourcePublicAPI?.datasourceId; @@ -173,7 +173,7 @@ export function LayerPanels( const layerDatasource = datasourceMap[datasourceId]; const layerDatasourceState = datasourceStates?.[datasourceId]?.state; const trigger = props.uiActions.getTrigger(UPDATE_FILTER_REFERENCES_TRIGGER); - const action = props.uiActions.getAction(UPDATE_FILTER_REFERENCES_ACTION); + const action = await props.uiActions.getAction(UPDATE_FILTER_REFERENCES_ACTION); action?.execute({ trigger, diff --git a/x-pack/platform/plugins/shared/maps/public/classes/tooltips/tooltip_property.ts b/x-pack/platform/plugins/shared/maps/public/classes/tooltips/tooltip_property.ts index 3c103089eff84..12e038053581a 100644 --- a/x-pack/platform/plugins/shared/maps/public/classes/tooltips/tooltip_property.ts +++ b/x-pack/platform/plugins/shared/maps/public/classes/tooltips/tooltip_property.ts @@ -60,7 +60,7 @@ export interface RenderTooltipContentParams { layerId: string; featureId?: string | number; }) => Geometry | null; - onSingleValueTrigger?: (actionId: string, key: string, value: RawValue) => void; + onSingleValueTrigger?: (actionId: string, key: string, value: RawValue) => Promise; } export type RenderToolTipContent = (params: RenderTooltipContentParams) => JSX.Element; diff --git a/x-pack/platform/plugins/shared/maps/public/connected_components/map_container/map_container.tsx b/x-pack/platform/plugins/shared/maps/public/connected_components/map_container/map_container.tsx index fa77048f6b88e..ac2587f84e4a6 100644 --- a/x-pack/platform/plugins/shared/maps/public/connected_components/map_container/map_container.tsx +++ b/x-pack/platform/plugins/shared/maps/public/connected_components/map_container/map_container.tsx @@ -35,7 +35,7 @@ export interface Props { addFilters: ((filters: Filter[], actionId: string) => Promise) | null; getFilterActions?: () => Promise; getActionContext?: () => ActionExecutionContext; - onSingleValueTrigger?: (actionId: string, key: string, value: RawValue) => void; + onSingleValueTrigger?: (actionId: string, key: string, value: RawValue) => Promise; isMapLoading: boolean; cancelAllInFlightRequests: () => void; exitFullScreen: () => void; diff --git a/x-pack/platform/plugins/shared/maps/public/connected_components/mb_map/mb_map.tsx b/x-pack/platform/plugins/shared/maps/public/connected_components/mb_map/mb_map.tsx index 335dd984e905d..dad264297f394 100644 --- a/x-pack/platform/plugins/shared/maps/public/connected_components/mb_map/mb_map.tsx +++ b/x-pack/platform/plugins/shared/maps/public/connected_components/mb_map/mb_map.tsx @@ -73,7 +73,7 @@ export interface Props { addFilters: ((filters: Filter[], actionId: string) => Promise) | null; getFilterActions?: () => Promise; getActionContext?: () => ActionExecutionContext; - onSingleValueTrigger?: (actionId: string, key: string, value: RawValue) => void; + onSingleValueTrigger?: (actionId: string, key: string, value: RawValue) => Promise; renderTooltipContent?: RenderToolTipContent; timeslice?: Timeslice; featureModeActive: boolean; diff --git a/x-pack/platform/plugins/shared/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/feature_properties.tsx b/x-pack/platform/plugins/shared/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/feature_properties.tsx index 73a2260de9757..22ba7776e7d3c 100644 --- a/x-pack/platform/plugins/shared/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/feature_properties.tsx +++ b/x-pack/platform/plugins/shared/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/feature_properties.tsx @@ -40,7 +40,7 @@ interface Props { addFilters: ((filters: Filter[], actionId: string) => Promise) | null; getFilterActions?: () => Promise; getActionContext?: () => ActionExecutionContext; - onSingleValueTrigger?: (actionId: string, key: string, value: RawValue) => void; + onSingleValueTrigger?: (actionId: string, key: string, value: RawValue) => Promise; showFilterActions: (view: ReactNode) => void; } diff --git a/x-pack/platform/plugins/shared/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/features_tooltip.tsx b/x-pack/platform/plugins/shared/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/features_tooltip.tsx index 8ccc9640568eb..243ad3b1fb194 100644 --- a/x-pack/platform/plugins/shared/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/features_tooltip.tsx +++ b/x-pack/platform/plugins/shared/maps/public/connected_components/mb_map/tooltip_control/features_tooltip/features_tooltip.tsx @@ -28,7 +28,7 @@ interface Props { addFilters: ((filters: Filter[], actionId: string) => Promise) | null; getFilterActions?: () => Promise; getActionContext?: () => ActionExecutionContext; - onSingleValueTrigger?: (actionId: string, key: string, value: RawValue) => void; + onSingleValueTrigger?: (actionId: string, key: string, value: RawValue) => Promise; closeTooltip: () => void; features: TooltipFeature[]; isLocked: boolean; diff --git a/x-pack/platform/plugins/shared/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.tsx b/x-pack/platform/plugins/shared/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.tsx index cc5d623904df3..aad7c0c203a84 100644 --- a/x-pack/platform/plugins/shared/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.tsx +++ b/x-pack/platform/plugins/shared/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.tsx @@ -62,7 +62,7 @@ export interface Props { mbMap: MbMap; openOnClickTooltip: (tooltipState: TooltipState) => void; openOnHoverTooltip: (tooltipState: TooltipState) => void; - onSingleValueTrigger?: (actionId: string, key: string, value: RawValue) => void; + onSingleValueTrigger?: (actionId: string, key: string, value: RawValue) => Promise; openTooltips: TooltipState[]; renderTooltipContent?: RenderToolTipContent; updateOpenTooltips: (openTooltips: TooltipState[]) => void; diff --git a/x-pack/platform/plugins/shared/maps/public/connected_components/mb_map/tooltip_control/tooltip_popover.tsx b/x-pack/platform/plugins/shared/maps/public/connected_components/mb_map/tooltip_control/tooltip_popover.tsx index 0263c523f0fce..20182ab1387ae 100644 --- a/x-pack/platform/plugins/shared/maps/public/connected_components/mb_map/tooltip_control/tooltip_popover.tsx +++ b/x-pack/platform/plugins/shared/maps/public/connected_components/mb_map/tooltip_control/tooltip_popover.tsx @@ -38,7 +38,7 @@ interface Props { }) => Geometry | null; location: [number, number]; mbMap: MbMap; - onSingleValueTrigger?: (actionId: string, key: string, value: RawValue) => void; + onSingleValueTrigger?: (actionId: string, key: string, value: RawValue) => Promise; renderTooltipContent?: RenderToolTipContent; executionContext: KibanaExecutionContext; } diff --git a/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_action_handlers.ts b/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_action_handlers.ts index cbb933cda8b15..9a991d31d2667 100644 --- a/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_action_handlers.ts +++ b/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_action_handlers.ts @@ -33,7 +33,7 @@ export function initializeActionHandlers(getApi: () => MapApi | undefined) { ...getActionContext(), filters, }; - const action = getUiActions().getAction(actionId); + const action = await getUiActions().getAction(actionId); if (!action) { throw new Error('Unable to apply filter, could not locate action'); } @@ -60,8 +60,8 @@ export function initializeActionHandlers(getApi: () => MapApi | undefined) { ); return [...filterActions, ...valueClickActions.filter(isUrlDrilldown)]; }, - onSingleValueTrigger: (actionId: string, key: string, value: RawValue) => { - const action = getUiActions().getAction(actionId); + onSingleValueTrigger: async (actionId: string, key: string, value: RawValue) => { + const action = await getUiActions().getAction(actionId); if (!action) { throw new Error('Unable to apply action, could not locate action'); } diff --git a/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_cross_panel_actions.ts b/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_cross_panel_actions.ts index 29d572f2d67d5..145c55131dfa8 100644 --- a/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_cross_panel_actions.ts +++ b/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_cross_panel_actions.ts @@ -104,7 +104,7 @@ export function initializeCrossPanelActions({ } // debounce to fix timing issue for dashboard with multiple maps with synchronized movement and filter by map extent enabled - const setMapExtentFilter = _.debounce(() => { + const setMapExtentFilter = _.debounce(async () => { const mapExtent = getMapExtent(savedMap.getStore().getState()); const geoFieldNames = mapEmbeddablesSingleton.getGeoFieldNames(); @@ -126,21 +126,21 @@ export function initializeCrossPanelActions({ filters: [mapExtentFilter], controlledBy, }; - const action = getUiActions().getAction(ACTION_GLOBAL_APPLY_FILTER); + const action = await getUiActions().getAction(ACTION_GLOBAL_APPLY_FILTER); if (!action) { throw new Error('Unable to apply map extent filter, could not locate action'); } action.execute(executeContext); }, 100); - function clearMapExtentFilter() { + async function clearMapExtentFilter() { prevMapExtent = undefined; const executeContext = { ...getActionContext(), filters: [], controlledBy, }; - const action = getUiActions().getAction(ACTION_GLOBAL_APPLY_FILTER); + const action = await getUiActions().getAction(ACTION_GLOBAL_APPLY_FILTER); if (!action) { throw new Error('Unable to apply map extent filter, could not locate action'); }