diff --git a/packages/grafana-ui/src/components/PanelChrome/HoverWidget.tsx b/packages/grafana-ui/src/components/PanelChrome/HoverWidget.tsx index 3105d991c54..8bff29f9f0b 100644 --- a/packages/grafana-ui/src/components/PanelChrome/HoverWidget.tsx +++ b/packages/grafana-ui/src/components/PanelChrome/HoverWidget.tsx @@ -15,14 +15,13 @@ interface Props { title?: string; offset?: number; dragClass?: string; + onOpenMenu?: () => void; } -const selectors = e2eSelectors.components.Panels.Panel.HoverWidget; - -export function HoverWidget({ menu, title, dragClass, children, offset = -32 }: Props) { +export function HoverWidget({ menu, title, dragClass, children, offset = -32, onOpenMenu }: Props) { const styles = useStyles2(getStyles); const draggableRef = useRef(null); - + const selectors = e2eSelectors.components.Panels.Panel.HoverWidget; // Capture the pointer to keep the widget visible while dragging const onPointerDown = useCallback((e: React.PointerEvent) => { draggableRef.current?.setPointerCapture(e.pointerId); @@ -64,6 +63,7 @@ export function HoverWidget({ menu, title, dragClass, children, offset = -32 }: placement="bottom" menuButtonClass={styles.menuButton} onVisibleChange={setMenuOpen} + onOpenMenu={onOpenMenu} /> )} diff --git a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx index 2921b053527..4017fd4c30a 100644 --- a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx +++ b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx @@ -53,6 +53,10 @@ export interface PanelChromeProps { actions?: ReactNode; displayMode?: 'default' | 'transparent'; onCancelQuery?: () => void; + /** + * callback when opening the panel menu + */ + onOpenMenu?: () => void; } /** @@ -83,6 +87,7 @@ export function PanelChrome({ leftItems, actions, onCancelQuery, + onOpenMenu, }: PanelChromeProps) { const theme = useTheme2(); const styles = useStyles2(getStyles); @@ -159,7 +164,13 @@ export function PanelChrome({ {hoverHeader && !isTouchDevice && ( <> - + {headerContent} @@ -192,6 +203,7 @@ export function PanelChrome({ dragClassCancel, showOnHoverClass )} + onOpenMenu={onOpenMenu} /> )} diff --git a/packages/grafana-ui/src/components/PanelChrome/PanelMenu.tsx b/packages/grafana-ui/src/components/PanelChrome/PanelMenu.tsx index c9180fe2882..2f80058db33 100644 --- a/packages/grafana-ui/src/components/PanelChrome/PanelMenu.tsx +++ b/packages/grafana-ui/src/components/PanelChrome/PanelMenu.tsx @@ -1,5 +1,5 @@ import { cx } from '@emotion/css'; -import React, { ReactElement } from 'react'; +import React, { ReactElement, useCallback } from 'react'; import { selectors } from '@grafana/e2e-selectors'; @@ -15,6 +15,7 @@ interface PanelMenuProps { placement?: TooltipPlacement; offset?: [number, number]; onVisibleChange?: (state: boolean) => void; + onOpenMenu?: () => void; } export function PanelMenu({ @@ -25,10 +26,22 @@ export function PanelMenu({ dragClassCancel, menuButtonClass, onVisibleChange, + onOpenMenu, }: PanelMenuProps) { const testId = title ? selectors.components.Panels.Panel.menu(title) : `panel-menu-button`; + + const handleVisibility = useCallback( + (show: boolean) => { + if (show && onOpenMenu) { + onOpenMenu(); + } + return onVisibleChange; + }, + [onOpenMenu, onVisibleChange] + ); + return ( - + { hoverHeader={panelChromeProps.hasOverlayHeader()} displayMode={transparent ? 'transparent' : 'default'} onCancelQuery={panelChromeProps.onCancelQuery} + onOpenMenu={panelChromeProps.onOpenMenu} > {() =>
(this.element = element)} className="panel-height-helper" />} diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx index 1b65818c5fa..144b7d4336a 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { DataLink, GrafanaTheme2, PanelData } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; +import { reportInteraction } from '@grafana/runtime'; import { Icon, useStyles2, ClickOutsideWrapper } from '@grafana/ui'; import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; import { PanelModel } from 'app/features/dashboard/state/PanelModel'; @@ -33,6 +34,10 @@ export function PanelHeader({ panel, error, isViewing, isEditing, data, alertSta const className = cx('panel-header', !(isViewing || isEditing) ? 'grid-drag-handle' : ''); const styles = useStyles2(panelStyles); + const onOpenMenu = () => { + reportInteraction('dashboards_panelheader_menu', { item: 'menu' }); + }; + return ( <> @@ -45,7 +50,7 @@ export function PanelHeader({ panel, error, isViewing, isEditing, data, alertSta error={error} />
- + {({ closeMenu, panelMenuOpen }) => { return ( diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuTrigger.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuTrigger.tsx index 6d12cb3d1f5..daf3666ef03 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuTrigger.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuTrigger.tsx @@ -9,9 +9,10 @@ interface PanelHeaderMenuTriggerApi { interface Props extends Omit, 'children'> { children: (props: PanelHeaderMenuTriggerApi) => ReactElement; + onOpenMenu?: () => void; } -export function PanelHeaderMenuTrigger({ children, ...divProps }: Props) { +export function PanelHeaderMenuTrigger({ children, onOpenMenu, ...divProps }: Props) { const clickCoordinates = useRef({ x: 0, y: 0 }); const [panelMenuOpen, setPanelMenuOpen] = useState(false); @@ -22,8 +23,11 @@ export function PanelHeaderMenuTrigger({ children, ...divProps }: Props) { } setPanelMenuOpen(!panelMenuOpen); + if (panelMenuOpen) { + onOpenMenu?.(); + } }, - [panelMenuOpen, setPanelMenuOpen] + [panelMenuOpen, setPanelMenuOpen, onOpenMenu] ); const onMouseDown = useCallback((event: MouseEvent) => { diff --git a/public/app/features/dashboard/dashgrid/PanelStateWrapper.tsx b/public/app/features/dashboard/dashgrid/PanelStateWrapper.tsx index a593580c61e..2b5be94b746 100644 --- a/public/app/features/dashboard/dashgrid/PanelStateWrapper.tsx +++ b/public/app/features/dashboard/dashgrid/PanelStateWrapper.tsx @@ -646,6 +646,7 @@ export class PanelStateWrapper extends PureComponent { hoverHeader={panelChromeProps.hasOverlayHeader()} displayMode={transparent ? 'transparent' : 'default'} onCancelQuery={panelChromeProps.onCancelQuery} + onOpenMenu={panelChromeProps.onOpenMenu} > {(innerWidth, innerHeight) => ( <> diff --git a/public/app/features/dashboard/utils/getPanelChromeProps.tsx b/public/app/features/dashboard/utils/getPanelChromeProps.tsx index 90cf4261878..32e4b6528d9 100644 --- a/public/app/features/dashboard/utils/getPanelChromeProps.tsx +++ b/public/app/features/dashboard/utils/getPanelChromeProps.tsx @@ -106,6 +106,10 @@ export function getPanelChromeProps(props: CommonProps) { const title = props.panel.getDisplayTitle(); + const onOpenMenu = () => { + reportInteraction('dashboards_panelheader_menu', { item: 'menu' }); + }; + return { hasOverlayHeader, onShowPanelDescription, @@ -118,5 +122,6 @@ export function getPanelChromeProps(props: CommonProps) { dragClass, title, titleItems, + onOpenMenu, }; } diff --git a/public/app/features/dashboard/utils/getPanelMenu.ts b/public/app/features/dashboard/utils/getPanelMenu.ts index 405e971c6a0..84ca6f1ae10 100644 --- a/public/app/features/dashboard/utils/getPanelMenu.ts +++ b/public/app/features/dashboard/utils/getPanelMenu.ts @@ -40,7 +40,7 @@ export function getPanelMenu( locationService.partial({ viewPanel: panel.id, }); - reportInteraction('dashboards_panelheader_view_clicked'); + reportInteraction('dashboards_panelheader_menu', { item: 'view' }); }; const onEditPanel = (event: React.MouseEvent) => { @@ -48,25 +48,26 @@ export function getPanelMenu( locationService.partial({ editPanel: panel.id, }); - reportInteraction('dashboards_panelheader_edit_clicked'); + + reportInteraction('dashboards_panelheader_menu', { item: 'edit' }); }; const onSharePanel = (event: React.MouseEvent) => { event.preventDefault(); sharePanel(dashboard, panel); - reportInteraction('dashboards_panelheader_share_clicked'); + reportInteraction('dashboards_panelheader_menu', { item: 'share' }); }; const onAddLibraryPanel = (event: React.MouseEvent) => { event.preventDefault(); addLibraryPanel(dashboard, panel); - reportInteraction('dashboards_panelheader_createlibrarypanel_clicked'); + reportInteraction('dashboards_panelheader_menu', { item: 'createLibraryPanel' }); }; const onUnlinkLibraryPanel = (event: React.MouseEvent) => { event.preventDefault(); unlinkLibraryPanel(panel); - reportInteraction('dashboards_panelheader_unlinklibrarypanel_clicked'); + reportInteraction('dashboards_panelheader_menu', { item: 'unlinkLibraryPanel' }); }; const onInspectPanel = (tab?: InspectTab) => { @@ -74,7 +75,7 @@ export function getPanelMenu( inspect: panel.id, inspectTab: tab, }); - reportInteraction('dashboards_panelheader_inspect_clicked', { tab: tab ?? InspectTab.Data }); + reportInteraction('dashboards_panelheader_menu', { item: 'inspect', tab: tab ?? InspectTab.Data }); }; const onMore = (event: React.MouseEvent) => { @@ -84,19 +85,19 @@ export function getPanelMenu( const onDuplicatePanel = (event: React.MouseEvent) => { event.preventDefault(); duplicatePanel(dashboard, panel); - reportInteraction('dashboards_panelheader_duplicate_clicked'); + reportInteraction('dashboards_panelheader_menu', { item: 'duplicate' }); }; const onCopyPanel = (event: React.MouseEvent) => { event.preventDefault(); copyPanel(panel); - reportInteraction('dashboards_panelheader_copy_clicked'); + reportInteraction('dashboards_panelheader_menu', { item: 'copy' }); }; const onRemovePanel = (event: React.MouseEvent) => { event.preventDefault(); removePanel(dashboard, panel, true); - reportInteraction('dashboards_panelheader_remove_clicked'); + reportInteraction('dashboards_panelheader_menu', { item: 'remove' }); }; const onNavigateToExplore = (event: React.MouseEvent) => { @@ -104,13 +105,13 @@ export function getPanelMenu( const openInNewWindow = event.ctrlKey || event.metaKey ? (url: string) => window.open(`${config.appSubUrl}${url}`) : undefined; store.dispatch(navigateToExplore(panel, { getDataSourceSrv, getTimeSrv, getExploreUrl, openInNewWindow }) as any); - reportInteraction('dashboards_panelheader_explore_clicked'); + reportInteraction('dashboards_panelheader_menu', { item: 'explore' }); }; const onToggleLegend = (event: React.MouseEvent) => { event.preventDefault(); toggleLegend(panel); - reportInteraction('dashboards_panelheader_togglelegend_clicked'); + reportInteraction('dashboards_panelheader_menu', { item: 'toggleLegend' }); }; const menu: PanelMenuItem[] = [];