diff --git a/public/app/features/commandPalette/ResultItem.tsx b/public/app/features/commandPalette/ResultItem.tsx
index 6f817fac456..036d70dffb4 100644
--- a/public/app/features/commandPalette/ResultItem.tsx
+++ b/public/app/features/commandPalette/ResultItem.tsx
@@ -34,6 +34,13 @@ export const ResultItem = React.forwardRef(
const theme = useTheme2();
const styles = getResultItemStyles(theme, active);
+ let name = action.name;
+
+ // TODO: does this needs adjusting for i18n?
+ if (action.children && !action.command?.perform && !name.endsWith('...')) {
+ name += '...';
+ }
+
return (
@@ -47,7 +54,7 @@ export const ResultItem = React.forwardRef(
›
))}
- {action.name}
+ {name}
{action.subtitle && {action.subtitle}}
@@ -94,9 +101,9 @@ const getResultItemStyles = (theme: GrafanaTheme2, isActive: boolean) => {
}),
actionContainer: css({
display: 'flex',
- gap: theme.spacing(2),
- alignitems: 'center',
- fontsize: theme.typography.fontSize,
+ gap: theme.spacing(1),
+ alignItems: 'center',
+ fontSize: theme.typography.fontSize,
}),
textContainer: css({
display: 'flex',
@@ -106,7 +113,7 @@ const getResultItemStyles = (theme: GrafanaTheme2, isActive: boolean) => {
padding: theme.spacing(0, 1),
background: shortcutBackgroundColor,
borderRadius: theme.shape.borderRadius(),
- fontsize: theme.typography.fontSize,
+ fontSize: theme.typography.fontSize,
}),
breadcrumbAncestor: css({
opacity: 0.5,
diff --git a/public/app/features/commandPalette/actions/alerting.static.actions.ts b/public/app/features/commandPalette/actions/alerting.static.actions.ts
deleted file mode 100644
index d4ceea7e138..00000000000
--- a/public/app/features/commandPalette/actions/alerting.static.actions.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-import { Priority } from 'kbar';
-
-import { locationService } from '@grafana/runtime';
-
-import { NavBarActions } from './global.static.actions';
-
-// Grafana Alerting and alerting sub navigation items
-const alertingCommandPaletteStaticActions: NavBarActions[] = [
- {
- url: '/alerting/list',
- actions: [
- {
- id: 'go/alerting',
- name: 'Go to alerting',
- keywords: 'alerting navigate',
- perform: () => locationService.push('/alerting'),
- section: 'Navigation',
- priority: Priority.NORMAL,
- },
- ],
- },
- {
- url: '/alerting/list',
- actions: [
- {
- id: 'go/alerting/rules',
- name: 'Go to alert rules',
- keywords: 'alerting navigate rules',
- perform: () => locationService.push('/alerting/list'),
- section: 'Navigation',
- parent: 'go/alerting',
- },
- ],
- },
- {
- url: '/alerting/notifications',
- actions: [
- {
- id: 'go/alerting/contact-points',
- name: 'Go to contact points',
- keywords: 'alerting navigate contact-points',
- perform: () => locationService.push('/alerting/notifications'),
- parent: 'go/alerting',
- },
- ],
- },
- {
- url: '/alerting/routes',
- actions: [
- {
- id: 'go/alerting/notification-policies',
- name: 'Go to notification policies',
- keywords: 'alerting navigate notification-policies',
- perform: () => locationService.push('/alerting/routes'),
- parent: 'go/alerting',
- },
- ],
- },
- {
- url: '/alerting/silences',
- actions: [
- {
- id: 'go/alerting/silences',
- name: 'Go to silences',
- keywords: 'alerting navigate silences',
- perform: () => locationService.push('/alerting/silences'),
- parent: 'go/alerting',
- },
- ],
- },
-];
-
-export { alertingCommandPaletteStaticActions };
diff --git a/public/app/features/commandPalette/actions/global.static.actions.ts b/public/app/features/commandPalette/actions/global.static.actions.ts
deleted file mode 100644
index 7fb13a30a0f..00000000000
--- a/public/app/features/commandPalette/actions/global.static.actions.ts
+++ /dev/null
@@ -1,153 +0,0 @@
-import { Action, Priority } from 'kbar';
-import { flatMapDeep } from 'lodash';
-
-import { NavModelItem } from '@grafana/data';
-import { locationService } from '@grafana/runtime';
-
-import { alertingCommandPaletteStaticActions } from './alerting.static.actions';
-
-export interface NavBarActions {
- url: string;
- actions: Action[];
-}
-
-export default (navBarTree: NavModelItem[]) => {
- const globalActions: Action[] = [
- {
- id: 'go/search',
- name: 'Go to dashboard search',
- keywords: 'navigate',
- perform: () => locationService.push('?search=open'),
- section: 'Navigation',
- shortcut: ['s', 'o'],
- },
- {
- id: 'go/dashboard',
- name: 'Go to dashboard...',
- keywords: 'navigate',
- section: 'Navigation',
- priority: Priority.NORMAL,
- },
- {
- id: 'preferences/theme',
- name: 'Change theme...',
- keywords: 'interface color dark light',
- section: 'Preferences',
- shortcut: ['c', 't'],
- },
- {
- id: 'preferences/dark-theme',
- name: 'Dark',
- keywords: 'dark theme',
- section: '',
- perform: () => {
- locationService.push({ search: '?theme=dark' });
- location.reload();
- },
- parent: 'preferences/theme',
- },
- {
- id: 'preferences/light-theme',
- name: 'Light',
- keywords: 'light theme',
- section: '',
- perform: () => {
- locationService.push({ search: '?theme=light' });
- location.reload();
- },
- parent: 'preferences/theme',
- },
- ];
-
- // this maps actions to navbar by URL items for showing/hiding
- // actions is an array for multiple child actions that would be under one navbar item
- const navBarActionMap: NavBarActions[] = [
- {
- url: '/dashboard/new',
- actions: [
- {
- id: 'management/create-folder',
- name: 'Create folder',
- keywords: 'new add',
- perform: () => locationService.push('/dashboards/folder/new'),
- section: 'Management',
- },
- {
- id: 'management/create-dashboard',
- name: 'Create dashboard',
- keywords: 'new add',
- perform: () => locationService.push('/dashboard/new'),
- section: 'Management',
- },
- ],
- },
- {
- url: '/',
- actions: [
- {
- id: 'go/home',
- name: 'Go to home',
- keywords: 'navigate',
- perform: () => locationService.push('/'),
- section: 'Navigation',
- shortcut: ['g', 'h'],
- priority: Priority.HIGH,
- },
- ],
- },
- {
- url: '/explore',
- actions: [
- {
- id: 'go/explore',
- name: 'Go to explore',
- keywords: 'navigate',
- perform: () => locationService.push('/explore'),
- section: 'Navigation',
- priority: Priority.NORMAL,
- },
- ],
- },
- ...alertingCommandPaletteStaticActions,
- {
- url: '/profile',
- actions: [
- {
- id: 'go/profile',
- name: 'Go to profile',
- keywords: 'navigate preferences',
- perform: () => locationService.push('/profile'),
- section: 'Navigation',
- shortcut: ['g', 'p'],
- priority: Priority.LOW,
- },
- ],
- },
- {
- url: '/datasources',
- actions: [
- {
- id: 'go/configuration',
- name: 'Go to data sources configuration',
- keywords: 'navigate settings ds',
- perform: () => locationService.push('/datasources'),
- section: 'Navigation',
- },
- ],
- },
- ];
-
- const navBarActions: Action[] = [];
- const navBarFlatUrls = flatMapDeep(navBarTree, (nbt) => nbt.children)
- .map((nbf) => nbf?.url)
- .filter((url) => url !== undefined);
-
- navBarActionMap.forEach((navBarAction) => {
- const navBarItem = navBarFlatUrls.find((url) => navBarAction.url === url);
- if (navBarItem) {
- navBarActions.push(...navBarAction.actions);
- }
- });
-
- return [...globalActions, ...navBarActions];
-};
diff --git a/public/app/features/commandPalette/actions/global.static.actions.tsx b/public/app/features/commandPalette/actions/global.static.actions.tsx
new file mode 100644
index 00000000000..505b89fb798
--- /dev/null
+++ b/public/app/features/commandPalette/actions/global.static.actions.tsx
@@ -0,0 +1,117 @@
+import { Action, Priority } from 'kbar';
+import React from 'react';
+
+import { isIconName, NavModelItem } from '@grafana/data';
+import { locationService } from '@grafana/runtime';
+import { Icon } from '@grafana/ui';
+
+const SECTION_PAGES = 'Pages';
+const SECTION_ACTIONS = 'Actions';
+const SECTION_PREFERENCES = 'Preferences';
+
+export interface NavBarActions {
+ url: string;
+ actions: Action[];
+}
+
+// TODO: Clean this once ID is mandatory on nav items
+function idForNavItem(navItem: NavModelItem) {
+ return 'navModel.' + navItem.id ?? navItem.url ?? navItem.text ?? navItem.subTitle;
+}
+
+function navTreeToActions(navTree: NavModelItem[], parent?: NavModelItem): Action[] {
+ const navActions: Action[] = [];
+
+ for (const navItem of navTree) {
+ const { url, text, isCreateAction, children } = navItem;
+ const hasChildren = Boolean(children?.length);
+
+ if (!(url || hasChildren)) {
+ continue;
+ }
+
+ const action: Action = {
+ id: idForNavItem(navItem),
+ name: text, // TODO: translate
+ section: isCreateAction ? SECTION_ACTIONS : SECTION_PAGES,
+ perform: url ? () => locationService.push(url) : undefined,
+ parent: parent && idForNavItem(parent),
+
+ // Only show icons for top level items
+ icon: !parent && iconForNavItem(navItem),
+ };
+
+ navActions.push(action);
+
+ if (children?.length) {
+ const childActions = navTreeToActions(children, navItem);
+ navActions.push(...childActions);
+ }
+ }
+
+ return navActions;
+}
+
+export default (navBarTree: NavModelItem[]) => {
+ const globalActions: Action[] = [
+ {
+ // TODO: Figure out what section, if any, to put this in
+ id: 'go/dashboard',
+ name: 'Dashboards...',
+ keywords: 'navigate',
+ priority: Priority.NORMAL,
+ },
+ {
+ id: 'go/search',
+ name: 'Search',
+ keywords: 'navigate',
+ icon: ,
+ perform: () => locationService.push('?search=open'),
+ section: SECTION_PAGES,
+ shortcut: ['s', 'o'],
+ },
+ {
+ id: 'preferences/theme',
+ name: 'Change theme...',
+ keywords: 'interface color dark light',
+ section: SECTION_PREFERENCES,
+ shortcut: ['c', 't'],
+ },
+ {
+ id: 'preferences/dark-theme',
+ name: 'Dark',
+ keywords: 'dark theme',
+ section: '',
+ perform: () => {
+ locationService.push({ search: '?theme=dark' });
+ location.reload();
+ },
+ parent: 'preferences/theme',
+ },
+ {
+ id: 'preferences/light-theme',
+ name: 'Light',
+ keywords: 'light theme',
+ section: '',
+ perform: () => {
+ locationService.push({ search: '?theme=light' });
+ location.reload();
+ },
+ parent: 'preferences/theme',
+ },
+ ];
+
+ const navBarActions = navTreeToActions(navBarTree);
+
+ return [...globalActions, ...navBarActions];
+};
+
+function iconForNavItem(navItem: NavModelItem) {
+ if (navItem.icon && isIconName(navItem.icon)) {
+ return ;
+ } else if (navItem.img) {
+ return
;
+ }
+
+ return undefined;
+}