From f040a8321bed4b16e0592b26724cd447777fb849 Mon Sep 17 00:00:00 2001 From: Ben Sully Date: Tue, 12 Dec 2023 08:52:09 +0000 Subject: [PATCH] Plugins: Add Command Palette extension point (#78098) Co-authored-by: Marcus Andersson --- packages/grafana-data/src/types/index.ts | 1 + .../src/types/pluginExtensions.ts | 3 +++ .../components/AppChrome/AppChrome.test.tsx | 5 +++++ .../actions/dashboardActions.test.ts | 2 +- .../actions/extensionActions.ts | 22 +++++++++++++++++++ .../commandPalette/actions/staticActions.ts | 5 ++++- public/app/features/commandPalette/values.ts | 3 ++- .../containers/DashboardPage.test.tsx | 5 +++++ 8 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 public/app/features/commandPalette/actions/extensionActions.ts diff --git a/packages/grafana-data/src/types/index.ts b/packages/grafana-data/src/types/index.ts index 96f3c8f7d39..5ee1e8971a4 100644 --- a/packages/grafana-data/src/types/index.ts +++ b/packages/grafana-data/src/types/index.ts @@ -63,5 +63,6 @@ export { type PluginExtensionEventHelpers, type PluginExtensionPanelContext, type PluginExtensionDataSourceConfigContext, + type PluginExtensionCommandPaletteContext, type PluginExtensionOpenModalOptions, } from './pluginExtensions'; diff --git a/packages/grafana-data/src/types/pluginExtensions.ts b/packages/grafana-data/src/types/pluginExtensions.ts index a29337bbe8d..68fa3c7d1d8 100644 --- a/packages/grafana-data/src/types/pluginExtensions.ts +++ b/packages/grafana-data/src/types/pluginExtensions.ts @@ -118,6 +118,7 @@ export type PluginExtensionEventHelpers = { // Extension Points available in core Grafana export enum PluginExtensionPoints { AlertInstanceAction = 'grafana/alerting/instance/action', + CommandPalette = 'grafana/commandpalette/action', DashboardPanelMenu = 'grafana/dashboard/panel/menu', DataSourceConfig = 'grafana/datasources/config', ExploreToolbarAction = 'grafana/explore/toolbar/action', @@ -154,6 +155,8 @@ export type PluginExtensionDataSourceConfigContext void; }; +export type PluginExtensionCommandPaletteContext = {}; + type Dashboard = { uid: string; title: string; diff --git a/public/app/core/components/AppChrome/AppChrome.test.tsx b/public/app/core/components/AppChrome/AppChrome.test.tsx index a4c4a40e43b..a846714075c 100644 --- a/public/app/core/components/AppChrome/AppChrome.test.tsx +++ b/public/app/core/components/AppChrome/AppChrome.test.tsx @@ -14,6 +14,11 @@ import { Page } from '../Page/Page'; import { AppChrome } from './AppChrome'; +jest.mock('@grafana/runtime', () => ({ + ...jest.requireActual('@grafana/runtime'), + getPluginLinkExtensions: jest.fn().mockReturnValue({ extensions: [] }), +})); + const pageNav: NavModelItem = { text: 'pageNav title', children: [ diff --git a/public/app/features/commandPalette/actions/dashboardActions.test.ts b/public/app/features/commandPalette/actions/dashboardActions.test.ts index cb96ea07581..efea227dc10 100644 --- a/public/app/features/commandPalette/actions/dashboardActions.test.ts +++ b/public/app/features/commandPalette/actions/dashboardActions.test.ts @@ -87,7 +87,7 @@ describe('dashboardActions', () => { { id: 'recent-dashboards/my-dashboard-1', name: 'My dashboard 1', - priority: 5, + priority: 6, section: 'Recent dashboards', url: '/my-dashboard-1', }, diff --git a/public/app/features/commandPalette/actions/extensionActions.ts b/public/app/features/commandPalette/actions/extensionActions.ts new file mode 100644 index 00000000000..c3df191e021 --- /dev/null +++ b/public/app/features/commandPalette/actions/extensionActions.ts @@ -0,0 +1,22 @@ +import { PluginExtensionCommandPaletteContext, PluginExtensionPoints } from '@grafana/data'; +import { getPluginLinkExtensions } from '@grafana/runtime'; + +import { CommandPaletteAction } from '../types'; +import { EXTENSIONS_PRIORITY } from '../values'; + +export default function getExtensionActions(): CommandPaletteAction[] { + const context: PluginExtensionCommandPaletteContext = {}; + const { extensions } = getPluginLinkExtensions({ + extensionPointId: PluginExtensionPoints.CommandPalette, + context, + limitPerPlugin: 3, + }); + return extensions.map((extension) => ({ + section: extension.category ?? 'Extensions', + priority: EXTENSIONS_PRIORITY, + id: extension.id, + name: extension.title, + target: extension.path, + perform: () => extension.onClick && extension.onClick(), + })); +} diff --git a/public/app/features/commandPalette/actions/staticActions.ts b/public/app/features/commandPalette/actions/staticActions.ts index d7dd4abd953..9d4e9dbd4ff 100644 --- a/public/app/features/commandPalette/actions/staticActions.ts +++ b/public/app/features/commandPalette/actions/staticActions.ts @@ -6,6 +6,8 @@ import { changeTheme } from 'app/core/services/theme'; import { CommandPaletteAction } from '../types'; import { ACTIONS_PRIORITY, DEFAULT_PRIORITY, PREFERENCES_PRIORITY } from '../values'; +import getExtensionActions from './extensionActions'; + // TODO: Clean this once ID is mandatory on nav items function idForNavItem(navItem: NavModelItem) { return 'navModel.' + navItem.id ?? navItem.url ?? navItem.text ?? navItem.subTitle; @@ -84,7 +86,8 @@ export default (navBarTree: NavModelItem[]): CommandPaletteAction[] => { }, ]; + const extensionActions = getExtensionActions(); const navBarActions = navTreeToActions(navBarTree); - return [...globalActions, ...navBarActions]; + return [...globalActions, ...extensionActions, ...navBarActions]; }; diff --git a/public/app/features/commandPalette/values.ts b/public/app/features/commandPalette/values.ts index 48c30d0a93c..b0af672ea43 100644 --- a/public/app/features/commandPalette/values.ts +++ b/public/app/features/commandPalette/values.ts @@ -1,4 +1,5 @@ -export const RECENT_DASHBOARDS_PRORITY = 5; +export const RECENT_DASHBOARDS_PRORITY = 6; +export const EXTENSIONS_PRIORITY = 5; export const ACTIONS_PRIORITY = 4; export const DEFAULT_PRIORITY = 3; export const PREFERENCES_PRIORITY = 2; diff --git a/public/app/features/dashboard/containers/DashboardPage.test.tsx b/public/app/features/dashboard/containers/DashboardPage.test.tsx index 0fe6cfef4a9..4ced917fe73 100644 --- a/public/app/features/dashboard/containers/DashboardPage.test.tsx +++ b/public/app/features/dashboard/containers/DashboardPage.test.tsx @@ -66,6 +66,11 @@ jest.mock('app/core/core', () => ({ }, })); +jest.mock('@grafana/runtime', () => ({ + ...jest.requireActual('@grafana/runtime'), + getPluginLinkExtensions: jest.fn().mockReturnValue({ extensions: [] }), +})); + jest.mock('react-virtualized-auto-sizer', () => { // The size of the children need to be small enough to be outside the view. // So it does not trigger the query to be run by the PanelQueryRunner.