From 89e3fa72457c6b067fae345ab9afc9ff8e3ca984 Mon Sep 17 00:00:00 2001 From: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com> Date: Thu, 16 Oct 2025 14:27:47 +0200 Subject: [PATCH] Add open Assistant keyboard shortcut (#112228) --- package.json | 2 +- packages/grafana-flamegraph/package.json | 2 +- .../src/FlameGraphContainer.test.tsx | 5 +- .../src/FlameGraphHeader.test.tsx | 5 +- .../core/components/help/HelpModal.test.tsx | 151 ++++++++ public/app/core/components/help/HelpModal.tsx | 327 +++++++++--------- public/app/core/services/keybindingSrv.ts | 28 ++ .../logs/components/panel/LogLine.test.tsx | 5 +- .../components/panel/LogLineContext.test.tsx | 5 +- .../components/panel/LogLineDetails.test.tsx | 5 +- .../components/panel/LogLineMenu.test.tsx | 5 +- .../logs/components/panel/LogList.test.tsx | 4 +- .../logs/components/panel/LogListContext.tsx | 2 +- .../components/panel/LogListControls.test.tsx | 4 +- .../panel/__mocks__/LogListContext.tsx | 4 +- .../grafana-pyroscope-datasource/package.json | 2 +- .../app/plugins/panel/logs/LogsPanel.test.tsx | 2 +- public/locales/en-US/grafana.json | 2 + yarn.lock | 14 +- 19 files changed, 397 insertions(+), 177 deletions(-) create mode 100644 public/app/core/components/help/HelpModal.test.tsx diff --git a/package.json b/package.json index 39fc401989a..0b08c45534b 100644 --- a/package.json +++ b/package.json @@ -279,7 +279,7 @@ "@formatjs/intl-durationformat": "^0.7.0", "@glideapps/glide-data-grid": "^6.0.0", "@grafana/alerting": "workspace:*", - "@grafana/assistant": "0.0.18", + "@grafana/assistant": "0.1.0", "@grafana/aws-sdk": "0.7.1", "@grafana/azure-sdk": "0.0.7", "@grafana/data": "workspace:*", diff --git a/packages/grafana-flamegraph/package.json b/packages/grafana-flamegraph/package.json index b7279e5244b..3185d65d5c1 100644 --- a/packages/grafana-flamegraph/package.json +++ b/packages/grafana-flamegraph/package.json @@ -83,7 +83,7 @@ "typescript": "5.9.2" }, "peerDependencies": { - "@grafana/assistant": "^0.0.17 || ^0.0.18", + "@grafana/assistant": "^0.1.0", "react": "^18.0.0", "react-dom": "^18.0.0" } diff --git a/packages/grafana-flamegraph/src/FlameGraphContainer.test.tsx b/packages/grafana-flamegraph/src/FlameGraphContainer.test.tsx index e8786e42466..10e2eeb8d03 100644 --- a/packages/grafana-flamegraph/src/FlameGraphContainer.test.tsx +++ b/packages/grafana-flamegraph/src/FlameGraphContainer.test.tsx @@ -10,7 +10,10 @@ import FlameGraphContainer, { labelSearch } from './FlameGraphContainer'; import { MIN_WIDTH_TO_SHOW_BOTH_TOPTABLE_AND_FLAMEGRAPH } from './constants'; jest.mock('@grafana/assistant', () => ({ - useAssistant: jest.fn(() => [false, null]), // [isAvailable, openAssistant] + useAssistant: jest.fn().mockReturnValue({ + isAvailable: false, + openAssistant: undefined, + }), createAssistantContextItem: jest.fn(), OpenAssistantButton: () =>
OpenAssistantButton
, })); diff --git a/packages/grafana-flamegraph/src/FlameGraphHeader.test.tsx b/packages/grafana-flamegraph/src/FlameGraphHeader.test.tsx index 59be0ed931e..ce00964a8d5 100644 --- a/packages/grafana-flamegraph/src/FlameGraphHeader.test.tsx +++ b/packages/grafana-flamegraph/src/FlameGraphHeader.test.tsx @@ -8,7 +8,10 @@ import FlameGraphHeader from './FlameGraphHeader'; import { ColorScheme, SelectedView } from './types'; jest.mock('@grafana/assistant', () => ({ - useAssistant: jest.fn(() => [false, null]), // [isAvailable, openAssistant] + useAssistant: jest.fn().mockReturnValue({ + isAvailable: false, + openAssistant: undefined, + }), createAssistantContextItem: jest.fn(), OpenAssistantButton: () =>
OpenAssistantButton
, })); diff --git a/public/app/core/components/help/HelpModal.test.tsx b/public/app/core/components/help/HelpModal.test.tsx new file mode 100644 index 00000000000..d343c5560b0 --- /dev/null +++ b/public/app/core/components/help/HelpModal.test.tsx @@ -0,0 +1,151 @@ +import { renderHook } from '@testing-library/react'; + +import { useAssistant } from '@grafana/assistant'; + +import { useShortcuts } from './HelpModal'; + +// Mock the assistant hook +jest.mock('@grafana/assistant', () => ({ + useAssistant: jest.fn(), +})); + +// Mock getModKey +jest.mock('app/core/utils/browser', () => ({ + getModKey: jest.fn(() => 'ctrl'), +})); + +const mockUseAssistant = useAssistant as jest.MockedFunction; + +describe('useShortcuts', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return shortcuts without assistant shortcut when assistant is not available', () => { + mockUseAssistant.mockReturnValue({ + isAvailable: false, + toggleAssistant: jest.fn(), + } as unknown as ReturnType); + + const { result } = renderHook(() => useShortcuts()); + + expect(result.current).toHaveLength(4); // Global, Time range, Dashboard, Focused panel + + // Check that global shortcuts don't include assistant shortcut + const globalCategory = result.current.find((category) => category.category.includes('Global')); + expect(globalCategory).toBeDefined(); + + const assistantShortcut = globalCategory!.shortcuts.find((shortcut) => shortcut.keys.includes('ctrl + .')); + expect(assistantShortcut).toBeUndefined(); + }); + + it('should return shortcuts with assistant shortcut when assistant is available', () => { + mockUseAssistant.mockReturnValue({ + isAvailable: true, + openAssistant: jest.fn(), + closeAssistant: jest.fn(), + toggleAssistant: jest.fn(), + }); + + const { result } = renderHook(() => useShortcuts()); + + expect(result.current).toHaveLength(4); // Global, Time range, Dashboard, Focused panel + + // Check that global shortcuts include assistant shortcut + const globalCategory = result.current.find((category) => category.category.includes('Global')); + expect(globalCategory).toBeDefined(); + + const assistantShortcut = globalCategory!.shortcuts.find((shortcut) => shortcut.keys.includes('ctrl + .')); + expect(assistantShortcut).toBeDefined(); + expect(assistantShortcut!.description).toContain('Assistant'); + }); + + it('should include all expected shortcut categories', () => { + mockUseAssistant.mockReturnValue({ + isAvailable: false, + openAssistant: jest.fn(), + closeAssistant: jest.fn(), + toggleAssistant: jest.fn(), + }); + + const { result } = renderHook(() => useShortcuts()); + + const categories = result.current.map((category) => category.category); + + expect(categories).toEqual( + expect.arrayContaining([ + expect.stringContaining('Global'), + expect.stringContaining('Time range'), + expect.stringContaining('Dashboard'), + expect.stringContaining('Focused panel'), + ]) + ); + }); + + it('should use the correct modKey in shortcuts', () => { + mockUseAssistant.mockReturnValue({ + isAvailable: false, + openAssistant: jest.fn(), + closeAssistant: jest.fn(), + toggleAssistant: jest.fn(), + }); + + const { result } = renderHook(() => useShortcuts()); + + // Find a shortcut that uses modKey (like save dashboard) + const dashboardCategory = result.current.find((category) => category.category.includes('Dashboard')); + const saveShortcut = dashboardCategory!.shortcuts.find((shortcut) => + shortcut.description.includes('Save dashboard') + ); + + expect(saveShortcut).toBeDefined(); + expect(saveShortcut!.keys[0]).toBe('ctrl + s'); + }); + + it('should memoize results when dependencies do not change', () => { + mockUseAssistant.mockReturnValue({ + isAvailable: false, + openAssistant: jest.fn(), + closeAssistant: jest.fn(), + toggleAssistant: jest.fn(), + }); + + const { result, rerender } = renderHook(() => useShortcuts()); + const firstResult = result.current; + + // Rerender without changing dependencies + rerender(); + + // Should return the same reference (memoized) + expect(result.current).toBe(firstResult); + }); + + it('should update when assistant availability changes', () => { + mockUseAssistant.mockReturnValue({ + isAvailable: false, + openAssistant: jest.fn(), + closeAssistant: jest.fn(), + toggleAssistant: jest.fn(), + }); + + const { result, rerender } = renderHook(() => useShortcuts()); + const firstResult = result.current; + + // Change assistant availability + mockUseAssistant.mockReturnValue({ + isAvailable: true, + openAssistant: jest.fn(), + closeAssistant: jest.fn(), + toggleAssistant: jest.fn(), + }); + rerender(); + + // Should return a different reference (not memoized) + expect(result.current).not.toBe(firstResult); + + // And should now include assistant shortcut + const globalCategory = result.current.find((category) => category.category.includes('Global')); + const assistantShortcut = globalCategory!.shortcuts.find((shortcut) => shortcut.keys.includes('ctrl + .')); + expect(assistantShortcut).toBeDefined(); + }); +}); diff --git a/public/app/core/components/help/HelpModal.tsx b/public/app/core/components/help/HelpModal.tsx index 375f3494daa..4017443b252 100644 --- a/public/app/core/components/help/HelpModal.tsx +++ b/public/app/core/components/help/HelpModal.tsx @@ -1,173 +1,19 @@ import { css } from '@emotion/css'; import { useMemo } from 'react'; +import { useAssistant } from '@grafana/assistant'; import { GrafanaTheme2 } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; import { Grid, Modal, useStyles2, Text } from '@grafana/ui'; import { getModKey } from 'app/core/utils/browser'; -const getShortcuts = (modKey: string) => { - return [ - { - category: t('help-modal.shortcuts-category.global', 'Global'), - shortcuts: [ - { - keys: ['g', 'h'], - description: t('help-modal.shortcuts-description.go-to-home-dashboard', 'Go to Home Dashboard'), - }, - { - keys: ['g', 'd'], - description: t('help-modal.shortcuts-description.go-to-dashboards', 'Go to Dashboards'), - }, - { keys: ['g', 'e'], description: t('help-modal.shortcuts-description.go-to-explore', 'Go to Explore') }, - { keys: ['g', 'p'], description: t('help-modal.shortcuts-description.go-to-profile', 'Go to Profile') }, - { keys: [`${modKey} + k`], description: t('help-modal.shortcuts-description.open-search', 'Open search') }, - { - keys: ['esc'], - description: t('help-modal.shortcuts-description.exit-edit/setting-views', 'Exit edit/setting views'), - }, - { - keys: ['?'], - description: t('help-modal.shortcuts-description.show-all-shortcuts', 'Show all keyboard shortcuts'), - }, - { keys: ['c', 't'], description: t('help-modal.shortcuts-description.change-theme', 'Change theme') }, - ], - }, - { - category: t('help-modal.shortcuts-category.time-range', 'Time range'), - shortcuts: [ - { - keys: ['t', 'z'], - description: t('help-modal.shortcuts-description.zoom-out-time-range', 'Zoom out time range'), - }, - { - keys: ['t', '←'], - description: t('help-modal.shortcuts-description.move-time-range-back', 'Move time range back'), - }, - { - keys: ['t', '→'], - description: t('help-modal.shortcuts-description.move-time-range-forward', 'Move time range forward'), - }, - { - keys: ['t', 'a'], - description: t( - 'help-modal.shortcuts-description.make-time-range-permanent', - 'Make time range absolute/permanent' - ), - }, - { - keys: ['t', 'c'], - description: t('help-modal.shortcuts-description.copy-time-range', 'Copy time range'), - }, - { - keys: ['t', 'v'], - description: t('help-modal.shortcuts-description.paste-time-range', 'Paste time range'), - }, - ], - }, - { - category: t('help-modal.shortcuts-category.dashboard', 'Dashboard'), - shortcuts: [ - { - keys: [`${modKey} + s`], - description: t('help-modal.shortcuts-description.save-dashboard', 'Save dashboard'), - }, - { - keys: ['d', 'r'], - description: t('help-modal.shortcuts-description.refresh-all-panels', 'Refresh all panels'), - }, - { - keys: ['d', 's'], - description: t('help-modal.shortcuts-description.dashboard-settings', 'Dashboard settings'), - }, - { - keys: ['d', 'v'], - description: t('help-modal.shortcuts-description.toggle-active-mode', 'Toggle in-active / view mode'), - }, - { - keys: ['d', 'k'], - description: t('help-modal.shortcuts-description.toggle-kiosk', 'Toggle kiosk mode (hides top nav)'), - }, - { - keys: ['d', '⇧ + e'], - description: t('help-modal.shortcuts-description.expand-all-rows', 'Expand all rows'), - }, - { - keys: ['d', '⇧ + c'], - description: t('help-modal.shortcuts-description.collapse-all-rows', 'Collapse all rows'), - }, - { - keys: ['d', 'a'], - description: t( - 'help-modal.shortcuts-description.toggle-auto-fit', - 'Toggle auto fit panels (experimental feature)' - ), - }, - { - keys: [`${modKey} + o`], - description: t('help-modal.shortcuts-description.toggle-graph-crosshair', 'Toggle shared graph crosshair'), - }, - { - keys: ['d', 'l'], - description: t('help-modal.shortcuts-description.toggle-all-panel-legends', 'Toggle all panel legends'), - }, - { - keys: ['d', 'x'], - description: t('help-modal.shortcuts-description.toggle-exemplars', 'Toggle exemplars in all panel'), - }, - ], - }, - { - category: t('help-modal.shortcuts-category.focused-panel', 'Focused panel'), - shortcuts: [ - { - keys: ['e'], - description: t('help-modal.shortcuts-description.toggle-panel-edit', 'Toggle panel edit view'), - }, - { - keys: ['v'], - description: t('help-modal.shortcuts-description.toggle-panel-fullscreen', 'Toggle panel fullscreen view'), - }, - { - keys: ['p', 'u'], - description: t('help-modal.shortcuts-description.open-share-link-drawer', 'Share panel link'), - }, - { - keys: ['p', 'e'], - description: t('help-modal.shortcuts-description.open-share-embed-drawer', 'Share panel embed'), - }, - { - keys: ['p', 's'], - description: t('help-modal.shortcuts-description.open-shared-modal', 'Share panel snapshot'), - }, - { - keys: ['p', 'x'], - description: t('help-modal.shortcuts-description.explore-panel', 'Explore panel'), - }, - { - keys: ['i'], - description: t('help-modal.shortcuts-description.inspect-panel', 'Inspect panel'), - }, - { keys: ['p', 'd'], description: t('help-modal.shortcuts-description.duplicate-panel', 'Duplicate Panel') }, - { keys: ['p', 'r'], description: t('help-modal.shortcuts-description.remove-panel', 'Remove Panel') }, - { - keys: ['p', 'l'], - description: t('help-modal.shortcuts-description.toggle-panel-legend', 'Toggle panel legend'), - }, - ], - }, - ]; -}; - export interface HelpModalProps { onDismiss: () => void; } export const HelpModal = ({ onDismiss }: HelpModalProps): JSX.Element => { const styles = useStyles2(getStyles); - - const modKey = useMemo(() => getModKey(), []); - const shortcuts = useMemo(() => getShortcuts(modKey), [modKey]); + const shortcuts = useShortcuts(); return ( @@ -213,6 +59,175 @@ export const HelpModal = ({ onDismiss }: HelpModalProps): JSX.Element => { ); }; +export const useShortcuts = () => { + const { isAvailable: assistantAvailable } = useAssistant(); + const modKey = useMemo(() => getModKey(), []); + + return useMemo(() => { + const globalShortcuts = [ + { + keys: ['g', 'h'], + description: t('help-modal.shortcuts-description.go-to-home-dashboard', 'Go to Home Dashboard'), + }, + { + keys: ['g', 'd'], + description: t('help-modal.shortcuts-description.go-to-dashboards', 'Go to Dashboards'), + }, + { keys: ['g', 'e'], description: t('help-modal.shortcuts-description.go-to-explore', 'Go to Explore') }, + { keys: ['g', 'p'], description: t('help-modal.shortcuts-description.go-to-profile', 'Go to Profile') }, + { keys: ['g', 'a'], description: t('help-modal.shortcuts-description.open-alerting', 'Go to Alerting') }, + { keys: [`${modKey} + k`], description: t('help-modal.shortcuts-description.open-search', 'Open search') }, + { + keys: ['esc'], + description: t('help-modal.shortcuts-description.exit-edit/setting-views', 'Exit edit/setting views'), + }, + { + keys: ['?'], + description: t('help-modal.shortcuts-description.show-all-shortcuts', 'Show all keyboard shortcuts'), + }, + { keys: ['c', 't'], description: t('help-modal.shortcuts-description.change-theme', 'Change theme') }, + ]; + + // Add assistant shortcut only if assistant is available + if (assistantAvailable) { + globalShortcuts.push({ + keys: [`${modKey} + .`], + description: t('help-modal.shortcuts-description.open-assistant', 'Open Assistant'), + }); + } + + return [ + { + category: t('help-modal.shortcuts-category.global', 'Global'), + shortcuts: globalShortcuts, + }, + { + category: t('help-modal.shortcuts-category.time-range', 'Time range'), + shortcuts: [ + { + keys: ['t', 'z'], + description: t('help-modal.shortcuts-description.zoom-out-time-range', 'Zoom out time range'), + }, + { + keys: ['t', '←'], + description: t('help-modal.shortcuts-description.move-time-range-back', 'Move time range back'), + }, + { + keys: ['t', '→'], + description: t('help-modal.shortcuts-description.move-time-range-forward', 'Move time range forward'), + }, + { + keys: ['t', 'a'], + description: t( + 'help-modal.shortcuts-description.make-time-range-permanent', + 'Make time range absolute/permanent' + ), + }, + { + keys: ['t', 'c'], + description: t('help-modal.shortcuts-description.copy-time-range', 'Copy time range'), + }, + { + keys: ['t', 'v'], + description: t('help-modal.shortcuts-description.paste-time-range', 'Paste time range'), + }, + ], + }, + { + category: t('help-modal.shortcuts-category.dashboard', 'Dashboard'), + shortcuts: [ + { + keys: [`${modKey} + s`], + description: t('help-modal.shortcuts-description.save-dashboard', 'Save dashboard'), + }, + { + keys: ['d', 'r'], + description: t('help-modal.shortcuts-description.refresh-all-panels', 'Refresh all panels'), + }, + { + keys: ['d', 's'], + description: t('help-modal.shortcuts-description.dashboard-settings', 'Dashboard settings'), + }, + { + keys: ['d', 'v'], + description: t('help-modal.shortcuts-description.toggle-active-mode', 'Toggle in-active / view mode'), + }, + { + keys: ['d', 'k'], + description: t('help-modal.shortcuts-description.toggle-kiosk', 'Toggle kiosk mode (hides top nav)'), + }, + { + keys: ['d', '⇧ + e'], + description: t('help-modal.shortcuts-description.expand-all-rows', 'Expand all rows'), + }, + { + keys: ['d', '⇧ + c'], + description: t('help-modal.shortcuts-description.collapse-all-rows', 'Collapse all rows'), + }, + { + keys: ['d', 'a'], + description: t( + 'help-modal.shortcuts-description.toggle-auto-fit', + 'Toggle auto fit panels (experimental feature)' + ), + }, + { + keys: [`${modKey} + o`], + description: t('help-modal.shortcuts-description.toggle-graph-crosshair', 'Toggle shared graph crosshair'), + }, + { + keys: ['d', 'l'], + description: t('help-modal.shortcuts-description.toggle-all-panel-legends', 'Toggle all panel legends'), + }, + { + keys: ['d', 'x'], + description: t('help-modal.shortcuts-description.toggle-exemplars', 'Toggle exemplars in all panel'), + }, + ], + }, + { + category: t('help-modal.shortcuts-category.focused-panel', 'Focused panel'), + shortcuts: [ + { + keys: ['e'], + description: t('help-modal.shortcuts-description.toggle-panel-edit', 'Toggle panel edit view'), + }, + { + keys: ['v'], + description: t('help-modal.shortcuts-description.toggle-panel-fullscreen', 'Toggle panel fullscreen view'), + }, + { + keys: ['p', 'u'], + description: t('help-modal.shortcuts-description.open-share-link-drawer', 'Share panel link'), + }, + { + keys: ['p', 'e'], + description: t('help-modal.shortcuts-description.open-share-embed-drawer', 'Share panel embed'), + }, + { + keys: ['p', 's'], + description: t('help-modal.shortcuts-description.open-shared-modal', 'Share panel snapshot'), + }, + { + keys: ['p', 'x'], + description: t('help-modal.shortcuts-description.explore-panel', 'Explore panel'), + }, + { + keys: ['i'], + description: t('help-modal.shortcuts-description.inspect-panel', 'Inspect panel'), + }, + { keys: ['p', 'd'], description: t('help-modal.shortcuts-description.duplicate-panel', 'Duplicate Panel') }, + { keys: ['p', 'r'], description: t('help-modal.shortcuts-description.remove-panel', 'Remove Panel') }, + { + keys: ['p', 'l'], + description: t('help-modal.shortcuts-description.toggle-panel-legend', 'Toggle panel legend'), + }, + ], + }, + ]; + }, [modKey, assistantAvailable]); +}; + interface KeyProps { children: string; } diff --git a/public/app/core/services/keybindingSrv.ts b/public/app/core/services/keybindingSrv.ts index 37f70eb6b9a..9b4202b74dc 100644 --- a/public/app/core/services/keybindingSrv.ts +++ b/public/app/core/services/keybindingSrv.ts @@ -1,3 +1,4 @@ +import { toggleAssistant, isAssistantAvailable } from '@grafana/assistant'; import { LegacyGraphHoverClearEvent, SetPanelAttentionEvent, locationUtil } from '@grafana/data'; import { LocationService } from '@grafana/runtime'; import appEvents from 'app/core/app_events'; @@ -38,6 +39,7 @@ export class KeybindingSrv { } /** string for VizPanel key and number for panelId */ private panelId: string | number | null = null; + private assistantSubscription: { unsubscribe: () => void } | null = null; clearAndInitGlobalBindings(route: RouteDescriptor) { mousetrap.reset(); @@ -51,6 +53,8 @@ export class KeybindingSrv { this.bind('g e', this.goToExplore); this.bind('g a', this.openAlerting); this.bind('g p', this.goToProfile); + // Conditionally bind open Assistant shortcut ('o a') if Assistant is available + this.bindAssistantShortcutIfAvailable(); this.bind('esc', this.exit); this.bindGlobalEsc(); } @@ -120,6 +124,30 @@ export class KeybindingSrv { appEvents.publish(new ShowModalReactEvent({ component: HelpModal })); } + private bindAssistantShortcutIfAvailable() { + // Clean up any existing subscription + if (this.assistantSubscription) { + this.assistantSubscription.unsubscribe(); + } + // Subscribe to assistant availability and bind/unbind shortcut accordingly + this.assistantSubscription = isAssistantAvailable().subscribe((available) => { + if (available) { + this.bind('mod+.', this.toggleAssistant); + } else { + // Unbind the shortcut if assistant becomes unavailable + mousetrap.unbind('mod+.'); + } + }); + } + + private toggleAssistant() { + toggleAssistant({ + origin: 'grafana/keyboard-shortcut', + prompt: '', + context: [], + }); + } + private exit() { const search = this.locationService.getSearchObject(); diff --git a/public/app/features/logs/components/panel/LogLine.test.tsx b/public/app/features/logs/components/panel/LogLine.test.tsx index 6e4b1599398..5222fae50d9 100644 --- a/public/app/features/logs/components/panel/LogLine.test.tsx +++ b/public/app/features/logs/components/panel/LogLine.test.tsx @@ -18,7 +18,10 @@ import { LogLineVirtualization } from './virtualization'; jest.mock('@grafana/assistant', () => ({ ...jest.requireActual('@grafana/assistant'), - useAssistant: jest.fn(() => [true, jest.fn()]), + useAssistant: jest.fn().mockReturnValue({ + isAvailable: true, + openAssistant: jest.fn(), + }), })); jest.mock('./LogListContext'); diff --git a/public/app/features/logs/components/panel/LogLineContext.test.tsx b/public/app/features/logs/components/panel/LogLineContext.test.tsx index 8b88a9cb2c9..fc6e6ed2a13 100644 --- a/public/app/features/logs/components/panel/LogLineContext.test.tsx +++ b/public/app/features/logs/components/panel/LogLineContext.test.tsx @@ -14,7 +14,10 @@ import { DEFAULT_TIME_WINDOW, LogLineContext, PAGE_SIZE } from './LogLineContext jest.mock('@grafana/assistant', () => ({ ...jest.requireActual('@grafana/assistant'), - useAssistant: jest.fn(() => [true, jest.fn()]), + useAssistant: jest.fn().mockReturnValue({ + isAvailable: true, + openAssistant: jest.fn(), + }), })); const dfBefore = createDataFrame({ diff --git a/public/app/features/logs/components/panel/LogLineDetails.test.tsx b/public/app/features/logs/components/panel/LogLineDetails.test.tsx index ccb35f93064..7deccf416ff 100644 --- a/public/app/features/logs/components/panel/LogLineDetails.test.tsx +++ b/public/app/features/logs/components/panel/LogLineDetails.test.tsx @@ -30,7 +30,10 @@ import { defaultValue } from './__mocks__/LogListContext'; jest.mock('@grafana/assistant', () => { return { ...jest.requireActual('@grafana/assistant'), - useAssistant: jest.fn().mockReturnValue([true, jest.fn()]), + useAssistant: jest.fn().mockReturnValue({ + isAvailable: true, + openAssistant: jest.fn(), + }), }; }); diff --git a/public/app/features/logs/components/panel/LogLineMenu.test.tsx b/public/app/features/logs/components/panel/LogLineMenu.test.tsx index a8c03ea385e..78e11921e8a 100644 --- a/public/app/features/logs/components/panel/LogLineMenu.test.tsx +++ b/public/app/features/logs/components/panel/LogLineMenu.test.tsx @@ -15,7 +15,10 @@ jest.mock('./LogListContext'); jest.mock('@grafana/assistant', () => ({ ...jest.requireActual('@grafana/assistant'), - useAssistant: jest.fn(() => [true, jest.fn()]), + useAssistant: jest.fn().mockReturnValue({ + isAvailable: true, + openAssistant: jest.fn(), + }), })); jest.mock('@grafana/runtime', () => ({ diff --git a/public/app/features/logs/components/panel/LogList.test.tsx b/public/app/features/logs/components/panel/LogList.test.tsx index 7f292ab7216..db4d3904577 100644 --- a/public/app/features/logs/components/panel/LogList.test.tsx +++ b/public/app/features/logs/components/panel/LogList.test.tsx @@ -21,7 +21,9 @@ import { LogList, Props } from './LogList'; jest.mock('@grafana/assistant', () => ({ ...jest.requireActual('@grafana/assistant'), - useAssistant: jest.fn(() => [true, jest.fn()]), + useAssistant: jest.fn().mockReturnValue({ + isAvailable: true, + }), })); jest.mock('@grafana/runtime', () => { diff --git a/public/app/features/logs/components/panel/LogListContext.tsx b/public/app/features/logs/components/panel/LogListContext.tsx index 9ec63e2765d..bb30028523e 100644 --- a/public/app/features/logs/components/panel/LogListContext.tsx +++ b/public/app/features/logs/components/panel/LogListContext.tsx @@ -265,7 +265,7 @@ export const LogListContextProvider = ({ const [detailsMode, setDetailsMode] = useState( detailsModeProp ?? getDefaultDetailsMode(containerElement) ); - const [isAssistantAvailable, openAssistant] = useAssistant(); + const { isAvailable: isAssistantAvailable, openAssistant } = useAssistant(); const [prettifyJSON, setPrettifyJSONState] = useState(prettifyJSONProp); const [wrapLogMessage, setWrapLogMessageState] = useState(wrapLogMessageProp); diff --git a/public/app/features/logs/components/panel/LogListControls.test.tsx b/public/app/features/logs/components/panel/LogListControls.test.tsx index bfd71304f2f..29955c8d46d 100644 --- a/public/app/features/logs/components/panel/LogListControls.test.tsx +++ b/public/app/features/logs/components/panel/LogListControls.test.tsx @@ -49,7 +49,9 @@ jest.mock('../../utils', () => ({ jest.mock('@grafana/assistant', () => { return { ...jest.requireActual('@grafana/assistant'), - useAssistant: jest.fn().mockReturnValue([true, jest.fn()]), + useAssistant: jest.fn().mockReturnValue({ + isAvailable: true, + }), }; }); diff --git a/public/app/features/logs/components/panel/__mocks__/LogListContext.tsx b/public/app/features/logs/components/panel/__mocks__/LogListContext.tsx index 03e5e394870..af289c71f2c 100644 --- a/public/app/features/logs/components/panel/__mocks__/LogListContext.tsx +++ b/public/app/features/logs/components/panel/__mocks__/LogListContext.tsx @@ -9,7 +9,9 @@ import { LogListModel } from '../processing'; jest.mock('@grafana/assistant', () => { return { ...jest.requireActual('@grafana/assistant'), - useAssistant: jest.fn().mockReturnValue([true, jest.fn()]), + useAssistant: jest.fn().mockReturnValue({ + isAvailable: true, + }), }; }); diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json b/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json index fa5a5a2d12a..80b07923827 100644 --- a/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json +++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json @@ -39,7 +39,7 @@ "webpack": "5.101.0" }, "peerDependencies": { - "@grafana/assistant": "^0.0.17 || ^0.0.18", + "@grafana/assistant": "^0.1.0", "@grafana/runtime": "*" }, "scripts": { diff --git a/public/app/plugins/panel/logs/LogsPanel.test.tsx b/public/app/plugins/panel/logs/LogsPanel.test.tsx index bd879def92a..b3d6aee7d6d 100644 --- a/public/app/plugins/panel/logs/LogsPanel.test.tsx +++ b/public/app/plugins/panel/logs/LogsPanel.test.tsx @@ -61,7 +61,7 @@ jest.mock('@grafana/data', () => ({ jest.mock('@grafana/assistant', () => { return { ...jest.requireActual('@grafana/assistant'), - useAssistant: jest.fn().mockReturnValue([true, jest.fn()]), + useAssistant: jest.fn().mockReturnValue({ isAvailable: true }), }; }); diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 9c91547b203..dbcb5040a1e 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -9097,6 +9097,8 @@ "make-time-range-permanent": "Make time range absolute/permanent", "move-time-range-back": "Move time range back", "move-time-range-forward": "Move time range forward", + "open-alerting": "Go to Alerting", + "open-assistant": "Open Assistant", "open-search": "Open search", "open-share-embed-drawer": "Share panel embed", "open-share-link-drawer": "Share panel link", diff --git a/yarn.lock b/yarn.lock index ab4d9f3cc1b..5f02d48c5c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2536,7 +2536,7 @@ __metadata: typescript: "npm:5.9.2" webpack: "npm:5.101.0" peerDependencies: - "@grafana/assistant": ^0.0.17 || ^0.0.18 + "@grafana/assistant": ^0.1.0 "@grafana/runtime": "*" languageName: unknown linkType: soft @@ -2993,9 +2993,9 @@ __metadata: languageName: unknown linkType: soft -"@grafana/assistant@npm:0.0.18": - version: 0.0.18 - resolution: "@grafana/assistant@npm:0.0.18" +"@grafana/assistant@npm:0.1.0": + version: 0.1.0 + resolution: "@grafana/assistant@npm:0.1.0" peerDependencies: "@grafana/data": ">=12.1.0" "@grafana/runtime": ">=12.1.0" @@ -3003,7 +3003,7 @@ __metadata: "@grafana/ui": ">=12.1.0" react: ">=18.0.0" rxjs: ">=7.0.0" - checksum: 10/6e95f6e7026121f220a20064aa1fc715cf969d93e02b506268dd589d4400b399a6e7d1f2a1a69edb973b604be932c7b091deee1960f90eccf8ea53b67314438e + checksum: 10/90cfee9860bc128190ec1357e554685892b3ddd080d914457da311f1d45294dfcc15a427be36c381463eefe39268b5abbc39e6b541ad402e78264cc6d038c23f languageName: node linkType: hard @@ -3223,7 +3223,7 @@ __metadata: tslib: "npm:2.8.1" typescript: "npm:5.9.2" peerDependencies: - "@grafana/assistant": ^0.0.17 || ^0.0.18 + "@grafana/assistant": ^0.1.0 react: ^18.0.0 react-dom: ^18.0.0 languageName: unknown @@ -18277,7 +18277,7 @@ __metadata: "@formatjs/intl-durationformat": "npm:^0.7.0" "@glideapps/glide-data-grid": "npm:^6.0.0" "@grafana/alerting": "workspace:*" - "@grafana/assistant": "npm:0.0.18" + "@grafana/assistant": "npm:0.1.0" "@grafana/aws-sdk": "npm:0.7.1" "@grafana/azure-sdk": "npm:0.0.7" "@grafana/data": "workspace:*"