diff --git a/docs/CHANGELOG.asciidoc b/docs/CHANGELOG.asciidoc index cafc1b6d78ce..6d42b25d4bce 100644 --- a/docs/CHANGELOG.asciidoc +++ b/docs/CHANGELOG.asciidoc @@ -6267,7 +6267,7 @@ Before you upgrade to 8.1.0, review the breaking changes, then mitigate the impa The `/api/reporting/generate/csv` endpoint has been removed. For more information, refer to {kibana-pull}121435[#121435]. *Impact* + -If you are using 7.13.0 and earlier, {kibana-ref-all}/8.1/automating-report-generation.html[regenerate the POST URLs] that you use to automatatically generate CSV reports. +If you are using 7.13.0 and earlier, {kibana-ref-all}/8.1/automating-report-generation.html[regenerate the POST URLs] that you use to automatically generate CSV reports. ==== [discrete] diff --git a/examples/embeddable_examples/public/react_embeddables/data_table/create_data_table_action.ts b/examples/embeddable_examples/public/react_embeddables/data_table/create_data_table_action.ts index 9971535e148d..6364073ade67 100644 --- a/examples/embeddable_examples/public/react_embeddables/data_table/create_data_table_action.ts +++ b/examples/embeddable_examples/public/react_embeddables/data_table/create_data_table_action.ts @@ -9,7 +9,11 @@ import { i18n } from '@kbn/i18n'; import { apiIsPresentationContainer } from '@kbn/presentation-containers'; import { EmbeddableApiContext } from '@kbn/presentation-publishing'; -import { IncompatibleActionError, UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import { + IncompatibleActionError, + UiActionsStart, + ADD_PANEL_TRIGGER, +} from '@kbn/ui-actions-plugin/public'; import { embeddableExamplesGrouping } from '../embeddable_examples_grouping'; import { ADD_DATA_TABLE_ACTION_ID, DATA_TABLE_ID } from './constants'; @@ -39,5 +43,5 @@ export const registerCreateDataTableAction = (uiActions: UiActionsStart) => { defaultMessage: 'Data table', }), }); - uiActions.attachAction('ADD_PANEL_TRIGGER', ADD_DATA_TABLE_ACTION_ID); + uiActions.attachAction(ADD_PANEL_TRIGGER, ADD_DATA_TABLE_ACTION_ID); }; diff --git a/examples/embeddable_examples/public/react_embeddables/embeddable_examples_grouping.ts b/examples/embeddable_examples/public/react_embeddables/embeddable_examples_grouping.ts index fa2ecd03b5d2..4c7f52d261fc 100644 --- a/examples/embeddable_examples/public/react_embeddables/embeddable_examples_grouping.ts +++ b/examples/embeddable_examples/public/react_embeddables/embeddable_examples_grouping.ts @@ -10,4 +10,5 @@ export const embeddableExamplesGrouping = { id: 'embeddableExamples', getIconType: () => 'documentation', getDisplayName: () => 'Embeddable examples', + order: -10, }; diff --git a/examples/embeddable_examples/public/react_embeddables/eui_markdown/create_eui_markdown_action.tsx b/examples/embeddable_examples/public/react_embeddables/eui_markdown/create_eui_markdown_action.tsx index 81c23a4d960b..c5eb2d72cc47 100644 --- a/examples/embeddable_examples/public/react_embeddables/eui_markdown/create_eui_markdown_action.tsx +++ b/examples/embeddable_examples/public/react_embeddables/eui_markdown/create_eui_markdown_action.tsx @@ -9,7 +9,11 @@ import { i18n } from '@kbn/i18n'; import { apiCanAddNewPanel } from '@kbn/presentation-containers'; import { EmbeddableApiContext } from '@kbn/presentation-publishing'; -import { IncompatibleActionError, UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import { + IncompatibleActionError, + UiActionsStart, + ADD_PANEL_TRIGGER, +} from '@kbn/ui-actions-plugin/public'; import { embeddableExamplesGrouping } from '../embeddable_examples_grouping'; import { ADD_EUI_MARKDOWN_ACTION_ID, EUI_MARKDOWN_ID } from './constants'; import { MarkdownEditorSerializedState } from './types'; @@ -41,7 +45,7 @@ export const registerCreateEuiMarkdownAction = (uiActions: UiActionsStart) => { defaultMessage: 'EUI Markdown', }), }); - uiActions.attachAction('ADD_PANEL_TRIGGER', ADD_EUI_MARKDOWN_ACTION_ID); + uiActions.attachAction(ADD_PANEL_TRIGGER, ADD_EUI_MARKDOWN_ACTION_ID); if (uiActions.hasTrigger('ADD_CANVAS_ELEMENT_TRIGGER')) { // Because Canvas is not enabled in Serverless, this trigger might not be registered - only attach // the create action if the Canvas-specific trigger does indeed exist. diff --git a/examples/embeddable_examples/public/react_embeddables/field_list/create_field_list_action.tsx b/examples/embeddable_examples/public/react_embeddables/field_list/create_field_list_action.tsx index e05868e7737d..175c3119955a 100644 --- a/examples/embeddable_examples/public/react_embeddables/field_list/create_field_list_action.tsx +++ b/examples/embeddable_examples/public/react_embeddables/field_list/create_field_list_action.tsx @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { apiCanAddNewPanel } from '@kbn/presentation-containers'; import { EmbeddableApiContext } from '@kbn/presentation-publishing'; -import { IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; +import { IncompatibleActionError, ADD_PANEL_TRIGGER } from '@kbn/ui-actions-plugin/public'; import { UiActionsPublicStart } from '@kbn/ui-actions-plugin/public/plugin'; import { embeddableExamplesGrouping } from '../embeddable_examples_grouping'; import { ADD_FIELD_LIST_ACTION_ID, FIELD_LIST_ID } from './constants'; @@ -34,5 +34,5 @@ export const registerCreateFieldListAction = (uiActions: UiActionsPublicStart) = defaultMessage: 'Field list', }), }); - uiActions.attachAction('ADD_PANEL_TRIGGER', ADD_FIELD_LIST_ACTION_ID); + uiActions.attachAction(ADD_PANEL_TRIGGER, ADD_FIELD_LIST_ACTION_ID); }; diff --git a/examples/embeddable_examples/public/react_embeddables/saved_book/create_saved_book_action.tsx b/examples/embeddable_examples/public/react_embeddables/saved_book/create_saved_book_action.tsx index 6916bd38cc28..eaaa607f7600 100644 --- a/examples/embeddable_examples/public/react_embeddables/saved_book/create_saved_book_action.tsx +++ b/examples/embeddable_examples/public/react_embeddables/saved_book/create_saved_book_action.tsx @@ -10,7 +10,7 @@ import { CoreStart } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { apiIsPresentationContainer } from '@kbn/presentation-containers'; import { EmbeddableApiContext } from '@kbn/presentation-publishing'; -import { IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; +import { IncompatibleActionError, ADD_PANEL_TRIGGER } from '@kbn/ui-actions-plugin/public'; import { UiActionsPublicStart } from '@kbn/ui-actions-plugin/public/plugin'; import { embeddableExamplesGrouping } from '../embeddable_examples_grouping'; import { @@ -67,5 +67,5 @@ export const registerCreateSavedBookAction = (uiActions: UiActionsPublicStart, c defaultMessage: 'Book', }), }); - uiActions.attachAction('ADD_PANEL_TRIGGER', ADD_SAVED_BOOK_ACTION_ID); + uiActions.attachAction(ADD_PANEL_TRIGGER, ADD_SAVED_BOOK_ACTION_ID); }; diff --git a/examples/embeddable_examples/public/react_embeddables/search/register_add_search_panel_action.tsx b/examples/embeddable_examples/public/react_embeddables/search/register_add_search_panel_action.tsx index 945e96918763..1bffd091164b 100644 --- a/examples/embeddable_examples/public/react_embeddables/search/register_add_search_panel_action.tsx +++ b/examples/embeddable_examples/public/react_embeddables/search/register_add_search_panel_action.tsx @@ -8,7 +8,11 @@ import { apiCanAddNewPanel } from '@kbn/presentation-containers'; import { EmbeddableApiContext } from '@kbn/presentation-publishing'; -import { IncompatibleActionError, UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import { + IncompatibleActionError, + type UiActionsStart, + ADD_PANEL_TRIGGER, +} from '@kbn/ui-actions-plugin/public'; import { embeddableExamplesGrouping } from '../embeddable_examples_grouping'; import { ADD_SEARCH_ACTION_ID, SEARCH_EMBEDDABLE_ID } from './constants'; import { SearchSerializedState } from './types'; @@ -33,7 +37,7 @@ export const registerAddSearchPanelAction = (uiActions: UiActionsStart) => { ); }, }); - uiActions.attachAction('ADD_PANEL_TRIGGER', ADD_SEARCH_ACTION_ID); + uiActions.attachAction(ADD_PANEL_TRIGGER, ADD_SEARCH_ACTION_ID); if (uiActions.hasTrigger('ADD_CANVAS_ELEMENT_TRIGGER')) { // Because Canvas is not enabled in Serverless, this trigger might not be registered - only attach // the create action if the Canvas-specific trigger does indeed exist. diff --git a/src/plugins/dashboard/public/triggers/index.ts b/packages/kbn-ui-actions-browser/src/triggers/dashboard_app_panel_trigger.ts similarity index 76% rename from src/plugins/dashboard/public/triggers/index.ts rename to packages/kbn-ui-actions-browser/src/triggers/dashboard_app_panel_trigger.ts index 96dfa814b949..74e894d2b556 100644 --- a/src/plugins/dashboard/public/triggers/index.ts +++ b/packages/kbn-ui-actions-browser/src/triggers/dashboard_app_panel_trigger.ts @@ -5,16 +5,18 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import { i18n } from '@kbn/i18n'; -import type { Trigger } from '@kbn/ui-actions-plugin/public'; +import { Trigger } from '.'; export const ADD_PANEL_TRIGGER = 'ADD_PANEL_TRIGGER'; + export const addPanelMenuTrigger: Trigger = { id: ADD_PANEL_TRIGGER, - title: i18n.translate('dashboard.addPanelMenuTrigger.title', { + title: i18n.translate('uiActions.triggers.dashboard.addPanelMenu.title', { defaultMessage: 'Add panel menu', }), - description: i18n.translate('dashboard.addPanelMenuTrigger.description', { + description: i18n.translate('uiActions.triggers.dashboard.addPanelMenu.description', { defaultMessage: "A new action will appear to the dashboard's add panel menu", }), }; diff --git a/packages/kbn-ui-actions-browser/src/triggers/index.ts b/packages/kbn-ui-actions-browser/src/triggers/index.ts index 091305791d85..d298be152441 100644 --- a/packages/kbn-ui-actions-browser/src/triggers/index.ts +++ b/packages/kbn-ui-actions-browser/src/triggers/index.ts @@ -11,3 +11,4 @@ export * from './row_click_trigger'; export * from './default_trigger'; export * from './visualize_field_trigger'; export * from './visualize_geo_field_trigger'; +export * from './dashboard_app_panel_trigger'; diff --git a/src/plugins/dashboard/public/dashboard_actions/index.ts b/src/plugins/dashboard/public/dashboard_actions/index.ts index ecffeaa5643a..ba93db83247d 100644 --- a/src/plugins/dashboard/public/dashboard_actions/index.ts +++ b/src/plugins/dashboard/public/dashboard_actions/index.ts @@ -8,7 +8,6 @@ import { CoreStart } from '@kbn/core/public'; import { CONTEXT_MENU_TRIGGER, PANEL_NOTIFICATION_TRIGGER } from '@kbn/embeddable-plugin/public'; - import { DashboardStartDependencies } from '../plugin'; import { AddToLibraryAction } from './add_to_library_action'; import { LegacyAddToLibraryAction } from './legacy_add_to_library_action'; diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/add_panel_action_menu_items.test.ts b/src/plugins/dashboard/public/dashboard_app/top_nav/add_panel_action_menu_items.test.ts index ed2fadd359db..180b9b7eb2ff 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/add_panel_action_menu_items.test.ts +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/add_panel_action_menu_items.test.ts @@ -7,7 +7,7 @@ */ import { getMockPresentationContainer } from '@kbn/presentation-containers/mocks'; -import { getAddPanelActionMenuItems } from './add_panel_action_menu_items'; +import { getAddPanelActionMenuItemsGroup } from './add_panel_action_menu_items'; describe('getAddPanelActionMenuItems', () => { it('returns the items correctly', async () => { @@ -54,39 +54,53 @@ describe('getAddPanelActionMenuItems', () => { ], }, ]; - const [items, grouped] = getAddPanelActionMenuItems( + const grouped = getAddPanelActionMenuItemsGroup( getMockPresentationContainer(), registeredActions, jest.fn() ); - expect(items).toStrictEqual([ - { - 'data-test-subj': 'create-action-Action name', - icon: 'pencil', - name: 'Action name', - onClick: expect.any(Function), - toolTipContent: 'Action tooltip', - }, - ]); + expect(grouped).toStrictEqual({ groupedAddPanelAction: { id: 'groupedAddPanelAction', title: 'Custom group', - icon: 'logoElasticsearch', + order: 0, + 'data-test-subj': 'dashboardEditorMenu-groupedAddPanelActionGroup', items: [ { 'data-test-subj': 'create-action-Action name 01', icon: 'pencil', + id: 'TEST_ACTION_01', name: 'Action name 01', onClick: expect.any(Function), - toolTipContent: 'Action tooltip', + description: 'Action tooltip', + order: 0, }, { 'data-test-subj': 'create-action-Action name', icon: 'empty', + id: 'TEST_ACTION_02', + name: 'Action name', + onClick: expect.any(Function), + description: 'Action tooltip', + order: 0, + }, + ], + }, + other: { + id: 'other', + title: 'Other', + order: -1, + 'data-test-subj': 'dashboardEditorMenu-otherGroup', + items: [ + { + id: 'ACTION_CREATE_ESQL_CHART', name: 'Action name', + icon: 'pencil', + description: 'Action tooltip', onClick: expect.any(Function), - toolTipContent: 'Action tooltip', + 'data-test-subj': 'create-action-Action name', + order: 0, }, ], }, @@ -94,12 +108,8 @@ describe('getAddPanelActionMenuItems', () => { }); it('returns empty array if no actions have been registered', async () => { - const [items, grouped] = getAddPanelActionMenuItems( - getMockPresentationContainer(), - [], - jest.fn() - ); - expect(items).toStrictEqual([]); + const grouped = getAddPanelActionMenuItemsGroup(getMockPresentationContainer(), [], jest.fn()); + expect(grouped).toStrictEqual({}); }); }); diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/add_panel_action_menu_items.ts b/src/plugins/dashboard/public/dashboard_app/top_nav/add_panel_action_menu_items.ts index 678129d0f580..4e90d94caa38 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/add_panel_action_menu_items.ts +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/add_panel_action_menu_items.ts @@ -5,13 +5,35 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import type { ActionExecutionContext, Action } from '@kbn/ui-actions-plugin/public'; + +import { + type ActionExecutionContext, + type Action, + addPanelMenuTrigger, +} from '@kbn/ui-actions-plugin/public'; import { PresentationContainer } from '@kbn/presentation-containers'; -import type { - EuiContextMenuPanelDescriptor, - EuiContextMenuPanelItemDescriptor, -} from '@elastic/eui'; -import { addPanelMenuTrigger } from '../../triggers'; +import { COMMON_EMBEDDABLE_GROUPING } from '@kbn/embeddable-plugin/public'; +import type { IconType, CommonProps } from '@elastic/eui'; +import React, { type MouseEventHandler } from 'react'; + +export interface PanelSelectionMenuItem extends Pick { + id: string; + name: string; + icon: IconType; + onClick: MouseEventHandler; + description?: string; + isDisabled?: boolean; + isDeprecated?: boolean; + order: number; +} + +export type GroupedAddPanelActions = Pick< + PanelSelectionMenuItem, + 'id' | 'isDisabled' | 'data-test-subj' | 'order' +> & { + title: string; + items: PanelSelectionMenuItem[]; +}; const onAddPanelActionClick = (action: Action, context: ActionExecutionContext, closePopover: () => void) => @@ -30,16 +52,11 @@ const onAddPanelActionClick = } else action.execute(context); }; -export type GroupedAddPanelActions = EuiContextMenuPanelDescriptor & { - icon?: string; -}; - -export const getAddPanelActionMenuItems = ( +export const getAddPanelActionMenuItemsGroup = ( api: PresentationContainer, actions: Array> | undefined, closePopover: () => void -): [EuiContextMenuPanelItemDescriptor[], Record] => { - const ungrouped: EuiContextMenuPanelItemDescriptor[] = []; +) => { const grouped: Record = {}; const context = { @@ -47,29 +64,31 @@ export const getAddPanelActionMenuItems = ( trigger: addPanelMenuTrigger, }; - const getMenuItem = (item: Action) => { + const getMenuItem = (item: Action): PanelSelectionMenuItem => { const actionName = item.getDisplayName(context); return { + id: item.id, name: actionName, icon: (typeof item.getIconType === 'function' ? item.getIconType(context) : undefined) ?? 'empty', onClick: onAddPanelActionClick(item, context, closePopover), 'data-test-subj': `create-action-${actionName}`, - toolTipContent: item?.getDisplayNameTooltip?.(context), + description: item?.getDisplayNameTooltip?.(context), + order: item.order ?? 0, }; }; actions?.forEach((item) => { if (Array.isArray(item.grouping)) { item.grouping.forEach((group) => { - if (!grouped[group.id]) { - grouped[group.id] = { - id: group.id, - icon: - (typeof group.getIconType === 'function' ? group.getIconType(context) : undefined) ?? - 'empty', - title: group.getDisplayName ? group.getDisplayName(context) : undefined, + const groupId = group.id; + if (!grouped[groupId]) { + grouped[groupId] = { + id: groupId, + title: group.getDisplayName ? group.getDisplayName(context) : '', + 'data-test-subj': `dashboardEditorMenu-${groupId}Group`, + order: group.order ?? 0, items: [], }; } @@ -77,9 +96,22 @@ export const getAddPanelActionMenuItems = ( grouped[group.id]!.items!.push(getMenuItem(item)); }); } else { - ungrouped.push(getMenuItem(item)); + // use other group as the default for definitions that don't have a group + const fallbackGroup = COMMON_EMBEDDABLE_GROUPING.other; + + if (!grouped[fallbackGroup.id]) { + grouped[fallbackGroup.id] = { + id: fallbackGroup.id, + title: fallbackGroup.getDisplayName?.({ embeddable: api }) || '', + 'data-test-subj': `dashboardEditorMenu-${fallbackGroup.id}Group`, + order: fallbackGroup.order || 0, + items: [], + }; + } + + grouped[fallbackGroup.id].items.push(getMenuItem(item)); } }); - return [ungrouped, grouped]; + return grouped; }; diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.scss b/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.scss index 859a0ee11067..0f463926908f 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.scss +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.scss @@ -3,4 +3,4 @@ @include euiOverflowShadow; max-height: 60vh; overflow-y: scroll; -} \ No newline at end of file +} diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.test.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.test.tsx index 6b2ff3f1900a..c2b51a5e0eda 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.test.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.test.tsx @@ -34,9 +34,10 @@ describe('mergeGroupedItemsProvider', () => { const factoryGroupMap = { group1: { - panelId: 'panel1', + id: 'panel1', appName: 'App 1', icon: 'icon1', + order: 10, factories: [mockFactory], }, } as unknown as Record; @@ -46,29 +47,23 @@ describe('mergeGroupedItemsProvider', () => { id: 'panel2', title: 'Panel 2', icon: 'icon2', + order: 10, items: [ { id: 'addPanelActionId', + order: 0, }, ], }, } as unknown as Record; it('should merge factoryGroupMap and groupedAddPanelAction correctly', () => { - const [initialPanelGroups, additionalPanels] = mergeGroupedItemsProvider( - getEmbeddableFactoryMenuItem - )(factoryGroupMap, groupedAddPanelAction); + const groupedPanels = mergeGroupedItemsProvider(getEmbeddableFactoryMenuItem)( + factoryGroupMap, + groupedAddPanelAction + ); - expect(initialPanelGroups).toEqual([ - { - 'data-test-subj': 'dashboardEditorMenu-group1Group', - name: 'App 1', - icon: 'icon1', - panel: 'panel1', - }, - ]); - - expect(additionalPanels).toEqual([ + expect(groupedPanels).toEqual([ { id: 'panel1', title: 'App 1', @@ -76,72 +71,68 @@ describe('mergeGroupedItemsProvider', () => { { icon: 'icon1', name: 'Factory 1', - toolTipContent: 'Factory 1 description', + id: 'mockFactory', + description: 'Factory 1 description', 'data-test-subj': 'createNew-mockFactory', onClick: expect.any(Function), + order: 0, }, { id: 'addPanelActionId', + order: 0, }, ], + 'data-test-subj': 'dashboardEditorMenu-group1Group', + order: 10, }, ]); }); it('should handle missing factoryGroup correctly', () => { - const [initialPanelGroups, additionalPanels] = mergeGroupedItemsProvider( - getEmbeddableFactoryMenuItem - )({}, groupedAddPanelAction); + const groupedPanels = mergeGroupedItemsProvider(getEmbeddableFactoryMenuItem)( + {}, + groupedAddPanelAction + ); - expect(initialPanelGroups).toEqual([ - { - 'data-test-subj': 'dashboardEditorMenu-group1Group', - name: 'Panel 2', - icon: 'icon2', - panel: 'panel2', - }, - ]); - - expect(additionalPanels).toEqual([ + expect(groupedPanels).toEqual([ { id: 'panel2', + icon: 'icon2', title: 'Panel 2', items: [ { id: 'addPanelActionId', + order: 0, }, ], + order: 10, }, ]); }); it('should handle missing groupedAddPanelAction correctly', () => { - const [initialPanelGroups, additionalPanels] = mergeGroupedItemsProvider( - getEmbeddableFactoryMenuItem - )(factoryGroupMap, {}); + const groupedPanels = mergeGroupedItemsProvider(getEmbeddableFactoryMenuItem)( + factoryGroupMap, + {} + ); - expect(initialPanelGroups).toEqual([ - { - 'data-test-subj': 'dashboardEditorMenu-group1Group', - name: 'App 1', - icon: 'icon1', - panel: 'panel1', - }, - ]); - - expect(additionalPanels).toEqual([ + expect(groupedPanels).toEqual([ { id: 'panel1', title: 'App 1', items: [ { icon: 'icon1', + id: 'mockFactory', name: 'Factory 1', - toolTipContent: 'Factory 1 description', + description: 'Factory 1 description', 'data-test-subj': 'createNew-mockFactory', onClick: expect.any(Function), + order: 0, }, ], + order: 10, + 'data-test-subj': 'dashboardEditorMenu-group1Group', }, ]); }); diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx index d92b913c28b5..ba7811dfec36 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx @@ -8,37 +8,30 @@ import './editor_menu.scss'; -import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react'; -import { - EuiBadge, - EuiContextMenu, - EuiContextMenuItemIcon, - type EuiContextMenuPanelDescriptor, - type EuiContextMenuPanelItemDescriptor, - EuiFlexGroup, - EuiFlexItem, - useEuiTheme, -} from '@elastic/eui'; +import React, { useEffect, useMemo, useState, useRef } from 'react'; +import { type IconType } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import type { Action } from '@kbn/ui-actions-plugin/public'; -import { ToolbarPopover } from '@kbn/shared-ux-button-toolbar'; +import { type Action, ADD_PANEL_TRIGGER } from '@kbn/ui-actions-plugin/public'; +import { ToolbarButton } from '@kbn/shared-ux-button-toolbar'; import { PresentationContainer } from '@kbn/presentation-containers'; import { type BaseVisType, VisGroups, type VisTypeAlias } from '@kbn/visualizations-plugin/public'; -import type { EmbeddableFactory } from '@kbn/embeddable-plugin/public'; +import { EmbeddableFactory, COMMON_EMBEDDABLE_GROUPING } from '@kbn/embeddable-plugin/public'; import { pluginServices } from '../../services/plugin_services'; -import { DASHBOARD_APP_ID } from '../../dashboard_constants'; -import { ADD_PANEL_TRIGGER } from '../../triggers'; import { - getAddPanelActionMenuItems, + getAddPanelActionMenuItemsGroup, + type PanelSelectionMenuItem, type GroupedAddPanelActions, } from './add_panel_action_menu_items'; +import { openDashboardPanelSelectionFlyout } from './open_dashboard_panel_selection_flyout'; +import type { DashboardServices } from '../../services/types'; +import { useDashboardAPI } from '../dashboard_app'; export interface FactoryGroup { id: string; appName: string; - icon: EuiContextMenuItemIcon; - panelId: number; + icon?: IconType; factories: EmbeddableFactory[]; + order: number; } interface UnwrappedEmbeddableFactory { @@ -49,31 +42,38 @@ interface UnwrappedEmbeddableFactory { export type GetEmbeddableFactoryMenuItem = ReturnType; export const getEmbeddableFactoryMenuItemProvider = - (api: PresentationContainer, closePopover: () => void) => (factory: EmbeddableFactory) => { + (api: PresentationContainer, closePopover: () => void) => + (factory: EmbeddableFactory): PanelSelectionMenuItem => { const icon = factory?.getIconType ? factory.getIconType() : 'empty'; - const toolTipContent = factory?.getDescription ? factory.getDescription() : undefined; - return { + id: factory.type, name: factory.getDisplayName(), icon, - toolTipContent, + description: factory.getDescription?.(), onClick: async () => { closePopover(); api.addNewPanel({ panelType: factory.type }, true); }, 'data-test-subj': `createNew-${factory.type}`, + order: factory.order ?? 0, }; }; +const sortGroupPanelsByOrder = (panelGroups: T[]): T[] => { + return panelGroups.sort( + // larger number sorted to the top + (panelGroupA, panelGroupB) => panelGroupB.order - panelGroupA.order + ); +}; + export const mergeGroupedItemsProvider = (getEmbeddableFactoryMenuItem: GetEmbeddableFactoryMenuItem) => ( factoryGroupMap: Record, groupedAddPanelAction: Record - ): [EuiContextMenuPanelItemDescriptor[], EuiContextMenuPanelDescriptor[]] => { - const initialPanelGroups: EuiContextMenuPanelItemDescriptor[] = []; - const additionalPanels: EuiContextMenuPanelDescriptor[] = []; + ) => { + const panelGroups: GroupedAddPanelActions[] = []; new Set(Object.keys(factoryGroupMap).concat(Object.keys(groupedAddPanelAction))).forEach( (groupId) => { @@ -83,87 +83,60 @@ export const mergeGroupedItemsProvider = const addPanelGroup = groupedAddPanelAction[groupId]; if (factoryGroup && addPanelGroup) { - const panelId = factoryGroup.panelId; - - initialPanelGroups.push({ - 'data-test-subj': dataTestSubj, - name: factoryGroup.appName, - icon: factoryGroup.icon, - panel: panelId, - }); - - additionalPanels.push({ - id: panelId, + panelGroups.push({ + id: factoryGroup.id, title: factoryGroup.appName, + 'data-test-subj': dataTestSubj, + order: factoryGroup.order, items: [ ...factoryGroup.factories.map(getEmbeddableFactoryMenuItem), ...(addPanelGroup?.items ?? []), ], }); } else if (factoryGroup) { - const panelId = factoryGroup.panelId; - - initialPanelGroups.push({ - 'data-test-subj': dataTestSubj, - name: factoryGroup.appName, - icon: factoryGroup.icon, - panel: panelId, - }); - - additionalPanels.push({ - id: panelId, + panelGroups.push({ + id: factoryGroup.id, title: factoryGroup.appName, + 'data-test-subj': dataTestSubj, + order: factoryGroup.order, items: factoryGroup.factories.map(getEmbeddableFactoryMenuItem), }); } else if (addPanelGroup) { - const panelId = addPanelGroup.id; - - initialPanelGroups.push({ - 'data-test-subj': dataTestSubj, - name: addPanelGroup.title, - icon: addPanelGroup.icon, - panel: panelId, - }); - - additionalPanels.push({ - id: panelId, - title: addPanelGroup.title, - items: addPanelGroup.items, - }); + panelGroups.push(addPanelGroup); } } ); - return [initialPanelGroups, additionalPanels]; + return panelGroups; }; -export const EditorMenu = ({ - createNewVisType, - isDisabled, - api, -}: { +interface EditorMenuProps { api: PresentationContainer; isDisabled?: boolean; /** Handler for creating new visualization of a specified type */ createNewVisType: (visType: BaseVisType | VisTypeAlias) => () => void; -}) => { +} + +export const EditorMenu = ({ createNewVisType, isDisabled, api }: EditorMenuProps) => { const isMounted = useRef(false); + const flyoutRef = useRef>(); + const dashboard = useDashboardAPI(); + + useEffect(() => { + isMounted.current = true; + + return () => { + isMounted.current = false; + flyoutRef.current?.close(); + }; + }, []); + const { embeddable, - visualizations: { - getAliases: getVisTypeAliases, - getByGroup: getVisTypesByGroup, - showNewVisModal, - }, + visualizations: { getAliases: getVisTypeAliases, getByGroup: getVisTypesByGroup }, uiActions, } = pluginServices.getServices(); - const { euiTheme } = useEuiTheme(); - - const embeddableFactories = useMemo( - () => Array.from(embeddable.getEmbeddableFactories()), - [embeddable] - ); const [unwrappedEmbeddableFactories, setUnwrappedEmbeddableFactories] = useState< UnwrappedEmbeddableFactory[] >([]); @@ -172,6 +145,11 @@ export const EditorMenu = ({ undefined ); + const embeddableFactories = useMemo( + () => Array.from(embeddable.getEmbeddableFactories()), + [embeddable] + ); + useEffect(() => { Promise.all( embeddableFactories.map>(async (factory) => ({ @@ -183,17 +161,6 @@ export const EditorMenu = ({ }); }, [embeddableFactories]); - const createNewAggsBasedVis = useCallback( - (visType?: BaseVisType) => () => - showNewVisModal({ - originatingApp: DASHBOARD_APP_ID, - outsideVisualizeApp: true, - showAggsSelection: true, - selectedVisType: visType, - }), - [showNewVisModal] - ); - const getSortedVisTypesByGroup = (group: VisGroups) => getVisTypesByGroup(group) .sort((a: BaseVisType | VisTypeAlias, b: BaseVisType | VisTypeAlias) => { @@ -210,8 +177,9 @@ export const EditorMenu = ({ .filter(({ disableCreate }: BaseVisType) => !disableCreate); const promotedVisTypes = getSortedVisTypesByGroup(VisGroups.PROMOTED); - const aggsBasedVisTypes = getSortedVisTypesByGroup(VisGroups.AGGBASED); const toolVisTypes = getSortedVisTypesByGroup(VisGroups.TOOLS); + const legacyVisTypes = getSortedVisTypesByGroup(VisGroups.LEGACY); + const visTypeAliases = getVisTypeAliases() .sort(({ promotion: a = false }: VisTypeAlias, { promotion: b = false }: VisTypeAlias) => a === b ? 0 : a ? -1 : 1 @@ -224,18 +192,6 @@ export const EditorMenu = ({ ); const factoryGroupMap: Record = {}; - const ungroupedFactories: EmbeddableFactory[] = []; - const aggBasedPanelID = 1; - - let panelCount = 1 + aggBasedPanelID; - - useEffect(() => { - isMounted.current = true; - - return () => { - isMounted.current = false; - }; - }, []); // Retrieve ADD_PANEL_TRIGGER actions useEffect(() => { @@ -243,6 +199,7 @@ export const EditorMenu = ({ const registeredActions = await uiActions?.getTriggerCompatibleActions?.(ADD_PANEL_TRIGGER, { embeddable: api, }); + if (isMounted.current) { setAddPanelActions(registeredActions); } @@ -260,142 +217,160 @@ export const EditorMenu = ({ } else { factoryGroupMap[group.id] = { id: group.id, - appName: group.getDisplayName ? group.getDisplayName({ embeddable }) : group.id, - icon: (group.getIconType - ? group.getIconType({ embeddable }) - : 'empty') as EuiContextMenuItemIcon, + appName: group.getDisplayName + ? group.getDisplayName({ embeddable: dashboard }) + : group.id, + icon: group.getIconType?.({ embeddable: dashboard }), factories: [factory], - panelId: panelCount, + order: group.order ?? 0, }; - - panelCount++; } }); } else { - ungroupedFactories.push(factory); + const fallbackGroup = COMMON_EMBEDDABLE_GROUPING.other; + + if (!factoryGroupMap[fallbackGroup.id]) { + factoryGroupMap[fallbackGroup.id] = { + id: fallbackGroup.id, + appName: fallbackGroup.getDisplayName + ? fallbackGroup.getDisplayName({ embeddable: dashboard }) + : fallbackGroup.id, + icon: fallbackGroup.getIconType?.({ embeddable: dashboard }) || 'empty', + factories: [], + order: fallbackGroup.order ?? 0, + }; + } + + factoryGroupMap[fallbackGroup.id].factories.push(factory); } }); - const getVisTypeMenuItem = (visType: BaseVisType): EuiContextMenuPanelItemDescriptor => { + const augmentedCreateNewVisType = ( + visType: Parameters[0], + cb: () => void + ) => { + const visClickHandler = createNewVisType(visType); + return () => { + visClickHandler(); + cb(); + }; + }; + + const getVisTypeMenuItem = ( + onClickCb: () => void, + visType: BaseVisType + ): PanelSelectionMenuItem => { const { name, title, titleInWizard, description, icon = 'empty', - group, isDeprecated, + order, } = visType; return { - name: !isDeprecated ? ( - titleInWizard || title - ) : ( - - {titleInWizard || title} - - - {i18n.translate('dashboard.editorMenu.deprecatedTag', { - defaultMessage: 'Deprecated', - })} - - - - ), - icon: icon as string, - onClick: - // not all the agg-based visualizations need to be created via the wizard - group === VisGroups.AGGBASED && visType.options.showIndexSelection - ? createNewAggsBasedVis(visType) - : createNewVisType(visType), + id: name, + name: titleInWizard || title, + isDeprecated, + icon, + onClick: augmentedCreateNewVisType(visType, onClickCb), 'data-test-subj': `visType-${name}`, - toolTipContent: description, + description, + order, }; }; const getVisTypeAliasMenuItem = ( + onClickCb: () => void, visTypeAlias: VisTypeAlias - ): EuiContextMenuPanelItemDescriptor => { - const { name, title, description, icon = 'empty' } = visTypeAlias; + ): PanelSelectionMenuItem => { + const { name, title, description, icon = 'empty', order } = visTypeAlias; return { + id: name, name: title, icon, - onClick: createNewVisType(visTypeAlias), + onClick: augmentedCreateNewVisType(visTypeAlias, onClickCb), 'data-test-subj': `visType-${name}`, - toolTipContent: description, + description, + order: order ?? 0, }; }; - const aggsPanelTitle = i18n.translate('dashboard.editorMenu.aggBasedGroupTitle', { - defaultMessage: 'Aggregation based', - }); - - const getEditorMenuPanels = (closePopover: () => void): EuiContextMenuPanelDescriptor[] => { - const getEmbeddableFactoryMenuItem = getEmbeddableFactoryMenuItemProvider(api, closePopover); + const getEditorMenuPanels = (closeFlyout: () => void): GroupedAddPanelActions[] => { + const getEmbeddableFactoryMenuItem = getEmbeddableFactoryMenuItemProvider(api, closeFlyout); - const [ungroupedAddPanelActions, groupedAddPanelAction] = getAddPanelActionMenuItems( + const groupedAddPanelAction = getAddPanelActionMenuItemsGroup( api, addPanelActions, - closePopover + closeFlyout ); - const [initialPanelGroups, additionalPanels] = mergeGroupedItemsProvider( - getEmbeddableFactoryMenuItem - )(factoryGroupMap, groupedAddPanelAction); - - const initialPanelItems = [ - ...visTypeAliases.map(getVisTypeAliasMenuItem), - ...ungroupedAddPanelActions, - ...toolVisTypes.map(getVisTypeMenuItem), - ...ungroupedFactories.map(getEmbeddableFactoryMenuItem), - ...initialPanelGroups, - ...promotedVisTypes.map(getVisTypeMenuItem), - ]; - if (aggsBasedVisTypes.length > 0) { - initialPanelItems.push({ - name: aggsPanelTitle, - icon: 'visualizeApp', - panel: aggBasedPanelID, - 'data-test-subj': `dashboardEditorAggBasedMenuItem`, - }); - } + const initialPanelGroups = mergeGroupedItemsProvider(getEmbeddableFactoryMenuItem)( + factoryGroupMap, + groupedAddPanelAction + ); - return [ - { - id: 0, - items: initialPanelItems, - }, - { - id: aggBasedPanelID, - title: aggsPanelTitle, - items: aggsBasedVisTypes.map(getVisTypeMenuItem), - }, - ...additionalPanels, - ]; + // enhance panel groups + return sortGroupPanelsByOrder(initialPanelGroups).map((panelGroup) => { + switch (panelGroup.id) { + case 'visualizations': { + return { + ...panelGroup, + items: sortGroupPanelsByOrder( + (panelGroup.items ?? []).concat( + // TODO: actually add grouping to vis type alias so we wouldn't randomly display an unintended item + visTypeAliases.map(getVisTypeAliasMenuItem.bind(null, closeFlyout)), + promotedVisTypes.map(getVisTypeMenuItem.bind(null, closeFlyout)) + ) + ), + }; + } + case COMMON_EMBEDDABLE_GROUPING.legacy.id: { + return { + ...panelGroup, + items: sortGroupPanelsByOrder( + (panelGroup.items ?? []).concat( + legacyVisTypes.map(getVisTypeMenuItem.bind(null, closeFlyout)) + ) + ), + }; + } + case COMMON_EMBEDDABLE_GROUPING.annotation.id: { + return { + ...panelGroup, + items: sortGroupPanelsByOrder( + (panelGroup.items ?? []).concat( + toolVisTypes.map(getVisTypeMenuItem.bind(null, closeFlyout)) + ) + ), + }; + } + default: { + return { + ...panelGroup, + items: sortGroupPanelsByOrder(panelGroup.items), + }; + } + } + }); }; return ( - { + flyoutRef.current = openDashboardPanelSelectionFlyout({ + getPanels: getEditorMenuPanels, + }); + }} size="s" - iconType="plusInCircle" - panelPaddingSize="none" - data-test-subj="dashboardEditorMenuButton" - > - {({ closePopover }: { closePopover: () => void }) => ( - - )} - + /> ); }; diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/open_dashboard_panel_selection_flyout.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/open_dashboard_panel_selection_flyout.tsx new file mode 100644 index 000000000000..8bd8dffc67c9 --- /dev/null +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/open_dashboard_panel_selection_flyout.tsx @@ -0,0 +1,255 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import React, { useEffect, useState, useRef } from 'react'; +import { toMountPoint } from '@kbn/react-kibana-mount'; +import { i18n as i18nFn } from '@kbn/i18n'; +import orderBy from 'lodash/orderBy'; +import { + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiForm, + EuiBadge, + EuiFormRow, + EuiTitle, + EuiFieldSearch, + useEuiTheme, + type EuiFlyoutProps, + EuiListGroup, + EuiListGroupItem, + EuiToolTip, + EuiText, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { pluginServices } from '../../services/plugin_services'; +import type { DashboardServices } from '../../services/types'; +import type { GroupedAddPanelActions, PanelSelectionMenuItem } from './add_panel_action_menu_items'; + +interface OpenDashboardPanelSelectionFlyoutArgs { + getPanels: (closePopover: () => void) => GroupedAddPanelActions[]; + flyoutPanelPaddingSize?: Exclude; +} + +interface Props extends Pick { + /** Handler to close flyout */ + close: () => void; + /** Padding for flyout */ + paddingSize: Exclude; +} + +export function openDashboardPanelSelectionFlyout({ + getPanels, + flyoutPanelPaddingSize = 'l', +}: OpenDashboardPanelSelectionFlyoutArgs) { + const { + overlays, + analytics, + settings: { i18n, theme }, + } = pluginServices.getServices(); + // eslint-disable-next-line prefer-const + let flyoutRef: ReturnType; + + const mount = toMountPoint( + React.createElement(function () { + const closeFlyout = () => flyoutRef.close(); + return ( + + ); + }), + { analytics, theme, i18n } + ); + + flyoutRef = overlays.openFlyout(mount, { + size: 'm', + maxWidth: 500, + paddingSize: flyoutPanelPaddingSize, + 'aria-labelledby': 'addPanelsFlyout', + 'data-test-subj': 'dashboardPanelSelectionFlyout', + }); + + return flyoutRef; +} + +export const DashboardPanelSelectionListFlyout: React.FC = ({ + close, + getPanels, + paddingSize, +}) => { + const { euiTheme } = useEuiTheme(); + const panels = useRef(getPanels(close)); + const [searchTerm, setSearchTerm] = useState(''); + const [panelsSearchResult, setPanelsSearchResult] = useState( + panels.current + ); + + useEffect(() => { + if (!searchTerm) { + return setPanelsSearchResult(panels.current); + } + + const q = searchTerm.toLowerCase(); + + setPanelsSearchResult( + orderBy( + panels.current.map((panel) => { + const groupSearchMatch = panel.title.toLowerCase().includes(q); + + const [groupSearchMatchAgg, items] = panel.items.reduce( + (acc, cur) => { + const searchMatch = cur.name.toLowerCase().includes(q); + + acc[0] = acc[0] || searchMatch; + acc[1].push({ + ...cur, + isDisabled: !(groupSearchMatch || searchMatch), + }); + + return acc; + }, + [groupSearchMatch, [] as PanelSelectionMenuItem[]] + ); + + return { + ...panel, + isDisabled: !groupSearchMatchAgg, + items, + }; + }), + ['isDisabled'] + ) + ); + }, [searchTerm]); + + return ( + <> + + +

+ +

+
+
+ + + + + + { + setSearchTerm(e.target.value); + }} + aria-label={i18nFn.translate( + 'dashboard.editorMenu.addPanelFlyout.searchLabelText', + { defaultMessage: 'search field for panels' } + )} + className="nsPanelSelectionFlyout__searchInput" + data-test-subj="dashboardPanelSelectionFlyout__searchInput" + /> + + + + + + {panelsSearchResult.some(({ isDisabled }) => !isDisabled) ? ( + panelsSearchResult.map( + ({ id, title, items, isDisabled, ['data-test-subj']: dataTestSubj }) => + !isDisabled ? ( + + + {typeof title === 'string' ?

{title}

: title} +
+ + {items?.map((item, idx) => { + return ( + + {!item.isDeprecated ? ( + {item.name} + ) : ( + + + {item.name} + + + + + + + + )} + + } + onClick={item?.onClick} + iconType={item.icon} + data-test-subj={item['data-test-subj']} + isDisabled={item.isDisabled} + /> + ); + })} + +
+ ) : null + ) + ) : ( + + + + )} +
+
+
+
+ + + + + + + + + + + ); +}; diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 0a231492d70b..c2838187d5ec 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -30,6 +30,7 @@ import type { UsageCollectionStart, } from '@kbn/usage-collection-plugin/public'; import { APP_WRAPPER_CLASS } from '@kbn/core/public'; +import { type UiActionsSetup, type UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; import { replaceUrlHashQuery } from '@kbn/kibana-utils-plugin/common'; @@ -39,7 +40,6 @@ import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public'; import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; import type { Start as InspectorStartContract } from '@kbn/inspector-plugin/public'; -import type { UiActionsSetup, UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public'; import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; @@ -70,7 +70,6 @@ import { import { DashboardMountContextProps } from './dashboard_app/types'; import type { FindDashboardsService } from './services/dashboard_content_management/types'; import { CONTENT_ID, LATEST_VERSION } from '../common/content_management'; -import { addPanelMenuTrigger } from './triggers'; import { GetPanelPlacementSettings } from './dashboard_container/panel_placement'; export interface DashboardFeatureFlagConfig { @@ -167,10 +166,6 @@ export class DashboardPlugin this.dashboardFeatureFlagConfig = this.initializerContext.config.get(); - // this trigger enables external consumers to register actions for - // adding items to the add panel menu - uiActions.registerTrigger(addPanelMenuTrigger); - core.analytics.registerEventType({ eventType: 'dashboard_loaded_with_data', schema: {}, diff --git a/src/plugins/embeddable/public/index.ts b/src/plugins/embeddable/public/index.ts index b00adeb711a5..88faa59b51c8 100644 --- a/src/plugins/embeddable/public/index.ts +++ b/src/plugins/embeddable/public/index.ts @@ -108,3 +108,5 @@ export { embeddableInputToSubject, embeddableOutputToSubject, } from './lib/embeddables/compatibility/embeddable_compatibility_utils'; + +export { COMMON_EMBEDDABLE_GROUPING } from './lib/embeddables/common/constants'; diff --git a/src/plugins/embeddable/public/lib/embeddables/common/constants.ts b/src/plugins/embeddable/public/lib/embeddables/common/constants.ts new file mode 100644 index 000000000000..78228ec370a0 --- /dev/null +++ b/src/plugins/embeddable/public/lib/embeddables/common/constants.ts @@ -0,0 +1,37 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { UiActionsPresentableGroup } from '@kbn/ui-actions-plugin/public'; + +export const COMMON_EMBEDDABLE_GROUPING: { [key: string]: UiActionsPresentableGroup } = { + legacy: { + id: 'legacy', + getDisplayName: () => + i18n.translate('embeddableApi.common.constants.grouping.legacy', { + defaultMessage: 'Legacy', + }), + order: -2, + }, + annotation: { + id: 'annotation-and-navigation', + getDisplayName: () => + i18n.translate('embeddableApi.common.constants.grouping.annotations', { + defaultMessage: 'Annotations and Navigation', + }), + }, + other: { + id: 'other', + getDisplayName: () => + i18n.translate('embeddableApi.common.constants.grouping.other', { + defaultMessage: 'Other', + }), + getIconType: () => 'empty', + order: -1, + }, +}; diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable_factory.ts b/src/plugins/embeddable/public/lib/embeddables/embeddable_factory.ts index cb19b82d75c9..39e32f756ae0 100644 --- a/src/plugins/embeddable/public/lib/embeddables/embeddable_factory.ts +++ b/src/plugins/embeddable/public/lib/embeddables/embeddable_factory.ts @@ -148,4 +148,6 @@ export interface EmbeddableFactory< initialInput: TEmbeddableInput, parent?: IContainer ): Promise; + + order?: number; } diff --git a/src/plugins/image_embeddable/public/actions/create_image_action.ts b/src/plugins/image_embeddable/public/actions/create_image_action.ts index 02cd8b26e118..bbbe0144856e 100644 --- a/src/plugins/image_embeddable/public/actions/create_image_action.ts +++ b/src/plugins/image_embeddable/public/actions/create_image_action.ts @@ -9,7 +9,8 @@ import { i18n } from '@kbn/i18n'; import { CanAddNewPanel } from '@kbn/presentation-containers'; import { EmbeddableApiContext } from '@kbn/presentation-publishing'; -import { IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; +import { COMMON_EMBEDDABLE_GROUPING } from '@kbn/embeddable-plugin/public'; +import { IncompatibleActionError, ADD_PANEL_TRIGGER } from '@kbn/ui-actions-plugin/public'; import { ADD_IMAGE_EMBEDDABLE_ACTION_ID, IMAGE_EMBEDDABLE_TYPE, @@ -27,6 +28,7 @@ export const registerCreateImageAction = () => { uiActionsService.registerAction({ id: ADD_IMAGE_EMBEDDABLE_ACTION_ID, getIconType: () => 'image', + order: 20, isCompatible: async ({ embeddable: parentApi }) => { return Boolean(await parentApiIsCompatible(parentApi)); }, @@ -45,13 +47,14 @@ export const registerCreateImageAction = () => { // swallow the rejection, since this just means the user closed without saving } }, + grouping: [COMMON_EMBEDDABLE_GROUPING.annotation], getDisplayName: () => i18n.translate('imageEmbeddable.imageEmbeddableFactory.displayName', { defaultMessage: 'Image', }), }); - uiActionsService.attachAction('ADD_PANEL_TRIGGER', ADD_IMAGE_EMBEDDABLE_ACTION_ID); + uiActionsService.attachAction(ADD_PANEL_TRIGGER, ADD_IMAGE_EMBEDDABLE_ACTION_ID); if (uiActionsService.hasTrigger('ADD_CANVAS_ELEMENT_TRIGGER')) { // Because Canvas is not enabled in Serverless, this trigger might not be registered - only attach // the create action if the Canvas-specific trigger does indeed exist. diff --git a/src/plugins/links/public/embeddable/links_embeddable.tsx b/src/plugins/links/public/embeddable/links_embeddable.tsx index dcc49a7265a4..523d8706b2b8 100644 --- a/src/plugins/links/public/embeddable/links_embeddable.tsx +++ b/src/plugins/links/public/embeddable/links_embeddable.tsx @@ -17,6 +17,7 @@ import { Embeddable, ReferenceOrValueEmbeddable, SavedObjectEmbeddableInput, + COMMON_EMBEDDABLE_GROUPING, } from '@kbn/embeddable-plugin/public'; import { CONTENT_ID } from '../../common'; @@ -44,6 +45,8 @@ export class LinksEmbeddable public attributes?: LinksAttributes; public attributes$ = new Subject(); + public grouping = [COMMON_EMBEDDABLE_GROUPING.annotation]; + constructor( config: LinksConfig, initialInput: LinksInput, diff --git a/src/plugins/links/public/embeddable/links_embeddable_factory.ts b/src/plugins/links/public/embeddable/links_embeddable_factory.ts index 9ff3877b8a42..40d377345e4f 100644 --- a/src/plugins/links/public/embeddable/links_embeddable_factory.ts +++ b/src/plugins/links/public/embeddable/links_embeddable_factory.ts @@ -14,6 +14,7 @@ import { EmbeddableFactory, EmbeddableFactoryDefinition, ErrorEmbeddable, + COMMON_EMBEDDABLE_GROUPING, } from '@kbn/embeddable-plugin/public'; import { GetMigrationFunctionObjectFn, @@ -55,7 +56,8 @@ export class LinksFactoryDefinition | ((state: EmbeddableStateWithType, stats: Record) => Record) | undefined; migrations?: MigrateFunctionsObject | GetMigrationFunctionObjectFn | undefined; - grouping?: UiActionsPresentableGrouping | undefined; + grouping: UiActionsPresentableGrouping = [COMMON_EMBEDDABLE_GROUPING.annotation]; + public readonly type = CONTENT_ID; public readonly isContainerType = false; diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts index 059ebad4b2be..cb9a2ae53ef0 100644 --- a/src/plugins/ui_actions/public/index.ts +++ b/src/plugins/ui_actions/public/index.ts @@ -24,6 +24,7 @@ export { ActionInternal, createAction, IncompatibleActionError } from './actions export { buildContextMenuForActions } from './context_menu'; export type { Presentable as UiActionsPresentable, + PresentableGroup as UiActionsPresentableGroup, PresentableGrouping as UiActionsPresentableGrouping, } from '@kbn/ui-actions-browser/src/types'; export type { Trigger, RowClickContext } from '@kbn/ui-actions-browser/src/triggers'; @@ -34,6 +35,8 @@ export { visualizeGeoFieldTrigger, ROW_CLICK_TRIGGER, rowClickTrigger, + ADD_PANEL_TRIGGER, + addPanelMenuTrigger, } from '@kbn/ui-actions-browser/src/triggers'; export type { VisualizeFieldContext } from './types'; export { diff --git a/src/plugins/ui_actions/public/plugin.ts b/src/plugins/ui_actions/public/plugin.ts index 1dbff5b9729a..5da343a0f640 100644 --- a/src/plugins/ui_actions/public/plugin.ts +++ b/src/plugins/ui_actions/public/plugin.ts @@ -12,6 +12,7 @@ import { rowClickTrigger, visualizeFieldTrigger, visualizeGeoFieldTrigger, + addPanelMenuTrigger, } from '@kbn/ui-actions-browser/src/triggers'; import { UiActionsService } from './service'; import { setAnalytics, setI18n, setTheme } from './services'; @@ -48,6 +49,7 @@ export class UiActionsPlugin constructor(_initializerContext: PluginInitializerContext) {} public setup(_core: CoreSetup): UiActionsPublicSetup { + this.service.registerTrigger(addPanelMenuTrigger); this.service.registerTrigger(rowClickTrigger); this.service.registerTrigger(visualizeFieldTrigger); this.service.registerTrigger(visualizeGeoFieldTrigger); diff --git a/src/plugins/vis_type_markdown/public/markdown_vis.ts b/src/plugins/vis_type_markdown/public/markdown_vis.ts index 33acfa21cd0b..4ebda6058777 100644 --- a/src/plugins/vis_type_markdown/public/markdown_vis.ts +++ b/src/plugins/vis_type_markdown/public/markdown_vis.ts @@ -27,6 +27,7 @@ export const markdownVisDefinition: VisTypeDefinition = { description: i18n.translate('visTypeMarkdown.markdownDescription', { defaultMessage: 'Add text and images to your dashboard.', }), + order: 30, toExpressionAst, visConfig: { defaults: { diff --git a/src/plugins/vis_types/timeseries/public/metrics_type.ts b/src/plugins/vis_types/timeseries/public/metrics_type.ts index 51b0fcce5a58..92361c6c53e1 100644 --- a/src/plugins/vis_types/timeseries/public/metrics_type.ts +++ b/src/plugins/vis_types/timeseries/public/metrics_type.ts @@ -104,7 +104,8 @@ export const metricsVisDefinition: VisTypeDefinition< defaultMessage: 'Perform advanced analysis of your time series data.', }), icon: 'visVisualBuilder', - group: VisGroups.PROMOTED, + group: VisGroups.LEGACY, + order: 10, visConfig: { defaults: { id: () => uuidv4(), diff --git a/src/plugins/visualizations/public/actions/add_agg_vis_action.ts b/src/plugins/visualizations/public/actions/add_agg_vis_action.ts new file mode 100644 index 000000000000..62c8e3654db6 --- /dev/null +++ b/src/plugins/visualizations/public/actions/add_agg_vis_action.ts @@ -0,0 +1,64 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { + apiHasAppContext, + EmbeddableApiContext, + HasType, + HasAppContext, +} from '@kbn/presentation-publishing'; +import { COMMON_EMBEDDABLE_GROUPING } from '@kbn/embeddable-plugin/public'; +import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; +import { apiHasType } from '@kbn/presentation-publishing'; +import { apiCanAddNewPanel, CanAddNewPanel } from '@kbn/presentation-containers'; +import { showNewVisModal } from '../wizard/show_new_vis'; + +const ADD_AGG_VIS_ACTION_ID = 'ADD_AGG_VIS'; + +type AddAggVisualizationPanelActionApi = HasType & CanAddNewPanel & HasAppContext; + +const isApiCompatible = (api: unknown | null): api is AddAggVisualizationPanelActionApi => { + return apiHasType(api) && apiCanAddNewPanel(api) && apiHasAppContext(api); +}; + +export class AddAggVisualizationPanelAction implements Action { + public readonly type = ADD_AGG_VIS_ACTION_ID; + public readonly id = ADD_AGG_VIS_ACTION_ID; + public readonly grouping = [COMMON_EMBEDDABLE_GROUPING.legacy]; + + public readonly order = 20; + + constructor() {} + + public getIconType() { + return 'visualizeApp'; + } + + public getDisplayName() { + return i18n.translate('visualizations.uiAction.addAggVis.displayName', { + defaultMessage: 'Aggregation based', + }); + } + + public async isCompatible({ embeddable }: EmbeddableApiContext) { + return isApiCompatible(embeddable); + } + + public async execute({ embeddable }: EmbeddableApiContext): Promise { + if (!isApiCompatible(embeddable)) { + throw new IncompatibleActionError(); + } + + showNewVisModal({ + originatingApp: embeddable.getAppContext().currentAppId, + outsideVisualizeApp: true, + showAggsSelection: true, + }); + } +} diff --git a/src/plugins/visualizations/public/embeddable/constants.ts b/src/plugins/visualizations/public/embeddable/constants.ts index cec3bd6cdfc8..920562103e24 100644 --- a/src/plugins/visualizations/public/embeddable/constants.ts +++ b/src/plugins/visualizations/public/embeddable/constants.ts @@ -7,3 +7,14 @@ */ export { VISUALIZE_EMBEDDABLE_TYPE } from '../../common/constants'; + +export const COMMON_VISUALIZATION_GROUPING = [ + { + id: 'visualizations', + getDisplayName: () => 'Visualizations', + getIconType: () => { + return 'visGauge'; + }, + order: 1000, + }, +]; diff --git a/src/plugins/visualizations/public/embeddable/index.ts b/src/plugins/visualizations/public/embeddable/index.ts index ae0748f4475c..ed3fef1c9ad4 100644 --- a/src/plugins/visualizations/public/embeddable/index.ts +++ b/src/plugins/visualizations/public/embeddable/index.ts @@ -7,7 +7,7 @@ */ export { VisualizeEmbeddableFactory } from './visualize_embeddable_factory'; -export { VISUALIZE_EMBEDDABLE_TYPE } from './constants'; +export { VISUALIZE_EMBEDDABLE_TYPE, COMMON_VISUALIZATION_GROUPING } from './constants'; export { VIS_EVENT_TO_TRIGGER } from './events'; export { createVisEmbeddableFromObject } from './create_vis_embeddable_from_object'; diff --git a/src/plugins/visualizations/public/index.ts b/src/plugins/visualizations/public/index.ts index 2f00bba14200..838ac3dbd754 100644 --- a/src/plugins/visualizations/public/index.ts +++ b/src/plugins/visualizations/public/index.ts @@ -21,6 +21,7 @@ export { apiHasVisualizeConfig, VISUALIZE_EMBEDDABLE_TYPE, VIS_EVENT_TO_TRIGGER, + COMMON_VISUALIZATION_GROUPING, } from './embeddable'; export { VisualizationContainer } from './components'; export { getVisSchemas } from './vis_schemas'; diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index bc208c6bb785..c97ff8f4eba4 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -36,7 +36,7 @@ import type { ApplicationStart, SavedObjectsClientContract, } from '@kbn/core/public'; -import type { UiActionsStart, UiActionsSetup } from '@kbn/ui-actions-plugin/public'; +import { UiActionsStart, UiActionsSetup, ADD_PANEL_TRIGGER } from '@kbn/ui-actions-plugin/public'; import type { SavedObjectsStart } from '@kbn/saved-objects-plugin/public'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import type { @@ -47,7 +47,11 @@ import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; import type { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { ExpressionsSetup, ExpressionsStart } from '@kbn/expressions-plugin/public'; -import { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public'; +import { + CONTEXT_MENU_TRIGGER, + EmbeddableSetup, + EmbeddableStart, +} from '@kbn/embeddable-plugin/public'; import type { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public'; import type { NavigationPublicPluginStart as NavigationStart } from '@kbn/navigation-plugin/public'; import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; @@ -122,6 +126,7 @@ import { } from '../common/content_management'; import { SerializedVisData } from '../common'; import { VisualizeByValueInput } from './embeddable/visualize_embeddable'; +import { AddAggVisualizationPanelAction } from './actions/add_agg_vis_action'; /** * Interface for this plugin's returned setup/start contracts. @@ -394,7 +399,9 @@ export class VisualizationsPlugin uiActions.registerTrigger(visualizeEditorTrigger); uiActions.registerTrigger(dashboardVisualizationPanelTrigger); const editInLensAction = new EditInLensAction(data.query.timefilter.timefilter); - uiActions.addTriggerAction('CONTEXT_MENU_TRIGGER', editInLensAction); + uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, editInLensAction); + const addAggVisAction = new AddAggVisualizationPanelAction(); + uiActions.addTriggerAction(ADD_PANEL_TRIGGER, addAggVisAction); const embeddableFactory = new VisualizeEmbeddableFactory({ start }); embeddable.registerEmbeddableFactory(VISUALIZE_EMBEDDABLE_TYPE, embeddableFactory); diff --git a/src/plugins/visualizations/public/vis_types/base_vis_type.ts b/src/plugins/visualizations/public/vis_types/base_vis_type.ts index 26801421159b..7a412aa40f02 100644 --- a/src/plugins/visualizations/public/vis_types/base_vis_type.ts +++ b/src/plugins/visualizations/public/vis_types/base_vis_type.ts @@ -24,6 +24,7 @@ const defaultOptions: VisTypeOptions = { export class BaseVisType { public readonly name; public readonly title; + public readonly order; public readonly description; public readonly note; public readonly getSupportedTriggers; @@ -67,6 +68,7 @@ export class BaseVisType { this.title = opts.title; this.icon = opts.icon; this.image = opts.image; + this.order = opts.order ?? 0; this.suppressWarnings = opts.suppressWarnings; this.visConfig = defaultsDeep({}, opts.visConfig, { defaults: {} }); this.editorConfig = defaultsDeep({}, opts.editorConfig, { collections: {} }); diff --git a/src/plugins/visualizations/public/vis_types/types.ts b/src/plugins/visualizations/public/vis_types/types.ts index 2f689cb81aee..b1920d5bb3a6 100644 --- a/src/plugins/visualizations/public/vis_types/types.ts +++ b/src/plugins/visualizations/public/vis_types/types.ts @@ -217,4 +217,6 @@ export interface VisTypeDefinition { * have incosistencies in legacy visLib visualizations */ readonly visConfig: Record; + + readonly order?: number; } diff --git a/src/plugins/visualizations/public/vis_types/vis_groups_enum.ts b/src/plugins/visualizations/public/vis_types/vis_groups_enum.ts index c8bd320c2f61..10a38cb69ba1 100644 --- a/src/plugins/visualizations/public/vis_types/vis_groups_enum.ts +++ b/src/plugins/visualizations/public/vis_types/vis_groups_enum.ts @@ -10,4 +10,5 @@ export enum VisGroups { PROMOTED = 'promoted', TOOLS = 'tools', AGGBASED = 'aggbased', + LEGACY = 'legacy', } diff --git a/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts b/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts index 617f0386f618..f736455faf04 100644 --- a/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts +++ b/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts @@ -117,6 +117,7 @@ export interface VisTypeAlias { visualizations: VisualizationsAppExtension; [appName: string]: unknown; }; + order?: number; } let registry: VisTypeAlias[] = []; diff --git a/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.tsx b/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.tsx index f4cdd0597883..1cf6ced52f41 100644 --- a/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.tsx +++ b/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.tsx @@ -35,6 +35,7 @@ interface VisTypeListEntry { } interface AggBasedSelectionProps { + openedAsRoot?: boolean; onVisTypeSelected: (visType: BaseVisType) => void; visTypesRegistry: TypesStart; toggleGroups: (flag: boolean) => void; @@ -58,13 +59,15 @@ class AggBasedSelection extends React.Component - this.props.toggleGroups(true)} /> + {this.props.openedAsRoot ? null : ( + this.props.toggleGroups(true)} /> + )} { return !visDefinition.disableCreate; }), @@ -65,6 +67,7 @@ function GroupSelection(props: GroupSelectionProps) { ), [props.visTypesRegistry] ); + return ( <> diff --git a/src/plugins/visualizations/public/wizard/new_vis_modal.tsx b/src/plugins/visualizations/public/wizard/new_vis_modal.tsx index b1e5de321526..382474dd11b5 100644 --- a/src/plugins/visualizations/public/wizard/new_vis_modal.tsx +++ b/src/plugins/visualizations/public/wizard/new_vis_modal.tsx @@ -106,6 +106,7 @@ class NewVisModal extends React.Component this.setState({ showGroups: flag })} + openedAsRoot={this.props.showAggsSelection && !this.props.selectedVisType} /> ); diff --git a/src/plugins/visualizations/public/wizard/show_new_vis.tsx b/src/plugins/visualizations/public/wizard/show_new_vis.tsx index 867af06637ce..e63ddc48f00d 100644 --- a/src/plugins/visualizations/public/wizard/show_new_vis.tsx +++ b/src/plugins/visualizations/public/wizard/show_new_vis.tsx @@ -7,9 +7,8 @@ */ import React, { lazy, Suspense } from 'react'; -import ReactDOM from 'react-dom'; import { EuiPortal, EuiProgress } from '@elastic/eui'; -import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import { getHttp, getTypes, @@ -50,47 +49,54 @@ export function showNewVisModal({ selectedVisType, }: ShowNewVisModalParams = {}) { const container = document.createElement('div'); + let isClosed = false; + + // initialize variable that will hold reference for unmount + // eslint-disable-next-line prefer-const + let unmount: ReturnType>; + const handleClose = () => { if (isClosed) return; - ReactDOM.unmountComponentAtNode(container); - document.body.removeChild(container); - if (onClose) { - onClose(); - } + + onClose?.(); + unmount?.(); isClosed = true; }; - document.body.appendChild(container); - const element = ( - - - - - } - > - - - + const mount = toMountPoint( + React.createElement(function () { + return ( + + + + } + > + + + ); + }), + { analytics: getAnalytics(), i18n: getI18n(), theme: getTheme() } ); - ReactDOM.render(element, container); + + unmount = mount(container); return () => handleClose(); } diff --git a/src/plugins/visualizations/tsconfig.json b/src/plugins/visualizations/tsconfig.json index 56d8275f80ea..1592eff839af 100644 --- a/src/plugins/visualizations/tsconfig.json +++ b/src/plugins/visualizations/tsconfig.json @@ -72,7 +72,8 @@ "@kbn/presentation-publishing", "@kbn/shared-ux-markdown", "@kbn/react-kibana-context-render", - "@kbn/react-kibana-mount" + "@kbn/react-kibana-mount", + "@kbn/presentation-containers" ], "exclude": [ "target/**/*", diff --git a/test/functional/apps/dashboard/group5/empty_dashboard.ts b/test/functional/apps/dashboard/group5/empty_dashboard.ts index 6939833a8008..a26382daa91a 100644 --- a/test/functional/apps/dashboard/group5/empty_dashboard.ts +++ b/test/functional/apps/dashboard/group5/empty_dashboard.ts @@ -60,7 +60,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should open editor menu when editor button is clicked', async () => { await dashboardAddPanel.clickEditorMenuButton(); - await testSubjects.existOrFail('dashboardEditorContextMenu'); + await testSubjects.existOrFail('dashboardPanelSelectionFlyout'); }); }); } diff --git a/test/functional/services/dashboard/add_panel.ts b/test/functional/services/dashboard/add_panel.ts index 1f4fa6b14aeb..ffc62bdfdb68 100644 --- a/test/functional/services/dashboard/add_panel.ts +++ b/test/functional/services/dashboard/add_panel.ts @@ -52,16 +52,16 @@ export class DashboardAddPanelService extends FtrService { async clickEditorMenuButton() { this.log.debug('DashboardAddPanel.clickEditorMenuButton'); await this.testSubjects.click('dashboardEditorMenuButton'); - await this.testSubjects.existOrFail('dashboardEditorContextMenu'); + await this.testSubjects.existOrFail('dashboardPanelSelectionFlyout'); } async expectEditorMenuClosed() { - await this.testSubjects.missingOrFail('dashboardEditorContextMenu'); + await this.testSubjects.missingOrFail('dashboardPanelSelectionFlyout'); } async clickAggBasedVisualizations() { this.log.debug('DashboardAddPanel.clickEditorMenuAggBasedMenuItem'); - await this.testSubjects.click('dashboardEditorAggBasedMenuItem'); + await this.clickAddNewPanelFromUIActionLink('Aggregation based'); } async clickVisType(visType: string) { @@ -69,9 +69,9 @@ export class DashboardAddPanelService extends FtrService { await this.testSubjects.click(`visType-${visType}`); } - async clickEmbeddableFactoryGroupButton(groupId: string) { - this.log.debug('DashboardAddPanel.clickEmbeddableFactoryGroupButton'); - await this.testSubjects.click(`dashboardEditorMenu-${groupId}Group`); + async verifyEmbeddableFactoryGroupExists(groupId: string) { + this.log.debug('DashboardAddPanel.verifyEmbeddableFactoryGroupExists'); + await this.testSubjects.existOrFail(`dashboardEditorMenu-${groupId}Group`); } async clickAddNewEmbeddableLink(type: string) { diff --git a/x-pack/plugins/aiops/public/ui_actions/create_change_point_chart.tsx b/x-pack/plugins/aiops/public/ui_actions/create_change_point_chart.tsx index f3bd3c359a34..16ad06b10392 100644 --- a/x-pack/plugins/aiops/public/ui_actions/create_change_point_chart.tsx +++ b/x-pack/plugins/aiops/public/ui_actions/create_change_point_chart.tsx @@ -34,11 +34,13 @@ export function createAddChangePointChartAction( id: 'ml', getDisplayName: () => i18n.translate('xpack.aiops.navMenu.mlAppNameText', { - defaultMessage: 'Machine Learning', + defaultMessage: 'Machine Learning and Analytics', }), getIconType: () => 'machineLearningApp', }, ], + order: 10, + getIconType: () => 'machineLearningApp', getDisplayName: () => i18n.translate('xpack.aiops.embeddableChangePointChartDisplayName', { defaultMessage: 'Change point detection', diff --git a/x-pack/plugins/aiops/public/ui_actions/index.ts b/x-pack/plugins/aiops/public/ui_actions/index.ts index daa1b8ffd5ff..c8e4edb58792 100644 --- a/x-pack/plugins/aiops/public/ui_actions/index.ts +++ b/x-pack/plugins/aiops/public/ui_actions/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { UiActionsSetup } from '@kbn/ui-actions-plugin/public'; +import { type UiActionsSetup, ADD_PANEL_TRIGGER } from '@kbn/ui-actions-plugin/public'; import { CONTEXT_MENU_TRIGGER } from '@kbn/embeddable-plugin/public'; import { categorizeFieldTrigger, @@ -26,7 +26,7 @@ export function registerAiopsUiActions( const openChangePointInMlAppAction = createOpenChangePointInMlAppAction(coreStart, pluginStart); const addChangePointChartAction = createAddChangePointChartAction(coreStart, pluginStart); - uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addChangePointChartAction); + uiActions.addTriggerAction(ADD_PANEL_TRIGGER, addChangePointChartAction); uiActions.registerTrigger(categorizeFieldTrigger); diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx index 24dcde46cf90..5c424961d7f5 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx @@ -158,14 +158,14 @@ export const EditorMenu: FC = ({ items: [ ...visTypeAliases.map(getVisTypeAliasMenuItem), ...getAddPanelActionMenuItems(closePopover), + ...ungroupedFactories.map(getEmbeddableFactoryMenuItem), + ...promotedVisTypes.map(getVisTypeMenuItem), ...Object.values(factoryGroupMap).map(({ id, appName, icon, panelId }) => ({ name: appName, icon, panel: panelId, 'data-test-subj': `canvasEditorMenu-${id}Group`, })), - ...ungroupedFactories.map(getEmbeddableFactoryMenuItem), - ...promotedVisTypes.map(getVisTypeMenuItem), ], }, ...Object.values(factoryGroupMap).map( diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx index 4e50d56f3cb7..fd644903ac25 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx @@ -7,7 +7,12 @@ import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { useLocation } from 'react-router-dom'; -import { BaseVisType, VisGroups, VisTypeAlias } from '@kbn/visualizations-plugin/public'; +import { + VisGroups, + type BaseVisType, + type VisTypeAlias, + type VisParams, +} from '@kbn/visualizations-plugin/public'; import { EmbeddableFactory, EmbeddableFactoryDefinition, @@ -201,13 +206,17 @@ export const EditorMenu: FC = ({ addElement }) => { .map(({ factory }) => factory); const promotedVisTypes = getVisTypesByGroup(VisGroups.PROMOTED); + const legacyVisTypes = getVisTypesByGroup(VisGroups.LEGACY); return ( >).concat( + promotedVisTypes, + legacyVisTypes + )} factories={factories} addPanelActions={addPanelActions} visTypeAliases={visTypeAliases} diff --git a/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_inputs.test.ts b/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_inputs.test.ts index 42fc4dda938a..40cda7583a3c 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_inputs.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_inputs.test.ts @@ -877,4 +877,51 @@ describe('Fleet - storedPackagePoliciesToAgentInputs', () => { }, ]); }); + + it('does not include processor add_fields when global tags array is empty', async () => { + expect( + await storedPackagePoliciesToAgentInputs( + [ + { + ...mockPackagePolicy, + package: { + name: 'mock_package', + title: 'Mock package', + version: '0.0.0', + }, + inputs: [ + { + ...mockInput, + compiled_input: { + inputVar: 'input-value', + }, + streams: [], + }, + ], + }, + ], + packageInfoCache, + undefined, + undefined, + [] + ) + ).toEqual([ + { + id: 'test-logs-some-uuid', + name: 'mock_package-policy', + package_policy_id: 'some-uuid', + revision: 1, + type: 'test-logs', + data_stream: { namespace: 'default' }, + use_output: 'default', + meta: { + package: { + name: 'mock_package', + version: '0.0.0', + }, + }, + inputVar: 'input-value', + }, + ]); + }); }); diff --git a/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_inputs.ts b/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_inputs.ts index 0a1643f293e5..d7f0c70a0786 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_inputs.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_inputs.ts @@ -146,7 +146,10 @@ export const storedPackagePoliciesToAgentInputs = async ( ): Promise => { const fullInputs: FullAgentPolicyInput[] = []; - const addFields = globalDataTags ? globalDataTagsToAddFields(globalDataTags) : undefined; + const addFields = + globalDataTags && globalDataTags.length > 0 + ? globalDataTagsToAddFields(globalDataTags) + : undefined; for (const packagePolicy of packagePolicies) { if (!isPolicyEnabled(packagePolicy)) { diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index cd48b0b33782..ba615678c334 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -46,6 +46,7 @@ import { ACTION_VISUALIZE_FIELD, VISUALIZE_FIELD_TRIGGER, VisualizeFieldContext, + ADD_PANEL_TRIGGER, } from '@kbn/ui-actions-plugin/public'; import { VISUALIZE_EDITOR_TRIGGER, @@ -648,7 +649,7 @@ export class LensPlugin { // Displays the add ESQL panel in the dashboard add Panel menu const createESQLPanelAction = new CreateESQLPanelAction(startDependencies, core); - startDependencies.uiActions.addTriggerAction('ADD_PANEL_TRIGGER', createESQLPanelAction); + startDependencies.uiActions.addTriggerAction(ADD_PANEL_TRIGGER, createESQLPanelAction); const discoverLocator = startDependencies.share?.url.locators.get('DISCOVER_APP_LOCATOR'); if (discoverLocator) { diff --git a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/create_action.tsx b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/create_action.tsx index 07301f239413..f1d58f9702fb 100644 --- a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/create_action.tsx +++ b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/create_action.tsx @@ -9,6 +9,7 @@ import type { CoreStart } from '@kbn/core/public'; import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; import { EmbeddableApiContext } from '@kbn/presentation-publishing'; import { apiIsPresentationContainer } from '@kbn/presentation-containers'; +import { COMMON_VISUALIZATION_GROUPING } from '@kbn/visualizations-plugin/public'; import type { LensPluginStartDependencies } from '../../plugin'; const ACTION_CREATE_ESQL_CHART = 'ACTION_CREATE_ESQL_CHART'; @@ -20,6 +21,8 @@ export class CreateESQLPanelAction implements Action { public id = ACTION_CREATE_ESQL_CHART; public order = 50; + public grouping = COMMON_VISUALIZATION_GROUPING; + constructor( protected readonly startDependencies: LensPluginStartDependencies, protected readonly core: CoreStart diff --git a/x-pack/plugins/lens/public/vis_type_alias.ts b/x-pack/plugins/lens/public/vis_type_alias.ts index e20b60a11c57..90d1df663d3e 100644 --- a/x-pack/plugins/lens/public/vis_type_alias.ts +++ b/x-pack/plugins/lens/public/vis_type_alias.ts @@ -27,6 +27,7 @@ export const getLensAliasConfig = (): VisTypeAlias => ({ note: i18n.translate('xpack.lens.visTypeAlias.note', { defaultMessage: 'Recommended for most users.', }), + order: 60, icon: 'lensApp', stage: 'production', appExtensions: { diff --git a/x-pack/plugins/maps/public/maps_vis_type_alias.ts b/x-pack/plugins/maps/public/maps_vis_type_alias.ts index 48a96a2d0f98..dc7c8cc8dc7b 100644 --- a/x-pack/plugins/maps/public/maps_vis_type_alias.ts +++ b/x-pack/plugins/maps/public/maps_vis_type_alias.ts @@ -33,6 +33,7 @@ export function getMapsVisTypeAlias() { description: appDescription, icon: APP_ICON, stage: 'production' as VisualizationStage, + order: 40, appExtensions: { visualizations: { docTypes: [MAP_SAVED_OBJECT_TYPE], diff --git a/x-pack/plugins/ml/common/constants/app.ts b/x-pack/plugins/ml/common/constants/app.ts index d74e1cb30c76..dd41353184fd 100644 --- a/x-pack/plugins/ml/common/constants/app.ts +++ b/x-pack/plugins/ml/common/constants/app.ts @@ -11,7 +11,7 @@ export const PLUGIN_ID = 'ml'; export const PLUGIN_ICON = 'machineLearningApp'; export const PLUGIN_ICON_SOLUTION = 'logoKibana'; export const ML_APP_NAME = i18n.translate('xpack.ml.navMenu.mlAppNameText', { - defaultMessage: 'Machine Learning', + defaultMessage: 'Machine Learning and Analytics', }); export const ML_APP_ROUTE = '/app/ml'; export const ML_INTERNAL_BASE_PATH = '/internal/ml'; diff --git a/x-pack/plugins/ml/public/ui_actions/create_anomaly_chart.tsx b/x-pack/plugins/ml/public/ui_actions/create_anomaly_chart.tsx index c4b88bdd4330..28b7b413827a 100644 --- a/x-pack/plugins/ml/public/ui_actions/create_anomaly_chart.tsx +++ b/x-pack/plugins/ml/public/ui_actions/create_anomaly_chart.tsx @@ -41,6 +41,10 @@ export function createAddAnomalyChartsPanelAction( getIconType: () => PLUGIN_ICON, }, ], + order: 30, + getIconType(): string { + return 'visLine'; + }, getDisplayName: () => i18n.translate('xpack.ml.components.mlAnomalyExplorerEmbeddable.displayName', { defaultMessage: 'Anomaly chart', diff --git a/x-pack/plugins/ml/public/ui_actions/create_single_metric_viewer.tsx b/x-pack/plugins/ml/public/ui_actions/create_single_metric_viewer.tsx index 55b3bdf44663..3b1e06f62538 100644 --- a/x-pack/plugins/ml/public/ui_actions/create_single_metric_viewer.tsx +++ b/x-pack/plugins/ml/public/ui_actions/create_single_metric_viewer.tsx @@ -42,6 +42,8 @@ export function createAddSingleMetricViewerPanelAction( getIconType: () => PLUGIN_ICON, }, ], + order: 20, + getIconType: () => 'visLine', getDisplayName: () => i18n.translate('xpack.ml.components.singleMetricViewerEmbeddable.displayName', { defaultMessage: 'Single metric viewer', diff --git a/x-pack/plugins/ml/public/ui_actions/create_swim_lane.tsx b/x-pack/plugins/ml/public/ui_actions/create_swim_lane.tsx index 388b32148847..b0f47eb4e1dc 100644 --- a/x-pack/plugins/ml/public/ui_actions/create_swim_lane.tsx +++ b/x-pack/plugins/ml/public/ui_actions/create_swim_lane.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import React from 'react'; import { i18n } from '@kbn/i18n'; import type { PresentationContainer } from '@kbn/presentation-containers'; import type { EmbeddableApiContext } from '@kbn/presentation-publishing'; @@ -41,6 +42,28 @@ export function createAddSwimlanePanelAction( getIconType: () => PLUGIN_ICON, }, ], + order: 40, + // @ts-expect-error getIconType is typed as string, but EuiIcon accepts ReactComponent for custom icons. + // See https://github.com/elastic/kibana/issues/184643 + getIconType: () => (iconProps) => + ( + + + + + + + + ), getDisplayName: () => i18n.translate('xpack.ml.components.jobAnomalyScoreEmbeddable.displayName', { defaultMessage: 'Anomaly swim lane', diff --git a/x-pack/plugins/ml/public/ui_actions/index.ts b/x-pack/plugins/ml/public/ui_actions/index.ts index 66dd1f0f06f3..1b650d331d00 100644 --- a/x-pack/plugins/ml/public/ui_actions/index.ts +++ b/x-pack/plugins/ml/public/ui_actions/index.ts @@ -8,7 +8,7 @@ import type { CoreSetup } from '@kbn/core/public'; import { CONTEXT_MENU_TRIGGER } from '@kbn/embeddable-plugin/public'; import { CREATE_PATTERN_ANALYSIS_TO_ML_AD_JOB_TRIGGER } from '@kbn/ml-ui-actions'; -import type { UiActionsSetup } from '@kbn/ui-actions-plugin/public'; +import { type UiActionsSetup, ADD_PANEL_TRIGGER } from '@kbn/ui-actions-plugin/public'; import type { MlPluginStart, MlStartDependencies } from '../plugin'; import { createApplyEntityFieldFiltersAction } from './apply_entity_filters_action'; import { createApplyInfluencerFiltersAction } from './apply_influencer_filters_action'; @@ -67,9 +67,9 @@ export function registerMlUiActions( uiActions.registerAction(addAnomalyChartsPanelAction); // Assign triggers - uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addSingleMetricViewerPanelAction); - uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addSwimlanePanelAction); - uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addAnomalyChartsPanelAction); + uiActions.addTriggerAction(ADD_PANEL_TRIGGER, addSingleMetricViewerPanelAction); + uiActions.addTriggerAction(ADD_PANEL_TRIGGER, addSwimlanePanelAction); + uiActions.addTriggerAction(ADD_PANEL_TRIGGER, addAnomalyChartsPanelAction); uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, openInExplorerAction); uiActions.attachAction(CONTEXT_MENU_TRIGGER, openInSingleMetricViewerAction.id); diff --git a/x-pack/plugins/observability_solution/infra/public/plugin.ts b/x-pack/plugins/observability_solution/infra/public/plugin.ts index 20f819b7eb34..f323d7adb629 100644 --- a/x-pack/plugins/observability_solution/infra/public/plugin.ts +++ b/x-pack/plugins/observability_solution/infra/public/plugin.ts @@ -21,7 +21,8 @@ import { BehaviorSubject, combineLatest, from } from 'rxjs'; import { map } from 'rxjs'; import type { EmbeddableApiContext } from '@kbn/presentation-publishing'; import { apiCanAddNewPanel } from '@kbn/presentation-containers'; -import { IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; +import { IncompatibleActionError, ADD_PANEL_TRIGGER } from '@kbn/ui-actions-plugin/public'; +import { COMMON_EMBEDDABLE_GROUPING } from '@kbn/embeddable-plugin/public'; import type { InfraPublicConfig } from '../common/plugin_config_types'; import { createInventoryMetricRuleType } from './alerting/inventory'; import { createLogThresholdRuleType } from './alerting/log_threshold'; @@ -400,6 +401,8 @@ export class Plugin implements InfraClientPluginClass { plugins.uiActions.registerAction({ id: ADD_LOG_STREAM_ACTION_ID, + grouping: [COMMON_EMBEDDABLE_GROUPING.legacy], + order: 30, getDisplayName: () => i18n.translate('xpack.infra.logStreamEmbeddable.displayName', { defaultMessage: 'Log stream', @@ -427,7 +430,7 @@ export class Plugin implements InfraClientPluginClass { ); }, }); - plugins.uiActions.attachAction('ADD_PANEL_TRIGGER', ADD_LOG_STREAM_ACTION_ID); + plugins.uiActions.attachAction(ADD_PANEL_TRIGGER, ADD_LOG_STREAM_ACTION_ID); const startContract: InfraClientStartExports = { inventoryViews, diff --git a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/common/constants.ts b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/common/constants.ts index 94cf6dd80fa5..b263c043d7ac 100644 --- a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/common/constants.ts +++ b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/common/constants.ts @@ -8,7 +8,7 @@ export const COMMON_SLO_GROUPING = [ { id: 'slos', - getDisplayName: () => 'SLOs', + getDisplayName: () => 'Observability', getIconType: () => { return 'visGauge'; }, diff --git a/x-pack/plugins/observability_solution/slo/public/plugin.ts b/x-pack/plugins/observability_solution/slo/public/plugin.ts index 5bdd830830fd..5748a55c2148 100644 --- a/x-pack/plugins/observability_solution/slo/public/plugin.ts +++ b/x-pack/plugins/observability_solution/slo/public/plugin.ts @@ -138,7 +138,8 @@ export class SloPlugin const registerAsyncSloUiActions = async () => { if (pluginsSetup.uiActions) { const { registerSloUiActions } = await import('./ui_actions'); - registerSloUiActions(pluginsSetup.uiActions, coreSetup); + + registerSloUiActions(coreSetup, pluginsSetup, pluginsStart); } }; registerAsyncSloUiActions(); diff --git a/x-pack/plugins/observability_solution/slo/public/ui_actions/create_alerts_panel_action.tsx b/x-pack/plugins/observability_solution/slo/public/ui_actions/create_alerts_panel_action.tsx index b365881bf915..63926aa24cd8 100644 --- a/x-pack/plugins/observability_solution/slo/public/ui_actions/create_alerts_panel_action.tsx +++ b/x-pack/plugins/observability_solution/slo/public/ui_actions/create_alerts_panel_action.tsx @@ -26,6 +26,7 @@ export function createAddAlertsPanelAction( id: ADD_SLO_ALERTS_ACTION_ID, grouping: COMMON_SLO_GROUPING, getIconType: () => 'alert', + order: 20, isCompatible: async ({ embeddable }) => { return apiIsPresentationContainer(embeddable); }, diff --git a/x-pack/plugins/observability_solution/slo/public/ui_actions/create_error_budget_action.tsx b/x-pack/plugins/observability_solution/slo/public/ui_actions/create_error_budget_action.tsx index c619252bc6eb..8d311bfdce70 100644 --- a/x-pack/plugins/observability_solution/slo/public/ui_actions/create_error_budget_action.tsx +++ b/x-pack/plugins/observability_solution/slo/public/ui_actions/create_error_budget_action.tsx @@ -24,6 +24,7 @@ export function createAddErrorBudgetPanelAction( return { id: ADD_SLO_ERROR_BUDGET_ACTION_ID, grouping: COMMON_SLO_GROUPING, + order: 10, getIconType: () => 'visLine', isCompatible: async ({ embeddable }) => { return apiIsPresentationContainer(embeddable); diff --git a/x-pack/plugins/observability_solution/slo/public/ui_actions/create_overview_panel_action.tsx b/x-pack/plugins/observability_solution/slo/public/ui_actions/create_overview_panel_action.tsx index edb7b129927b..49d2d269d4cd 100644 --- a/x-pack/plugins/observability_solution/slo/public/ui_actions/create_overview_panel_action.tsx +++ b/x-pack/plugins/observability_solution/slo/public/ui_actions/create_overview_panel_action.tsx @@ -25,6 +25,7 @@ export function createOverviewPanelAction( return { id: ADD_SLO_OVERVIEW_ACTION_ID, grouping: COMMON_SLO_GROUPING, + order: 30, getIconType: () => 'visGauge', isCompatible: async ({ embeddable }) => { return apiIsPresentationContainer(embeddable); diff --git a/x-pack/plugins/observability_solution/slo/public/ui_actions/index.ts b/x-pack/plugins/observability_solution/slo/public/ui_actions/index.ts index 61c1569f1a9d..95c1f19a8842 100644 --- a/x-pack/plugins/observability_solution/slo/public/ui_actions/index.ts +++ b/x-pack/plugins/observability_solution/slo/public/ui_actions/index.ts @@ -5,24 +5,31 @@ * 2.0. */ -import type { UiActionsSetup } from '@kbn/ui-actions-plugin/public'; +import { ADD_PANEL_TRIGGER } from '@kbn/ui-actions-plugin/public'; import type { CoreSetup } from '@kbn/core/public'; import { createOverviewPanelAction } from './create_overview_panel_action'; import { createAddErrorBudgetPanelAction } from './create_error_budget_action'; import { createAddAlertsPanelAction } from './create_alerts_panel_action'; -import { SloPublicPluginsStart, SloPublicStart } from '..'; +import { SloPublicPluginsStart, SloPublicStart, SloPublicPluginsSetup } from '..'; export function registerSloUiActions( - uiActions: UiActionsSetup, - core: CoreSetup + core: CoreSetup, + pluginsSetup: SloPublicPluginsSetup, + pluginsStart: SloPublicPluginsStart ) { + const { uiActions } = pluginsSetup; + const { serverless, cloud } = pluginsStart; + // Initialize actions const addOverviewPanelAction = createOverviewPanelAction(core.getStartServices); const addErrorBudgetPanelAction = createAddErrorBudgetPanelAction(core.getStartServices); const addAlertsPanelAction = createAddAlertsPanelAction(core.getStartServices); // Assign triggers - uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addOverviewPanelAction); - uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addErrorBudgetPanelAction); - uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addAlertsPanelAction); + // Only register these actions in stateful kibana, and the serverless observability project + if (Boolean((serverless && cloud?.serverless.projectType === 'observability') || !serverless)) { + uiActions.addTriggerAction(ADD_PANEL_TRIGGER, addOverviewPanelAction); + uiActions.addTriggerAction(ADD_PANEL_TRIGGER, addErrorBudgetPanelAction); + uiActions.addTriggerAction(ADD_PANEL_TRIGGER, addAlertsPanelAction); + } } diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 1e2d0b57533f..ae1c41e94708 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -1192,8 +1192,6 @@ "dashboard.actions.downloadOptionsUnsavedFilename": "sans titre", "dashboard.actions.toggleExpandPanelMenuItem.expandedDisplayName": "Minimiser", "dashboard.actions.toggleExpandPanelMenuItem.notExpandedDisplayName": "Maximiser le panneau", - "dashboard.addPanelMenuTrigger.description": "Une nouvelle action apparaîtra dans le menu Ajouter un panneau du tableau de bord", - "dashboard.addPanelMenuTrigger.title": "Menu Ajouter un panneau", "dashboard.appLeaveConfirmModal.cancelButtonLabel": "Annuler", "dashboard.appLeaveConfirmModal.unsavedChangesSubtitle": "Quitter Dashboard sans enregistrer ?", "dashboard.appLeaveConfirmModal.unsavedChangesTitle": "Modifications non enregistrées", @@ -1215,7 +1213,6 @@ "dashboard.editingToolbar.controlsButtonTitle": "Contrôles", "dashboard.editingToolbar.editControlGroupButtonTitle": "Paramètres", "dashboard.editingToolbar.onlyOneTimeSliderControlMsg": "Le groupe de contrôle contient déjà un contrôle de curseur temporel.", - "dashboard.editorMenu.aggBasedGroupTitle": "Basé sur une agrégation", "dashboard.editorMenu.deprecatedTag": "Déclassé", "dashboard.embeddableApi.showSettings.flyout.applyButtonTitle": "Appliquer", "dashboard.embeddableApi.showSettings.flyout.cancelButtonTitle": "Annuler", @@ -44235,6 +44232,8 @@ "uiActions.errors.incompatibleAction": "Action non compatible", "uiActions.triggers.rowClickkDescription": "Un clic sur une ligne de tableau", "uiActions.triggers.rowClickTitle": "Clic sur ligne de tableau", + "uiActions.triggers.dashboard.addPanelMenu.description": "Une nouvelle action apparaîtra dans le menu Ajouter un panneau du tableau de bord", + "uiActions.triggers.dashboard.addPanelMenu.title": "Menu Ajouter un panneau", "unsavedChangesBadge.contextMenu.openButton": "Afficher les actions disponibles", "unsavedChangesBadge.contextMenu.revertChangesButton": "Restaurer les modifications", "unsavedChangesBadge.contextMenu.revertingChangesButtonStatus": "Annuler les modifications", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 305baf56ccc5..9635294d1616 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -1192,8 +1192,6 @@ "dashboard.actions.downloadOptionsUnsavedFilename": "無題", "dashboard.actions.toggleExpandPanelMenuItem.expandedDisplayName": "最小化", "dashboard.actions.toggleExpandPanelMenuItem.notExpandedDisplayName": "パネルを最大化", - "dashboard.addPanelMenuTrigger.description": "新しいアクションは、ダッシュボードのパネルの追加メニューに表示されます", - "dashboard.addPanelMenuTrigger.title": "パネルの追加メニュー", "dashboard.appLeaveConfirmModal.cancelButtonLabel": "キャンセル", "dashboard.appLeaveConfirmModal.unsavedChangesSubtitle": "作業を保存せずにダッシュボードから移動しますか?", "dashboard.appLeaveConfirmModal.unsavedChangesTitle": "保存されていない変更", @@ -1215,7 +1213,6 @@ "dashboard.editingToolbar.controlsButtonTitle": "コントロール", "dashboard.editingToolbar.editControlGroupButtonTitle": "設定", "dashboard.editingToolbar.onlyOneTimeSliderControlMsg": "コントロールグループには、すでに時間スライダーコントロールがあります。", - "dashboard.editorMenu.aggBasedGroupTitle": "アグリゲーションに基づく", "dashboard.editorMenu.deprecatedTag": "非推奨", "dashboard.embeddableApi.showSettings.flyout.applyButtonTitle": "適用", "dashboard.embeddableApi.showSettings.flyout.cancelButtonTitle": "キャンセル", @@ -44209,6 +44206,8 @@ "uiActions.actionPanel.more": "詳細", "uiActions.actionPanel.title": "オプション", "uiActions.errors.incompatibleAction": "操作に互換性がありません", + "uiActions.triggers.dashboard.addPanelMenu.description": "新しいアクションは、ダッシュボードのパネルの追加メニューに表示されます", + "uiActions.triggers.dashboard.addPanelMenu.title": "パネルの追加メニュー", "uiActions.triggers.rowClickkDescription": "テーブル行をクリック", "uiActions.triggers.rowClickTitle": "テーブル行クリック", "unsavedChangesBadge.contextMenu.openButton": "使用可能なアクションを表示", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 49bc0b78a863..f2e659ca333b 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -1194,8 +1194,6 @@ "dashboard.actions.downloadOptionsUnsavedFilename": "未命名", "dashboard.actions.toggleExpandPanelMenuItem.expandedDisplayName": "最小化", "dashboard.actions.toggleExpandPanelMenuItem.notExpandedDisplayName": "最大化面板", - "dashboard.addPanelMenuTrigger.description": "一个新操作将在仪表板的添加面板菜单中显示出来", - "dashboard.addPanelMenuTrigger.title": "添加面板菜单", "dashboard.appLeaveConfirmModal.cancelButtonLabel": "取消", "dashboard.appLeaveConfirmModal.unsavedChangesSubtitle": "离开有未保存工作的仪表板?", "dashboard.appLeaveConfirmModal.unsavedChangesTitle": "未保存的更改", @@ -1217,7 +1215,6 @@ "dashboard.editingToolbar.controlsButtonTitle": "控件", "dashboard.editingToolbar.editControlGroupButtonTitle": "设置", "dashboard.editingToolbar.onlyOneTimeSliderControlMsg": "控件组已包含时间滑块控件。", - "dashboard.editorMenu.aggBasedGroupTitle": "基于聚合", "dashboard.editorMenu.deprecatedTag": "(已过时)", "dashboard.embeddableApi.showSettings.flyout.applyButtonTitle": "应用", "dashboard.embeddableApi.showSettings.flyout.cancelButtonTitle": "取消", @@ -44257,6 +44254,8 @@ "uiActions.actionPanel.more": "更多", "uiActions.actionPanel.title": "选项", "uiActions.errors.incompatibleAction": "操作不兼容", + "uiActions.triggers.dashboard.addPanelMenu.description": "一个新操作将在仪表板的添加面板菜单中显示出来", + "uiActions.triggers.dashboard.addPanelMenu.title": "添加面板菜单", "uiActions.triggers.rowClickkDescription": "表格行的单击", "uiActions.triggers.rowClickTitle": "表格行单击", "unsavedChangesBadge.contextMenu.openButton": "查看可用操作", diff --git a/x-pack/test/functional/apps/dashboard/group2/dashboard_maps_by_value.ts b/x-pack/test/functional/apps/dashboard/group2/dashboard_maps_by_value.ts index 61fc8319cb90..6cfdc7356a4b 100644 --- a/x-pack/test/functional/apps/dashboard/group2/dashboard_maps_by_value.ts +++ b/x-pack/test/functional/apps/dashboard/group2/dashboard_maps_by_value.ts @@ -33,6 +33,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.dashboard.switchToEditMode(); } await dashboardAddPanel.clickEditorMenuButton(); + await testSubjects.setValue('dashboardPanelSelectionFlyout__searchInput', 'maps'); await dashboardAddPanel.clickVisType('maps'); await PageObjects.maps.clickSaveAndReturnButton(); } diff --git a/x-pack/test/functional/apps/slo/embeddables/overview_embeddable.ts b/x-pack/test/functional/apps/slo/embeddables/overview_embeddable.ts index a9671b6e350d..5a531d6cf5bb 100644 --- a/x-pack/test/functional/apps/slo/embeddables/overview_embeddable.ts +++ b/x-pack/test/functional/apps/slo/embeddables/overview_embeddable.ts @@ -39,7 +39,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('Single SLO', function () { it('should open SLO configuration flyout', async () => { await dashboardAddPanel.clickEditorMenuButton(); - await dashboardAddPanel.clickEmbeddableFactoryGroupButton('slos'); + await dashboardAddPanel.verifyEmbeddableFactoryGroupExists('slos'); await dashboardAddPanel.clickAddNewPanelFromUIActionLink('SLO Overview'); await sloUi.common.assertSloOverviewConfigurationExists(); }); @@ -67,7 +67,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('Group of SLOs', function () { it('can select Group Overview mode in the Flyout configuration', async () => { await dashboardAddPanel.clickEditorMenuButton(); - await dashboardAddPanel.clickEmbeddableFactoryGroupButton('slos'); + await dashboardAddPanel.verifyEmbeddableFactoryGroupExists('slos'); await dashboardAddPanel.clickAddNewPanelFromUIActionLink('SLO Overview'); await sloUi.common.clickOverviewMode(); await sloUi.common.assertSloConfigurationGroupOverviewModeIsSelected(); diff --git a/x-pack/test/functional/services/ml/dashboard_embeddables.ts b/x-pack/test/functional/services/ml/dashboard_embeddables.ts index 9a5428276479..b22622ead61d 100644 --- a/x-pack/test/functional/services/ml/dashboard_embeddables.ts +++ b/x-pack/test/functional/services/ml/dashboard_embeddables.ts @@ -124,9 +124,9 @@ export function MachineLearningDashboardEmbeddablesProvider( }; await retry.tryForTime(60 * 1000, async () => { await dashboardAddPanel.clickEditorMenuButton(); - await testSubjects.existOrFail('dashboardEditorContextMenu', { timeout: 2000 }); + await testSubjects.existOrFail('dashboardPanelSelectionFlyout', { timeout: 2000 }); - await dashboardAddPanel.clickEmbeddableFactoryGroupButton('ml'); + await dashboardAddPanel.verifyEmbeddableFactoryGroupExists('ml'); await dashboardAddPanel.clickAddNewPanelFromUIActionLink(name[mlEmbeddableType]); await testSubjects.existOrFail('mlAnomalyJobSelectionControls', { timeout: 2000 });