mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
[Chore] Refactor Panel Menu Rudderstack events and add missing instrumentation (#66850)
This commit is contained in:
parent
236862a07c
commit
b4a7427f31
@ -15,14 +15,13 @@ interface Props {
|
|||||||
title?: string;
|
title?: string;
|
||||||
offset?: number;
|
offset?: number;
|
||||||
dragClass?: string;
|
dragClass?: string;
|
||||||
|
onOpenMenu?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectors = e2eSelectors.components.Panels.Panel.HoverWidget;
|
export function HoverWidget({ menu, title, dragClass, children, offset = -32, onOpenMenu }: Props) {
|
||||||
|
|
||||||
export function HoverWidget({ menu, title, dragClass, children, offset = -32 }: Props) {
|
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const draggableRef = useRef<HTMLDivElement>(null);
|
const draggableRef = useRef<HTMLDivElement>(null);
|
||||||
|
const selectors = e2eSelectors.components.Panels.Panel.HoverWidget;
|
||||||
// Capture the pointer to keep the widget visible while dragging
|
// Capture the pointer to keep the widget visible while dragging
|
||||||
const onPointerDown = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
|
const onPointerDown = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
|
||||||
draggableRef.current?.setPointerCapture(e.pointerId);
|
draggableRef.current?.setPointerCapture(e.pointerId);
|
||||||
@ -64,6 +63,7 @@ export function HoverWidget({ menu, title, dragClass, children, offset = -32 }:
|
|||||||
placement="bottom"
|
placement="bottom"
|
||||||
menuButtonClass={styles.menuButton}
|
menuButtonClass={styles.menuButton}
|
||||||
onVisibleChange={setMenuOpen}
|
onVisibleChange={setMenuOpen}
|
||||||
|
onOpenMenu={onOpenMenu}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,6 +53,10 @@ export interface PanelChromeProps {
|
|||||||
actions?: ReactNode;
|
actions?: ReactNode;
|
||||||
displayMode?: 'default' | 'transparent';
|
displayMode?: 'default' | 'transparent';
|
||||||
onCancelQuery?: () => void;
|
onCancelQuery?: () => void;
|
||||||
|
/**
|
||||||
|
* callback when opening the panel menu
|
||||||
|
*/
|
||||||
|
onOpenMenu?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,6 +87,7 @@ export function PanelChrome({
|
|||||||
leftItems,
|
leftItems,
|
||||||
actions,
|
actions,
|
||||||
onCancelQuery,
|
onCancelQuery,
|
||||||
|
onOpenMenu,
|
||||||
}: PanelChromeProps) {
|
}: PanelChromeProps) {
|
||||||
const theme = useTheme2();
|
const theme = useTheme2();
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
@ -159,7 +164,13 @@ export function PanelChrome({
|
|||||||
|
|
||||||
{hoverHeader && !isTouchDevice && (
|
{hoverHeader && !isTouchDevice && (
|
||||||
<>
|
<>
|
||||||
<HoverWidget menu={menu} title={title} offset={hoverHeaderOffset} dragClass={dragClass}>
|
<HoverWidget
|
||||||
|
menu={menu}
|
||||||
|
title={title}
|
||||||
|
offset={hoverHeaderOffset}
|
||||||
|
dragClass={dragClass}
|
||||||
|
onOpenMenu={onOpenMenu}
|
||||||
|
>
|
||||||
{headerContent}
|
{headerContent}
|
||||||
</HoverWidget>
|
</HoverWidget>
|
||||||
|
|
||||||
@ -192,6 +203,7 @@ export function PanelChrome({
|
|||||||
dragClassCancel,
|
dragClassCancel,
|
||||||
showOnHoverClass
|
showOnHoverClass
|
||||||
)}
|
)}
|
||||||
|
onOpenMenu={onOpenMenu}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { cx } from '@emotion/css';
|
import { cx } from '@emotion/css';
|
||||||
import React, { ReactElement } from 'react';
|
import React, { ReactElement, useCallback } from 'react';
|
||||||
|
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ interface PanelMenuProps {
|
|||||||
placement?: TooltipPlacement;
|
placement?: TooltipPlacement;
|
||||||
offset?: [number, number];
|
offset?: [number, number];
|
||||||
onVisibleChange?: (state: boolean) => void;
|
onVisibleChange?: (state: boolean) => void;
|
||||||
|
onOpenMenu?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PanelMenu({
|
export function PanelMenu({
|
||||||
@ -25,10 +26,22 @@ export function PanelMenu({
|
|||||||
dragClassCancel,
|
dragClassCancel,
|
||||||
menuButtonClass,
|
menuButtonClass,
|
||||||
onVisibleChange,
|
onVisibleChange,
|
||||||
|
onOpenMenu,
|
||||||
}: PanelMenuProps) {
|
}: PanelMenuProps) {
|
||||||
const testId = title ? selectors.components.Panels.Panel.menu(title) : `panel-menu-button`;
|
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 (
|
return (
|
||||||
<Dropdown overlay={menu} placement={placement} offset={offset} onVisibleChange={onVisibleChange}>
|
<Dropdown overlay={menu} placement={placement} offset={offset} onVisibleChange={handleVisibility}>
|
||||||
<ToolbarButton
|
<ToolbarButton
|
||||||
aria-label={`Menu for panel with ${title ? `title ${title}` : 'no title'}`}
|
aria-label={`Menu for panel with ${title ? `title ${title}` : 'no title'}`}
|
||||||
title="Menu"
|
title="Menu"
|
||||||
|
@ -229,6 +229,7 @@ export class PanelChromeAngularUnconnected extends PureComponent<Props, State> {
|
|||||||
hoverHeader={panelChromeProps.hasOverlayHeader()}
|
hoverHeader={panelChromeProps.hasOverlayHeader()}
|
||||||
displayMode={transparent ? 'transparent' : 'default'}
|
displayMode={transparent ? 'transparent' : 'default'}
|
||||||
onCancelQuery={panelChromeProps.onCancelQuery}
|
onCancelQuery={panelChromeProps.onCancelQuery}
|
||||||
|
onOpenMenu={panelChromeProps.onOpenMenu}
|
||||||
>
|
>
|
||||||
{() => <div ref={(element) => (this.element = element)} className="panel-height-helper" />}
|
{() => <div ref={(element) => (this.element = element)} className="panel-height-helper" />}
|
||||||
</PanelChrome>
|
</PanelChrome>
|
||||||
|
@ -3,6 +3,7 @@ import React from 'react';
|
|||||||
|
|
||||||
import { DataLink, GrafanaTheme2, PanelData } from '@grafana/data';
|
import { DataLink, GrafanaTheme2, PanelData } from '@grafana/data';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
import { reportInteraction } from '@grafana/runtime';
|
||||||
import { Icon, useStyles2, ClickOutsideWrapper } from '@grafana/ui';
|
import { Icon, useStyles2, ClickOutsideWrapper } from '@grafana/ui';
|
||||||
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
|
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
|
||||||
import { PanelModel } from 'app/features/dashboard/state/PanelModel';
|
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 className = cx('panel-header', !(isViewing || isEditing) ? 'grid-drag-handle' : '');
|
||||||
const styles = useStyles2(panelStyles);
|
const styles = useStyles2(panelStyles);
|
||||||
|
|
||||||
|
const onOpenMenu = () => {
|
||||||
|
reportInteraction('dashboards_panelheader_menu', { item: 'menu' });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PanelHeaderLoadingIndicator state={data.state} onClick={onCancelQuery} />
|
<PanelHeaderLoadingIndicator state={data.state} onClick={onCancelQuery} />
|
||||||
@ -45,7 +50,7 @@ export function PanelHeader({ panel, error, isViewing, isEditing, data, alertSta
|
|||||||
error={error}
|
error={error}
|
||||||
/>
|
/>
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<PanelHeaderMenuTrigger data-testid={selectors.components.Panels.Panel.title(title)}>
|
<PanelHeaderMenuTrigger data-testid={selectors.components.Panels.Panel.title(title)} onOpenMenu={onOpenMenu}>
|
||||||
{({ closeMenu, panelMenuOpen }) => {
|
{({ closeMenu, panelMenuOpen }) => {
|
||||||
return (
|
return (
|
||||||
<ClickOutsideWrapper onClick={closeMenu} parent={document}>
|
<ClickOutsideWrapper onClick={closeMenu} parent={document}>
|
||||||
|
@ -9,9 +9,10 @@ interface PanelHeaderMenuTriggerApi {
|
|||||||
|
|
||||||
interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
||||||
children: (props: PanelHeaderMenuTriggerApi) => ReactElement;
|
children: (props: PanelHeaderMenuTriggerApi) => ReactElement;
|
||||||
|
onOpenMenu?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PanelHeaderMenuTrigger({ children, ...divProps }: Props) {
|
export function PanelHeaderMenuTrigger({ children, onOpenMenu, ...divProps }: Props) {
|
||||||
const clickCoordinates = useRef<CartesianCoords2D>({ x: 0, y: 0 });
|
const clickCoordinates = useRef<CartesianCoords2D>({ x: 0, y: 0 });
|
||||||
const [panelMenuOpen, setPanelMenuOpen] = useState<boolean>(false);
|
const [panelMenuOpen, setPanelMenuOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
@ -22,8 +23,11 @@ export function PanelHeaderMenuTrigger({ children, ...divProps }: Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setPanelMenuOpen(!panelMenuOpen);
|
setPanelMenuOpen(!panelMenuOpen);
|
||||||
|
if (panelMenuOpen) {
|
||||||
|
onOpenMenu?.();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[panelMenuOpen, setPanelMenuOpen]
|
[panelMenuOpen, setPanelMenuOpen, onOpenMenu]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onMouseDown = useCallback((event: MouseEvent<HTMLDivElement>) => {
|
const onMouseDown = useCallback((event: MouseEvent<HTMLDivElement>) => {
|
||||||
|
@ -646,6 +646,7 @@ export class PanelStateWrapper extends PureComponent<Props, State> {
|
|||||||
hoverHeader={panelChromeProps.hasOverlayHeader()}
|
hoverHeader={panelChromeProps.hasOverlayHeader()}
|
||||||
displayMode={transparent ? 'transparent' : 'default'}
|
displayMode={transparent ? 'transparent' : 'default'}
|
||||||
onCancelQuery={panelChromeProps.onCancelQuery}
|
onCancelQuery={panelChromeProps.onCancelQuery}
|
||||||
|
onOpenMenu={panelChromeProps.onOpenMenu}
|
||||||
>
|
>
|
||||||
{(innerWidth, innerHeight) => (
|
{(innerWidth, innerHeight) => (
|
||||||
<>
|
<>
|
||||||
|
@ -106,6 +106,10 @@ export function getPanelChromeProps(props: CommonProps) {
|
|||||||
|
|
||||||
const title = props.panel.getDisplayTitle();
|
const title = props.panel.getDisplayTitle();
|
||||||
|
|
||||||
|
const onOpenMenu = () => {
|
||||||
|
reportInteraction('dashboards_panelheader_menu', { item: 'menu' });
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hasOverlayHeader,
|
hasOverlayHeader,
|
||||||
onShowPanelDescription,
|
onShowPanelDescription,
|
||||||
@ -118,5 +122,6 @@ export function getPanelChromeProps(props: CommonProps) {
|
|||||||
dragClass,
|
dragClass,
|
||||||
title,
|
title,
|
||||||
titleItems,
|
titleItems,
|
||||||
|
onOpenMenu,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ export function getPanelMenu(
|
|||||||
locationService.partial({
|
locationService.partial({
|
||||||
viewPanel: panel.id,
|
viewPanel: panel.id,
|
||||||
});
|
});
|
||||||
reportInteraction('dashboards_panelheader_view_clicked');
|
reportInteraction('dashboards_panelheader_menu', { item: 'view' });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onEditPanel = (event: React.MouseEvent<any>) => {
|
const onEditPanel = (event: React.MouseEvent<any>) => {
|
||||||
@ -48,25 +48,26 @@ export function getPanelMenu(
|
|||||||
locationService.partial({
|
locationService.partial({
|
||||||
editPanel: panel.id,
|
editPanel: panel.id,
|
||||||
});
|
});
|
||||||
reportInteraction('dashboards_panelheader_edit_clicked');
|
|
||||||
|
reportInteraction('dashboards_panelheader_menu', { item: 'edit' });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSharePanel = (event: React.MouseEvent<any>) => {
|
const onSharePanel = (event: React.MouseEvent<any>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
sharePanel(dashboard, panel);
|
sharePanel(dashboard, panel);
|
||||||
reportInteraction('dashboards_panelheader_share_clicked');
|
reportInteraction('dashboards_panelheader_menu', { item: 'share' });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onAddLibraryPanel = (event: React.MouseEvent<any>) => {
|
const onAddLibraryPanel = (event: React.MouseEvent<any>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
addLibraryPanel(dashboard, panel);
|
addLibraryPanel(dashboard, panel);
|
||||||
reportInteraction('dashboards_panelheader_createlibrarypanel_clicked');
|
reportInteraction('dashboards_panelheader_menu', { item: 'createLibraryPanel' });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onUnlinkLibraryPanel = (event: React.MouseEvent<any>) => {
|
const onUnlinkLibraryPanel = (event: React.MouseEvent<any>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
unlinkLibraryPanel(panel);
|
unlinkLibraryPanel(panel);
|
||||||
reportInteraction('dashboards_panelheader_unlinklibrarypanel_clicked');
|
reportInteraction('dashboards_panelheader_menu', { item: 'unlinkLibraryPanel' });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onInspectPanel = (tab?: InspectTab) => {
|
const onInspectPanel = (tab?: InspectTab) => {
|
||||||
@ -74,7 +75,7 @@ export function getPanelMenu(
|
|||||||
inspect: panel.id,
|
inspect: panel.id,
|
||||||
inspectTab: tab,
|
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<any>) => {
|
const onMore = (event: React.MouseEvent<any>) => {
|
||||||
@ -84,19 +85,19 @@ export function getPanelMenu(
|
|||||||
const onDuplicatePanel = (event: React.MouseEvent<any>) => {
|
const onDuplicatePanel = (event: React.MouseEvent<any>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
duplicatePanel(dashboard, panel);
|
duplicatePanel(dashboard, panel);
|
||||||
reportInteraction('dashboards_panelheader_duplicate_clicked');
|
reportInteraction('dashboards_panelheader_menu', { item: 'duplicate' });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCopyPanel = (event: React.MouseEvent<any>) => {
|
const onCopyPanel = (event: React.MouseEvent<any>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
copyPanel(panel);
|
copyPanel(panel);
|
||||||
reportInteraction('dashboards_panelheader_copy_clicked');
|
reportInteraction('dashboards_panelheader_menu', { item: 'copy' });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onRemovePanel = (event: React.MouseEvent<any>) => {
|
const onRemovePanel = (event: React.MouseEvent<any>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
removePanel(dashboard, panel, true);
|
removePanel(dashboard, panel, true);
|
||||||
reportInteraction('dashboards_panelheader_remove_clicked');
|
reportInteraction('dashboards_panelheader_menu', { item: 'remove' });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onNavigateToExplore = (event: React.MouseEvent<any>) => {
|
const onNavigateToExplore = (event: React.MouseEvent<any>) => {
|
||||||
@ -104,13 +105,13 @@ export function getPanelMenu(
|
|||||||
const openInNewWindow =
|
const openInNewWindow =
|
||||||
event.ctrlKey || event.metaKey ? (url: string) => window.open(`${config.appSubUrl}${url}`) : undefined;
|
event.ctrlKey || event.metaKey ? (url: string) => window.open(`${config.appSubUrl}${url}`) : undefined;
|
||||||
store.dispatch(navigateToExplore(panel, { getDataSourceSrv, getTimeSrv, getExploreUrl, openInNewWindow }) as any);
|
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) => {
|
const onToggleLegend = (event: React.MouseEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
toggleLegend(panel);
|
toggleLegend(panel);
|
||||||
reportInteraction('dashboards_panelheader_togglelegend_clicked');
|
reportInteraction('dashboards_panelheader_menu', { item: 'toggleLegend' });
|
||||||
};
|
};
|
||||||
|
|
||||||
const menu: PanelMenuItem[] = [];
|
const menu: PanelMenuItem[] = [];
|
||||||
|
Loading…
Reference in New Issue
Block a user