mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboard interactions reporting refactor, DashboardScene interactions handling (#79078)
* Refactor dashboard interactions tracking * Local scenes tmp * Use latest scenes * Review refactor * Fix enterprise tests * Reporting parity for panel description
This commit is contained in:
parent
a9a18a4b6d
commit
bfde6f2c8a
@ -256,7 +256,7 @@
|
|||||||
"@grafana/lezer-traceql": "0.0.11",
|
"@grafana/lezer-traceql": "0.0.11",
|
||||||
"@grafana/monaco-logql": "^0.0.7",
|
"@grafana/monaco-logql": "^0.0.7",
|
||||||
"@grafana/runtime": "workspace:*",
|
"@grafana/runtime": "workspace:*",
|
||||||
"@grafana/scenes": "^1.26.0",
|
"@grafana/scenes": "1.27.0",
|
||||||
"@grafana/schema": "workspace:*",
|
"@grafana/schema": "workspace:*",
|
||||||
"@grafana/ui": "workspace:*",
|
"@grafana/ui": "workspace:*",
|
||||||
"@kusto/monaco-kusto": "^7.4.0",
|
"@kusto/monaco-kusto": "^7.4.0",
|
||||||
|
@ -13,14 +13,25 @@ interface LibraryVizPanelState extends SceneObjectState {
|
|||||||
title: string;
|
title: string;
|
||||||
uid: string;
|
uid: string;
|
||||||
name: string;
|
name: string;
|
||||||
panel?: VizPanel;
|
panel: VizPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LibraryVizPanel extends SceneObjectBase<LibraryVizPanelState> {
|
export class LibraryVizPanel extends SceneObjectBase<LibraryVizPanelState> {
|
||||||
static Component = LibraryPanelRenderer;
|
static Component = LibraryPanelRenderer;
|
||||||
|
|
||||||
constructor({ uid, title, key, name }: Pick<LibraryVizPanelState, 'uid' | 'title' | 'key' | 'name'>) {
|
constructor({ uid, title, key, name }: Pick<LibraryVizPanelState, 'uid' | 'title' | 'key' | 'name'>) {
|
||||||
super({ uid, title, key, name });
|
super({
|
||||||
|
uid,
|
||||||
|
title,
|
||||||
|
key,
|
||||||
|
name,
|
||||||
|
panel: new VizPanel({
|
||||||
|
title,
|
||||||
|
menu: new VizPanelMenu({
|
||||||
|
$behaviors: [panelMenuBehavior],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
this.addActivationHandler(this._onActivate);
|
this.addActivationHandler(this._onActivate);
|
||||||
}
|
}
|
||||||
@ -30,8 +41,7 @@ export class LibraryVizPanel extends SceneObjectBase<LibraryVizPanelState> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private async loadLibraryPanelFromPanelModel() {
|
private async loadLibraryPanelFromPanelModel() {
|
||||||
const { title } = this.state;
|
const vizPanel = this.state.panel;
|
||||||
let vizPanel = new VizPanel({ title });
|
|
||||||
try {
|
try {
|
||||||
const libPanel = await getLibraryPanel(this.state.uid, true);
|
const libPanel = await getLibraryPanel(this.state.uid, true);
|
||||||
const libPanelModel = new PanelModel(libPanel.model);
|
const libPanelModel = new PanelModel(libPanel.model);
|
||||||
@ -40,10 +50,8 @@ export class LibraryVizPanel extends SceneObjectBase<LibraryVizPanelState> {
|
|||||||
fieldConfig: libPanelModel.fieldConfig,
|
fieldConfig: libPanelModel.fieldConfig,
|
||||||
pluginVersion: libPanelModel.pluginVersion,
|
pluginVersion: libPanelModel.pluginVersion,
|
||||||
displayMode: libPanelModel.transparent ? 'transparent' : undefined,
|
displayMode: libPanelModel.transparent ? 'transparent' : undefined,
|
||||||
|
description: libPanelModel.description,
|
||||||
$data: createPanelDataProvider(libPanelModel),
|
$data: createPanelDataProvider(libPanelModel),
|
||||||
menu: new VizPanelMenu({
|
|
||||||
$behaviors: [panelMenuBehavior],
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
vizPanel.setState({
|
vizPanel.setState({
|
||||||
|
@ -8,6 +8,7 @@ import { t } from 'app/core/internationalization';
|
|||||||
import { DashNavButton } from 'app/features/dashboard/components/DashNav/DashNavButton';
|
import { DashNavButton } from 'app/features/dashboard/components/DashNav/DashNavButton';
|
||||||
|
|
||||||
import { ShareModal } from '../sharing/ShareModal';
|
import { ShareModal } from '../sharing/ShareModal';
|
||||||
|
import { DashboardInteractions } from '../utils/interactions';
|
||||||
|
|
||||||
import { DashboardScene } from './DashboardScene';
|
import { DashboardScene } from './DashboardScene';
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ export const NavToolbarActions = React.memo<Props>(({ dashboard }) => {
|
|||||||
iconType={meta.isStarred ? 'mono' : 'default'}
|
iconType={meta.isStarred ? 'mono' : 'default'}
|
||||||
iconSize="lg"
|
iconSize="lg"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
DashboardInteractions.toolbarFavoritesClick();
|
||||||
dashboard.onStarDashboard();
|
dashboard.onStarDashboard();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -45,6 +47,7 @@ export const NavToolbarActions = React.memo<Props>(({ dashboard }) => {
|
|||||||
icon="share-alt"
|
icon="share-alt"
|
||||||
iconSize="lg"
|
iconSize="lg"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
DashboardInteractions.toolbarShareClick();
|
||||||
dashboard.showModal(new ShareModal({ dashboardRef: dashboard.getRef() }));
|
dashboard.showModal(new ShareModal({ dashboardRef: dashboard.getRef() }));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -65,7 +68,9 @@ export const NavToolbarActions = React.memo<Props>(({ dashboard }) => {
|
|||||||
if (viewPanelScene) {
|
if (viewPanelScene) {
|
||||||
toolbarActions.push(
|
toolbarActions.push(
|
||||||
<Button
|
<Button
|
||||||
onClick={() => locationService.partial({ viewPanel: null })}
|
onClick={() => {
|
||||||
|
locationService.partial({ viewPanel: null });
|
||||||
|
}}
|
||||||
tooltip=""
|
tooltip=""
|
||||||
key="back"
|
key="back"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
@ -82,7 +87,9 @@ export const NavToolbarActions = React.memo<Props>(({ dashboard }) => {
|
|||||||
if (dashboard.canEditDashboard()) {
|
if (dashboard.canEditDashboard()) {
|
||||||
toolbarActions.push(
|
toolbarActions.push(
|
||||||
<Button
|
<Button
|
||||||
onClick={dashboard.onEnterEditMode}
|
onClick={() => {
|
||||||
|
dashboard.onEnterEditMode();
|
||||||
|
}}
|
||||||
tooltip="Enter edit mode"
|
tooltip="Enter edit mode"
|
||||||
key="edit"
|
key="edit"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
@ -96,17 +103,40 @@ export const NavToolbarActions = React.memo<Props>(({ dashboard }) => {
|
|||||||
} else {
|
} else {
|
||||||
if (dashboard.canEditDashboard()) {
|
if (dashboard.canEditDashboard()) {
|
||||||
toolbarActions.push(
|
toolbarActions.push(
|
||||||
<Button onClick={dashboard.onSave} tooltip="Save as copy" fill="text" key="save-as">
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
dashboard.onSave();
|
||||||
|
}}
|
||||||
|
tooltip="Save as copy"
|
||||||
|
fill="text"
|
||||||
|
key="save-as"
|
||||||
|
>
|
||||||
Save as
|
Save as
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
toolbarActions.push(
|
toolbarActions.push(
|
||||||
<Button onClick={dashboard.onDiscard} tooltip="Discard changes" fill="text" key="discard" variant="destructive">
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
dashboard.onDiscard();
|
||||||
|
}}
|
||||||
|
tooltip="Discard changes"
|
||||||
|
fill="text"
|
||||||
|
key="discard"
|
||||||
|
variant="destructive"
|
||||||
|
>
|
||||||
Discard
|
Discard
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
toolbarActions.push(
|
toolbarActions.push(
|
||||||
<Button onClick={dashboard.onSave} tooltip="Save changes" key="save" disabled={!isDirty}>
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
DashboardInteractions.toolbarSaveClick();
|
||||||
|
dashboard.onSave();
|
||||||
|
}}
|
||||||
|
tooltip="Save changes"
|
||||||
|
key="save"
|
||||||
|
disabled={!isDirty}
|
||||||
|
>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
PluginExtensionPoints,
|
PluginExtensionPoints,
|
||||||
getTimeZone,
|
getTimeZone,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { config, getPluginLinkExtensions, locationService, reportInteraction } from '@grafana/runtime';
|
import { config, getPluginLinkExtensions, locationService } from '@grafana/runtime';
|
||||||
import {
|
import {
|
||||||
LocalValueVariable,
|
LocalValueVariable,
|
||||||
SceneDataTransformer,
|
SceneDataTransformer,
|
||||||
@ -24,6 +24,7 @@ import { createExtensionSubMenu } from 'app/features/plugins/extensions/utils';
|
|||||||
import { addDataTrailPanelAction } from 'app/features/trails/dashboardIntegration';
|
import { addDataTrailPanelAction } from 'app/features/trails/dashboardIntegration';
|
||||||
|
|
||||||
import { ShareModal } from '../sharing/ShareModal';
|
import { ShareModal } from '../sharing/ShareModal';
|
||||||
|
import { DashboardInteractions } from '../utils/interactions';
|
||||||
import { getDashboardUrl, getInspectUrl, getViewPanelUrl, tryGetExploreUrlForPanel } from '../utils/urlBuilders';
|
import { getDashboardUrl, getInspectUrl, getViewPanelUrl, tryGetExploreUrlForPanel } from '../utils/urlBuilders';
|
||||||
import { getPanelIdForVizPanel } from '../utils/utils';
|
import { getPanelIdForVizPanel } from '../utils/utils';
|
||||||
|
|
||||||
@ -54,7 +55,7 @@ export function panelMenuBehavior(menu: VizPanelMenu) {
|
|||||||
text: t('panel.header-menu.view', `View`),
|
text: t('panel.header-menu.view', `View`),
|
||||||
iconClassName: 'eye',
|
iconClassName: 'eye',
|
||||||
shortcut: 'v',
|
shortcut: 'v',
|
||||||
onClick: () => reportInteraction('dashboards_panelheader_menu', { item: 'view' }),
|
onClick: () => DashboardInteractions.panelMenuItemClicked('view'),
|
||||||
href: getViewPanelUrl(panel),
|
href: getViewPanelUrl(panel),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -65,7 +66,7 @@ export function panelMenuBehavior(menu: VizPanelMenu) {
|
|||||||
text: t('panel.header-menu.edit', `Edit`),
|
text: t('panel.header-menu.edit', `Edit`),
|
||||||
iconClassName: 'eye',
|
iconClassName: 'eye',
|
||||||
shortcut: 'e',
|
shortcut: 'e',
|
||||||
onClick: () => reportInteraction('dashboards_panelheader_menu', { item: 'edit' }),
|
onClick: () => () => DashboardInteractions.panelMenuItemClicked('edit'),
|
||||||
href: getDashboardUrl({
|
href: getDashboardUrl({
|
||||||
uid: dashboard.state.uid,
|
uid: dashboard.state.uid,
|
||||||
subPath: `/panel-edit/${panelId}`,
|
subPath: `/panel-edit/${panelId}`,
|
||||||
@ -79,7 +80,7 @@ export function panelMenuBehavior(menu: VizPanelMenu) {
|
|||||||
text: t('panel.header-menu.share', `Share`),
|
text: t('panel.header-menu.share', `Share`),
|
||||||
iconClassName: 'share-alt',
|
iconClassName: 'share-alt',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
reportInteraction('dashboards_panelheader_menu', { item: 'share' });
|
DashboardInteractions.panelMenuItemClicked('share');
|
||||||
dashboard.showModal(new ShareModal({ panelRef: panel.getRef(), dashboardRef: dashboard.getRef() }));
|
dashboard.showModal(new ShareModal({ panelRef: panel.getRef(), dashboardRef: dashboard.getRef() }));
|
||||||
},
|
},
|
||||||
shortcut: 'p s',
|
shortcut: 'p s',
|
||||||
@ -92,7 +93,7 @@ export function panelMenuBehavior(menu: VizPanelMenu) {
|
|||||||
text: t('panel.header-menu.create-library-panel', `Create library panel`),
|
text: t('panel.header-menu.create-library-panel', `Create library panel`),
|
||||||
iconClassName: 'share-alt',
|
iconClassName: 'share-alt',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
reportInteraction('dashboards_panelheader_menu', { item: 'createLibraryPanel' });
|
DashboardInteractions.panelMenuItemClicked('createLibraryPanel');
|
||||||
dashboard.showModal(
|
dashboard.showModal(
|
||||||
new ShareModal({
|
new ShareModal({
|
||||||
panelRef: panel.getRef(),
|
panelRef: panel.getRef(),
|
||||||
@ -115,7 +116,7 @@ export function panelMenuBehavior(menu: VizPanelMenu) {
|
|||||||
text: t('panel.header-menu.explore', `Explore`),
|
text: t('panel.header-menu.explore', `Explore`),
|
||||||
iconClassName: 'compass',
|
iconClassName: 'compass',
|
||||||
shortcut: 'p x',
|
shortcut: 'p x',
|
||||||
onClick: () => reportInteraction('dashboards_panelheader_menu', { item: 'explore' }),
|
onClick: () => DashboardInteractions.panelMenuItemClicked('explore'),
|
||||||
href: exploreUrl,
|
href: exploreUrl,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -127,7 +128,7 @@ export function panelMenuBehavior(menu: VizPanelMenu) {
|
|||||||
onClick: (e) => {
|
onClick: (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
locationService.partial({ inspect: panel.state.key, inspectTab: InspectTab.Data });
|
locationService.partial({ inspect: panel.state.key, inspectTab: InspectTab.Data });
|
||||||
reportInteraction('dashboards_panelheader_menu', { item: 'inspect', tab: InspectTab.Data });
|
DashboardInteractions.panelMenuInspectClicked(InspectTab.Data);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -138,7 +139,7 @@ export function panelMenuBehavior(menu: VizPanelMenu) {
|
|||||||
onClick: (e) => {
|
onClick: (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
locationService.partial({ inspect: panel.state.key, inspectTab: InspectTab.Query });
|
locationService.partial({ inspect: panel.state.key, inspectTab: InspectTab.Query });
|
||||||
reportInteraction('dashboards_panelheader_menu', { item: 'inspect', tab: InspectTab.Query });
|
DashboardInteractions.panelMenuInspectClicked(InspectTab.Query);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -150,7 +151,7 @@ export function panelMenuBehavior(menu: VizPanelMenu) {
|
|||||||
onClick: (e) => {
|
onClick: (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
locationService.partial({ inspect: panel.state.key, inspectTab: InspectTab.JSON });
|
locationService.partial({ inspect: panel.state.key, inspectTab: InspectTab.JSON });
|
||||||
reportInteraction('dashboards_panelheader_menu', { item: 'inspect', tab: InspectTab.JSON });
|
DashboardInteractions.panelMenuInspectClicked(InspectTab.JSON);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -162,7 +163,7 @@ export function panelMenuBehavior(menu: VizPanelMenu) {
|
|||||||
onClick: (e) => {
|
onClick: (e) => {
|
||||||
if (!e.isDefaultPrevented()) {
|
if (!e.isDefaultPrevented()) {
|
||||||
locationService.partial({ inspect: panel.state.key, inspectTab: InspectTab.Data });
|
locationService.partial({ inspect: panel.state.key, inspectTab: InspectTab.Data });
|
||||||
reportInteraction('dashboards_panelheader_menu', { item: 'inspect', tab: InspectTab.Data });
|
DashboardInteractions.panelMenuInspectClicked(InspectTab.Data);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
subMenu: inspectSubMenu.length > 0 ? inspectSubMenu : undefined,
|
subMenu: inspectSubMenu.length > 0 ? inspectSubMenu : undefined,
|
||||||
@ -223,7 +224,7 @@ export function getPanelLinksBehavior(panel: PanelModel) {
|
|||||||
const links = panelLinks.map((panelLink) => ({
|
const links = panelLinks.map((panelLink) => ({
|
||||||
...panelLink,
|
...panelLink,
|
||||||
onClick: (e: any, origin: any) => {
|
onClick: (e: any, origin: any) => {
|
||||||
reportInteraction('dashboards_panelheader_datalink_clicked', { has_multiple_links: panelLinks.length > 1 });
|
DashboardInteractions.panelLinkClicked({ has_multiple_links: panelLinks.length > 1 });
|
||||||
panelLink.onClick?.(e, origin);
|
panelLink.onClick?.(e, origin);
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
@ -118,7 +118,7 @@ describe('transformSaveModelToScene', () => {
|
|||||||
|
|
||||||
const scene = createDashboardSceneFromDashboardModel(oldModel);
|
const scene = createDashboardSceneFromDashboardModel(oldModel);
|
||||||
|
|
||||||
expect(scene.state.$behaviors).toHaveLength(2);
|
expect(scene.state.$behaviors).toHaveLength(3);
|
||||||
expect(scene.state.$behaviors![1]).toBeInstanceOf(behaviors.CursorSync);
|
expect(scene.state.$behaviors![1]).toBeInstanceOf(behaviors.CursorSync);
|
||||||
expect((scene.state.$behaviors![1] as behaviors.CursorSync).state.sync).toEqual(DashboardCursorSync.Crosshair);
|
expect((scene.state.$behaviors![1] as behaviors.CursorSync).state.sync).toEqual(DashboardCursorSync.Crosshair);
|
||||||
});
|
});
|
||||||
|
@ -26,6 +26,7 @@ import {
|
|||||||
SceneDataLayerControls,
|
SceneDataLayerControls,
|
||||||
AdHocFilterSet,
|
AdHocFilterSet,
|
||||||
TextBoxVariable,
|
TextBoxVariable,
|
||||||
|
UserActionEvent,
|
||||||
} from '@grafana/scenes';
|
} from '@grafana/scenes';
|
||||||
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
||||||
import { DashboardDTO } from 'app/types';
|
import { DashboardDTO } from 'app/types';
|
||||||
@ -44,6 +45,7 @@ import { PanelTimeRange } from '../scene/PanelTimeRange';
|
|||||||
import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
|
import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
|
||||||
import { setDashboardPanelContext } from '../scene/setDashboardPanelContext';
|
import { setDashboardPanelContext } from '../scene/setDashboardPanelContext';
|
||||||
import { createPanelDataProvider } from '../utils/createPanelDataProvider';
|
import { createPanelDataProvider } from '../utils/createPanelDataProvider';
|
||||||
|
import { DashboardInteractions } from '../utils/interactions';
|
||||||
import {
|
import {
|
||||||
getCurrentValueForOldIntervalModel,
|
getCurrentValueForOldIntervalModel,
|
||||||
getIntervalsFromOldIntervalModel,
|
getIntervalsFromOldIntervalModel,
|
||||||
@ -223,7 +225,7 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DashboardScene({
|
const dashboardScene = new DashboardScene({
|
||||||
title: oldModel.title,
|
title: oldModel.title,
|
||||||
tags: oldModel.tags || [],
|
tags: oldModel.tags || [],
|
||||||
links: oldModel.links || [],
|
links: oldModel.links || [],
|
||||||
@ -249,6 +251,7 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel)
|
|||||||
new behaviors.CursorSync({
|
new behaviors.CursorSync({
|
||||||
sync: oldModel.graphTooltip,
|
sync: oldModel.graphTooltip,
|
||||||
}),
|
}),
|
||||||
|
registerPanelInteractionsReporter,
|
||||||
],
|
],
|
||||||
$data:
|
$data:
|
||||||
layers.length > 0
|
layers.length > 0
|
||||||
@ -272,6 +275,8 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel)
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return dashboardScene;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createSceneVariableFromVariableModel(variable: TypedVariableModel): SceneVariable {
|
export function createSceneVariableFromVariableModel(variable: TypedVariableModel): SceneVariable {
|
||||||
@ -368,13 +373,15 @@ export function buildGridItemForLibPanel(panel: PanelModel) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const body = new LibraryVizPanel({
|
||||||
|
title: panel.title,
|
||||||
|
uid: panel.libraryPanel.uid,
|
||||||
|
name: panel.libraryPanel.name,
|
||||||
|
key: getVizPanelKeyForPanelId(panel.id),
|
||||||
|
});
|
||||||
|
|
||||||
return new SceneGridItem({
|
return new SceneGridItem({
|
||||||
body: new LibraryVizPanel({
|
body,
|
||||||
title: panel.title,
|
|
||||||
uid: panel.libraryPanel.uid,
|
|
||||||
name: panel.libraryPanel.name,
|
|
||||||
key: getVizPanelKeyForPanelId(panel.id),
|
|
||||||
}),
|
|
||||||
y: panel.gridPos.y,
|
y: panel.gridPos.y,
|
||||||
x: panel.gridPos.x,
|
x: panel.gridPos.x,
|
||||||
width: panel.gridPos.w,
|
width: panel.gridPos.w,
|
||||||
@ -438,14 +445,51 @@ export function buildGridItemForPanel(panel: PanelModel): SceneGridItemLike {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const body = new VizPanel(vizPanelState);
|
||||||
|
|
||||||
return new SceneGridItem({
|
return new SceneGridItem({
|
||||||
key: `grid-item-${panel.id}`,
|
key: `grid-item-${panel.id}`,
|
||||||
x: panel.gridPos.x,
|
x: panel.gridPos.x,
|
||||||
y: panel.gridPos.y,
|
y: panel.gridPos.y,
|
||||||
width: panel.gridPos.w,
|
width: panel.gridPos.w,
|
||||||
height: panel.gridPos.h,
|
height: panel.gridPos.h,
|
||||||
body: new VizPanel(vizPanelState),
|
body,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const isAdhocVariable = (v: VariableModel): v is AdHocVariableModel => v.type === 'adhoc';
|
const isAdhocVariable = (v: VariableModel): v is AdHocVariableModel => v.type === 'adhoc';
|
||||||
|
|
||||||
|
const getLimitedDescriptionReporter = () => {
|
||||||
|
const reportedPanels: string[] = [];
|
||||||
|
|
||||||
|
return (key: string) => {
|
||||||
|
if (reportedPanels.includes(key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reportedPanels.push(key);
|
||||||
|
DashboardInteractions.panelDescriptionShown();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
function registerPanelInteractionsReporter(scene: DashboardScene) {
|
||||||
|
const descriptionReporter = getLimitedDescriptionReporter();
|
||||||
|
|
||||||
|
// Subscriptions set with subscribeToEvent are automatically unsubscribed when the scene deactivated
|
||||||
|
scene.subscribeToEvent(UserActionEvent, (e) => {
|
||||||
|
const { interaction } = e.payload;
|
||||||
|
switch (interaction) {
|
||||||
|
case 'panel-description-shown':
|
||||||
|
descriptionReporter(e.payload.origin.state.key || '');
|
||||||
|
break;
|
||||||
|
case 'panel-status-message-clicked':
|
||||||
|
DashboardInteractions.panelStatusMessageClicked();
|
||||||
|
break;
|
||||||
|
case 'panel-cancel-query-clicked':
|
||||||
|
DashboardInteractions.panelCancelQueryClicked();
|
||||||
|
break;
|
||||||
|
case 'panel-menu-shown':
|
||||||
|
DashboardInteractions.panelMenuShown();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -3,15 +3,16 @@ import React from 'react';
|
|||||||
import { useAsync } from 'react-use';
|
import { useAsync } from 'react-use';
|
||||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||||
|
|
||||||
import { reportInteraction } from '@grafana/runtime';
|
|
||||||
import { SceneComponentProps, SceneObjectBase, SceneObjectRef } from '@grafana/scenes';
|
import { SceneComponentProps, SceneObjectBase, SceneObjectRef } from '@grafana/scenes';
|
||||||
import { Button, ClipboardButton, CodeEditor, Field, Modal, Switch, VerticalGroup } from '@grafana/ui';
|
import { Button, ClipboardButton, CodeEditor, Field, Modal, Switch, VerticalGroup } from '@grafana/ui';
|
||||||
import { t, Trans } from 'app/core/internationalization';
|
import { t, Trans } from 'app/core/internationalization';
|
||||||
import { DashboardExporter } from 'app/features/dashboard/components/DashExportModal';
|
import { DashboardExporter } from 'app/features/dashboard/components/DashExportModal';
|
||||||
|
import { shareDashboardType } from 'app/features/dashboard/components/ShareModal/utils';
|
||||||
import { DashboardModel } from 'app/features/dashboard/state';
|
import { DashboardModel } from 'app/features/dashboard/state';
|
||||||
|
|
||||||
import { DashboardScene } from '../scene/DashboardScene';
|
import { DashboardScene } from '../scene/DashboardScene';
|
||||||
import { transformSceneToSaveModel } from '../serialization/transformSceneToSaveModel';
|
import { transformSceneToSaveModel } from '../serialization/transformSceneToSaveModel';
|
||||||
|
import { DashboardInteractions } from '../utils/interactions';
|
||||||
|
|
||||||
import { SceneShareTabState } from './types';
|
import { SceneShareTabState } from './types';
|
||||||
|
|
||||||
@ -24,7 +25,7 @@ interface ShareExportTabState extends SceneShareTabState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ShareExportTab extends SceneObjectBase<ShareExportTabState> {
|
export class ShareExportTab extends SceneObjectBase<ShareExportTabState> {
|
||||||
public tabId = 'Export';
|
public tabId = shareDashboardType.export;
|
||||||
static Component = ShareExportTabRenderer;
|
static Component = ShareExportTabRenderer;
|
||||||
|
|
||||||
private _exporter = new DashboardExporter();
|
private _exporter = new DashboardExporter();
|
||||||
@ -83,7 +84,9 @@ export class ShareExportTab extends SceneObjectBase<ShareExportTabState> {
|
|||||||
title = dashboardJson.title;
|
title = dashboardJson.title;
|
||||||
}
|
}
|
||||||
saveAs(blob, `${title}-${time}.json`);
|
saveAs(blob, `${title}-${time}.json`);
|
||||||
reportInteraction('dashboards_sharing_export_download_json_clicked', { externally: isSharingExternally });
|
DashboardInteractions.exportDownloadJsonClicked({
|
||||||
|
externally: isSharingExternally,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import React from 'react';
|
|||||||
import { SceneComponentProps, SceneGridItem, SceneObjectBase, SceneObjectRef, VizPanel } from '@grafana/scenes';
|
import { SceneComponentProps, SceneGridItem, SceneObjectBase, SceneObjectRef, VizPanel } from '@grafana/scenes';
|
||||||
import { t } from 'app/core/internationalization';
|
import { t } from 'app/core/internationalization';
|
||||||
import { ShareLibraryPanel } from 'app/features/dashboard/components/ShareModal/ShareLibraryPanel';
|
import { ShareLibraryPanel } from 'app/features/dashboard/components/ShareModal/ShareLibraryPanel';
|
||||||
|
import { shareDashboardType } from 'app/features/dashboard/components/ShareModal/utils';
|
||||||
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
||||||
|
|
||||||
import { DashboardScene } from '../scene/DashboardScene';
|
import { DashboardScene } from '../scene/DashboardScene';
|
||||||
@ -17,7 +18,7 @@ export interface ShareLibraryPanelTabState extends SceneShareTabState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ShareLibraryPanelTab extends SceneObjectBase<ShareLibraryPanelTabState> {
|
export class ShareLibraryPanelTab extends SceneObjectBase<ShareLibraryPanelTabState> {
|
||||||
public tabId = 'Library panel';
|
public tabId = shareDashboardType.libraryPanel;
|
||||||
static Component = ShareLibraryPanelTabRenderer;
|
static Component = ShareLibraryPanelTabRenderer;
|
||||||
|
|
||||||
public getTabLabel() {
|
public getTabLabel() {
|
||||||
|
@ -2,15 +2,17 @@ import React from 'react';
|
|||||||
|
|
||||||
import { dateTime, UrlQueryMap } from '@grafana/data';
|
import { dateTime, UrlQueryMap } from '@grafana/data';
|
||||||
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
|
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
|
||||||
import { config, locationService, reportInteraction } from '@grafana/runtime';
|
import { config, locationService } from '@grafana/runtime';
|
||||||
import { SceneComponentProps, SceneObjectBase, SceneObjectRef, VizPanel, sceneGraph } from '@grafana/scenes';
|
import { SceneComponentProps, SceneObjectBase, SceneObjectRef, VizPanel, sceneGraph } from '@grafana/scenes';
|
||||||
import { TimeZone } from '@grafana/schema';
|
import { TimeZone } from '@grafana/schema';
|
||||||
import { Alert, ClipboardButton, Field, FieldSet, Icon, Input, Switch } from '@grafana/ui';
|
import { Alert, ClipboardButton, Field, FieldSet, Icon, Input, Switch } from '@grafana/ui';
|
||||||
import { t, Trans } from 'app/core/internationalization';
|
import { t, Trans } from 'app/core/internationalization';
|
||||||
import { createShortLink } from 'app/core/utils/shortLinks';
|
import { createShortLink } from 'app/core/utils/shortLinks';
|
||||||
import { ThemePicker } from 'app/features/dashboard/components/ShareModal/ThemePicker';
|
import { ThemePicker } from 'app/features/dashboard/components/ShareModal/ThemePicker';
|
||||||
|
import { shareDashboardType } from 'app/features/dashboard/components/ShareModal/utils';
|
||||||
|
|
||||||
import { DashboardScene } from '../scene/DashboardScene';
|
import { DashboardScene } from '../scene/DashboardScene';
|
||||||
|
import { DashboardInteractions } from '../utils/interactions';
|
||||||
import { getDashboardUrl } from '../utils/urlBuilders';
|
import { getDashboardUrl } from '../utils/urlBuilders';
|
||||||
|
|
||||||
import { SceneShareTabState } from './types';
|
import { SceneShareTabState } from './types';
|
||||||
@ -28,7 +30,7 @@ interface ShareOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ShareLinkTab extends SceneObjectBase<ShareLinkTabState> {
|
export class ShareLinkTab extends SceneObjectBase<ShareLinkTabState> {
|
||||||
public tabId = 'Link';
|
public tabId = shareDashboardType.link;
|
||||||
|
|
||||||
static Component = ShareLinkTabRenderer;
|
static Component = ShareLinkTabRenderer;
|
||||||
|
|
||||||
@ -119,7 +121,7 @@ export class ShareLinkTab extends SceneObjectBase<ShareLinkTabState> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onCopy() {
|
onCopy() {
|
||||||
reportInteraction('dashboards_sharing_link_copy_clicked', {
|
DashboardInteractions.shareLinkCopied({
|
||||||
currentTimeRange: this.state.useLockedTime,
|
currentTimeRange: this.state.useLockedTime,
|
||||||
theme: this.state.selectedTheme,
|
theme: this.state.selectedTheme,
|
||||||
shortenURL: this.state.useShortUrl,
|
shortenURL: this.state.useShortUrl,
|
||||||
|
@ -7,6 +7,7 @@ import { contextSrv } from 'app/core/core';
|
|||||||
import { t } from 'app/core/internationalization';
|
import { t } from 'app/core/internationalization';
|
||||||
|
|
||||||
import { DashboardScene } from '../scene/DashboardScene';
|
import { DashboardScene } from '../scene/DashboardScene';
|
||||||
|
import { DashboardInteractions } from '../utils/interactions';
|
||||||
import { getDashboardSceneFor } from '../utils/utils';
|
import { getDashboardSceneFor } from '../utils/utils';
|
||||||
|
|
||||||
import { ShareExportTab } from './ShareExportTab';
|
import { ShareExportTab } from './ShareExportTab';
|
||||||
@ -32,7 +33,7 @@ export class ShareModal extends SceneObjectBase<ShareModalState> implements Moda
|
|||||||
|
|
||||||
constructor(state: Omit<ShareModalState, 'activeTab'> & { activeTab?: string }) {
|
constructor(state: Omit<ShareModalState, 'activeTab'> & { activeTab?: string }) {
|
||||||
super({
|
super({
|
||||||
activeTab: 'Link',
|
activeTab: 'link',
|
||||||
...state,
|
...state,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -73,6 +74,7 @@ export class ShareModal extends SceneObjectBase<ShareModalState> implements Moda
|
|||||||
};
|
};
|
||||||
|
|
||||||
onChangeTab: ComponentProps<typeof ModalTabsHeader>['onChangeTab'] = (tab) => {
|
onChangeTab: ComponentProps<typeof ModalTabsHeader>['onChangeTab'] = (tab) => {
|
||||||
|
DashboardInteractions.sharingTabChanged({ item: tab.value });
|
||||||
this.setState({ activeTab: tab.value });
|
this.setState({ activeTab: tab.value });
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { TimeRange } from '@grafana/data';
|
|||||||
import { SceneComponentProps, sceneGraph, SceneObjectBase, SceneObjectRef, VizPanel } from '@grafana/scenes';
|
import { SceneComponentProps, sceneGraph, SceneObjectBase, SceneObjectRef, VizPanel } from '@grafana/scenes';
|
||||||
import { t } from 'app/core/internationalization';
|
import { t } from 'app/core/internationalization';
|
||||||
import { ShareEmbed } from 'app/features/dashboard/components/ShareModal/ShareEmbed';
|
import { ShareEmbed } from 'app/features/dashboard/components/ShareModal/ShareEmbed';
|
||||||
import { buildParams } from 'app/features/dashboard/components/ShareModal/utils';
|
import { buildParams, shareDashboardType } from 'app/features/dashboard/components/ShareModal/utils';
|
||||||
|
|
||||||
import { DashboardScene } from '../scene/DashboardScene';
|
import { DashboardScene } from '../scene/DashboardScene';
|
||||||
import { getDashboardUrl } from '../utils/urlBuilders';
|
import { getDashboardUrl } from '../utils/urlBuilders';
|
||||||
@ -18,7 +18,7 @@ export interface SharePanelEmbedTabState extends SceneShareTabState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class SharePanelEmbedTab extends SceneObjectBase<SharePanelEmbedTabState> {
|
export class SharePanelEmbedTab extends SceneObjectBase<SharePanelEmbedTabState> {
|
||||||
public tabId = 'Embed';
|
public tabId = shareDashboardType.embed;
|
||||||
static Component = SharePanelEmbedTabRenderer;
|
static Component = SharePanelEmbedTabRenderer;
|
||||||
|
|
||||||
public constructor(state: SharePanelEmbedTabState) {
|
public constructor(state: SharePanelEmbedTabState) {
|
||||||
|
@ -2,14 +2,16 @@ import React from 'react';
|
|||||||
import useAsyncFn from 'react-use/lib/useAsyncFn';
|
import useAsyncFn from 'react-use/lib/useAsyncFn';
|
||||||
|
|
||||||
import { SelectableValue } from '@grafana/data';
|
import { SelectableValue } from '@grafana/data';
|
||||||
import { getBackendSrv, reportInteraction } from '@grafana/runtime';
|
import { getBackendSrv } from '@grafana/runtime';
|
||||||
import { SceneComponentProps, sceneGraph, SceneObjectBase, SceneObjectRef, VizPanel } from '@grafana/scenes';
|
import { SceneComponentProps, sceneGraph, SceneObjectBase, SceneObjectRef, VizPanel } from '@grafana/scenes';
|
||||||
import { Button, ClipboardButton, Field, Input, Modal, RadioButtonGroup } from '@grafana/ui';
|
import { Button, ClipboardButton, Field, Input, Modal, RadioButtonGroup } from '@grafana/ui';
|
||||||
import { t, Trans } from 'app/core/internationalization';
|
import { t, Trans } from 'app/core/internationalization';
|
||||||
|
import { shareDashboardType } from 'app/features/dashboard/components/ShareModal/utils';
|
||||||
import { getDashboardSnapshotSrv, SnapshotSharingOptions } from 'app/features/dashboard/services/SnapshotSrv';
|
import { getDashboardSnapshotSrv, SnapshotSharingOptions } from 'app/features/dashboard/services/SnapshotSrv';
|
||||||
|
|
||||||
import { DashboardScene } from '../scene/DashboardScene';
|
import { DashboardScene } from '../scene/DashboardScene';
|
||||||
import { transformSceneToSaveModel, trimDashboardForSnapshot } from '../serialization/transformSceneToSaveModel';
|
import { transformSceneToSaveModel, trimDashboardForSnapshot } from '../serialization/transformSceneToSaveModel';
|
||||||
|
import { DashboardInteractions } from '../utils/interactions';
|
||||||
|
|
||||||
import { SceneShareTabState } from './types';
|
import { SceneShareTabState } from './types';
|
||||||
|
|
||||||
@ -48,7 +50,7 @@ export interface ShareSnapshotTabState extends SceneShareTabState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ShareSnapshotTab extends SceneObjectBase<ShareSnapshotTabState> {
|
export class ShareSnapshotTab extends SceneObjectBase<ShareSnapshotTabState> {
|
||||||
public tabId = 'Snapshot';
|
public tabId = shareDashboardType.snapshot;
|
||||||
static Component = ShareSnapshoTabRenderer;
|
static Component = ShareSnapshoTabRenderer;
|
||||||
|
|
||||||
public constructor(state: ShareSnapshotTabState) {
|
public constructor(state: ShareSnapshotTabState) {
|
||||||
@ -123,13 +125,9 @@ export class ShareSnapshotTab extends SceneObjectBase<ShareSnapshotTabState> {
|
|||||||
return results;
|
return results;
|
||||||
} finally {
|
} finally {
|
||||||
if (external) {
|
if (external) {
|
||||||
reportInteraction('dashboards_sharing_snapshot_publish_clicked', {
|
DashboardInteractions.publishSnapshotClicked({ expires: cmdData.expires });
|
||||||
expires: cmdData.expires,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
reportInteraction('dashboards_sharing_snapshot_local_clicked', {
|
DashboardInteractions.publishSnapshotLocalClicked({ expires: cmdData.expires });
|
||||||
expires: cmdData.expires,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -5,6 +5,7 @@ import { t } from 'app/core/internationalization';
|
|||||||
import { useGetPublicDashboardQuery } from 'app/features/dashboard/api/publicDashboardApi';
|
import { useGetPublicDashboardQuery } from 'app/features/dashboard/api/publicDashboardApi';
|
||||||
import { Loader } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard';
|
import { Loader } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard';
|
||||||
import { publicDashboardPersisted } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils';
|
import { publicDashboardPersisted } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils';
|
||||||
|
import { shareDashboardType } from 'app/features/dashboard/components/ShareModal/utils';
|
||||||
|
|
||||||
import { DashboardScene } from '../../scene/DashboardScene';
|
import { DashboardScene } from '../../scene/DashboardScene';
|
||||||
import { SceneShareTabState } from '../types';
|
import { SceneShareTabState } from '../types';
|
||||||
@ -17,7 +18,7 @@ export interface SharePublicDashboardTabState extends SceneShareTabState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class SharePublicDashboardTab extends SceneObjectBase<SharePublicDashboardTabState> {
|
export class SharePublicDashboardTab extends SceneObjectBase<SharePublicDashboardTabState> {
|
||||||
public tabId = 'Public dashboard';
|
public tabId = shareDashboardType.publicDashboard;
|
||||||
static Component = SharePublicDashboardTabRenderer;
|
static Component = SharePublicDashboardTabRenderer;
|
||||||
|
|
||||||
public getTabLabel() {
|
public getTabLabel() {
|
||||||
|
148
public/app/features/dashboard-scene/utils/interactions.ts
Normal file
148
public/app/features/dashboard-scene/utils/interactions.ts
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
import { reportInteraction } from '@grafana/runtime';
|
||||||
|
import { InspectTab } from 'app/features/inspector/types';
|
||||||
|
|
||||||
|
export const DashboardInteractions = {
|
||||||
|
// Dashboard interactions:
|
||||||
|
dashboardInitialized: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('init_dashboard_completed', { ...properties });
|
||||||
|
},
|
||||||
|
|
||||||
|
// Panel interactions:
|
||||||
|
panelMenuShown: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('panelheader_menu', { ...properties, item: 'menu' });
|
||||||
|
},
|
||||||
|
panelMenuItemClicked: (
|
||||||
|
item:
|
||||||
|
| 'view'
|
||||||
|
| 'edit'
|
||||||
|
| 'share'
|
||||||
|
| 'createLibraryPanel'
|
||||||
|
| 'unlinkLibraryPanel'
|
||||||
|
| 'duplicate'
|
||||||
|
| 'copy'
|
||||||
|
| 'remove'
|
||||||
|
| 'explore'
|
||||||
|
| 'toggleLegend'
|
||||||
|
| 'create-alert'
|
||||||
|
) => {
|
||||||
|
reportDashboardInteraction('panelheader_menu', { item });
|
||||||
|
},
|
||||||
|
panelMenuInspectClicked(tab: InspectTab) {
|
||||||
|
reportDashboardInteraction('panelheader_menu', { item: 'inspect', tab });
|
||||||
|
},
|
||||||
|
panelLinkClicked: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('panelheader_datalink_clicked', properties);
|
||||||
|
},
|
||||||
|
panelStatusMessageClicked: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('panelheader_statusmessage_clicked', properties);
|
||||||
|
},
|
||||||
|
panelCancelQueryClicked: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('panelheader_cancelquery_clicked', properties);
|
||||||
|
},
|
||||||
|
panelDescriptionShown: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('panelheader_description_displayed', properties);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Sharing interactions:
|
||||||
|
sharingTabChanged: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('sharing_category_clicked', properties);
|
||||||
|
},
|
||||||
|
shareLinkCopied: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('sharing_link_copy_clicked', properties);
|
||||||
|
},
|
||||||
|
embedSnippetCopy: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('sharing_embed_copy_clicked', properties);
|
||||||
|
},
|
||||||
|
publishSnapshotClicked: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('sharing_snapshot_publish_clicked', properties);
|
||||||
|
},
|
||||||
|
publishSnapshotLocalClicked: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('sharing_snapshot_local_clicked', properties);
|
||||||
|
},
|
||||||
|
exportDownloadJsonClicked: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('sharing_export_download_json_clicked', properties);
|
||||||
|
},
|
||||||
|
exportCopyJsonClicked: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('sharing_export_copy_json_clicked', properties);
|
||||||
|
},
|
||||||
|
exportSaveJsonClicked: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('sharing_export_save_json_clicked', properties);
|
||||||
|
},
|
||||||
|
exportViewJsonClicked: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('sharing_export_view_json_clicked', properties);
|
||||||
|
},
|
||||||
|
generatePublicDashboardUrlClicked: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('sharing_public_generate_url_clicked', properties);
|
||||||
|
},
|
||||||
|
revokePublicDashboardEmailClicked: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('sharing_public_email_revoke_clicked', properties);
|
||||||
|
},
|
||||||
|
resendPublicDashboardEmailClicked: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('sharing_public_email_resend_clicked', properties);
|
||||||
|
},
|
||||||
|
publicDashboardEmailInviteClicked: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('sharing_public_email_invite_clicked', properties);
|
||||||
|
},
|
||||||
|
publicDashboardShareTypeChange: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('sharing_public_can_view_clicked', properties);
|
||||||
|
},
|
||||||
|
publicDashboardTimeSelectionChanged: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('sharing_public_time_picker_clicked', properties);
|
||||||
|
},
|
||||||
|
publicDashboardAnnotationsSelectionChanged: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('sharing_public_annotations_clicked', properties);
|
||||||
|
},
|
||||||
|
publicDashboardUrlCopied: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('sharing_public_copy_url_clicked', properties);
|
||||||
|
},
|
||||||
|
publicDashboardPauseSharingClicked: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('sharing_public_pause_clicked', properties);
|
||||||
|
},
|
||||||
|
revokePublicDashboardClicked: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('sharing_public_revoke_clicked', properties);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Empty dashboard state interactions:
|
||||||
|
emptyDashboardButtonClicked: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('emptydashboard_clicked', properties);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Toolbar interactions
|
||||||
|
toolbarAddButtonClicked: (properties?: Record<string, unknown>) => {
|
||||||
|
reportDashboardInteraction('toolbar_add_clicked', properties);
|
||||||
|
},
|
||||||
|
toolbarFavoritesClick: () => {
|
||||||
|
reportDashboardInteraction('toolbar_actions_clicked', { item: 'favorites' });
|
||||||
|
},
|
||||||
|
toolbarSettingsClick: () => {
|
||||||
|
reportDashboardInteraction('toolbar_actions_clicked', { item: 'settings' });
|
||||||
|
},
|
||||||
|
toolbarRefreshClick: () => {
|
||||||
|
reportDashboardInteraction('toolbar_actions_clicked', { item: 'refresh' });
|
||||||
|
},
|
||||||
|
toolbarTimePickerClick: () => {
|
||||||
|
reportDashboardInteraction('toolbar_actions_clicked', { item: 'time_picker' });
|
||||||
|
},
|
||||||
|
toolbarZoomClick: () => {
|
||||||
|
reportDashboardInteraction('toolbar_actions_clicked', { item: 'zoom_out_time_range' });
|
||||||
|
},
|
||||||
|
|
||||||
|
toolbarShareClick: () => {
|
||||||
|
reportDashboardInteraction('toolbar_actions_clicked', { item: 'share' });
|
||||||
|
},
|
||||||
|
toolbarSaveClick: () => {
|
||||||
|
reportDashboardInteraction('toolbar_actions_clicked', { item: 'save' });
|
||||||
|
},
|
||||||
|
|
||||||
|
toolbarAddClick: () => {
|
||||||
|
reportDashboardInteraction('toolbar_actions_clicked', { item: 'add' });
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const reportDashboardInteraction: typeof reportInteraction = (name, properties) => {
|
||||||
|
if (properties) {
|
||||||
|
reportInteraction(`dashboards_${name}`, properties);
|
||||||
|
} else {
|
||||||
|
reportInteraction(`dashboards_${name}`);
|
||||||
|
}
|
||||||
|
};
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
|
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { config, locationService, reportInteraction } from '@grafana/runtime';
|
import { config, locationService } from '@grafana/runtime';
|
||||||
import { Menu } from '@grafana/ui';
|
import { Menu } from '@grafana/ui';
|
||||||
import { t } from 'app/core/internationalization';
|
import { t } from 'app/core/internationalization';
|
||||||
import { DashboardModel } from 'app/features/dashboard/state';
|
import { DashboardModel } from 'app/features/dashboard/state';
|
||||||
@ -12,6 +12,7 @@ import {
|
|||||||
onCreateNewRow,
|
onCreateNewRow,
|
||||||
onPasteCopiedPanel,
|
onPasteCopiedPanel,
|
||||||
} from 'app/features/dashboard/utils/dashboard';
|
} from 'app/features/dashboard/utils/dashboard';
|
||||||
|
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||||
import { useDispatch, useSelector } from 'app/types';
|
import { useDispatch, useSelector } from 'app/types';
|
||||||
|
|
||||||
import { setInitialDatasource } from '../../state/reducers';
|
import { setInitialDatasource } from '../../state/reducers';
|
||||||
@ -33,7 +34,7 @@ const AddPanelMenu = ({ dashboard }: Props) => {
|
|||||||
label={t('dashboard.add-menu.visualization', 'Visualization')}
|
label={t('dashboard.add-menu.visualization', 'Visualization')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const id = onCreateNewPanel(dashboard, initialDatasource);
|
const id = onCreateNewPanel(dashboard, initialDatasource);
|
||||||
reportInteraction('dashboards_toolbar_add_clicked', { item: 'add_visualization' });
|
DashboardInteractions.toolbarAddButtonClicked({ item: 'add_visualization' });
|
||||||
locationService.partial({ editPanel: id });
|
locationService.partial({ editPanel: id });
|
||||||
dispatch(setInitialDatasource(undefined));
|
dispatch(setInitialDatasource(undefined));
|
||||||
}}
|
}}
|
||||||
@ -44,7 +45,7 @@ const AddPanelMenu = ({ dashboard }: Props) => {
|
|||||||
testId={selectors.pages.AddDashboard.itemButton('Add new widget menu item')}
|
testId={selectors.pages.AddDashboard.itemButton('Add new widget menu item')}
|
||||||
label={t('dashboard.add-menu.widget', 'Widget')}
|
label={t('dashboard.add-menu.widget', 'Widget')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
reportInteraction('dashboards_toolbar_add_clicked', { item: 'add_widget' });
|
DashboardInteractions.toolbarAddButtonClicked({ item: 'add_widget' });
|
||||||
locationService.partial({ addWidget: true });
|
locationService.partial({ addWidget: true });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -54,7 +55,7 @@ const AddPanelMenu = ({ dashboard }: Props) => {
|
|||||||
testId={selectors.pages.AddDashboard.itemButton('Add new row menu item')}
|
testId={selectors.pages.AddDashboard.itemButton('Add new row menu item')}
|
||||||
label={t('dashboard.add-menu.row', 'Row')}
|
label={t('dashboard.add-menu.row', 'Row')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
reportInteraction('dashboards_toolbar_add_clicked', { item: 'add_row' });
|
DashboardInteractions.toolbarAddButtonClicked({ item: 'add_row' });
|
||||||
onCreateNewRow(dashboard);
|
onCreateNewRow(dashboard);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -63,7 +64,7 @@ const AddPanelMenu = ({ dashboard }: Props) => {
|
|||||||
testId={selectors.pages.AddDashboard.itemButton('Add new panel from panel library menu item')}
|
testId={selectors.pages.AddDashboard.itemButton('Add new panel from panel library menu item')}
|
||||||
label={t('dashboard.add-menu.import', 'Import from library')}
|
label={t('dashboard.add-menu.import', 'Import from library')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
reportInteraction('dashboards_toolbar_add_clicked', { item: 'import_from_library' });
|
DashboardInteractions.toolbarAddButtonClicked({ item: 'import_from_library' });
|
||||||
onAddLibraryPanel(dashboard);
|
onAddLibraryPanel(dashboard);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -72,7 +73,7 @@ const AddPanelMenu = ({ dashboard }: Props) => {
|
|||||||
testId={selectors.pages.AddDashboard.itemButton('Add new panel from clipboard menu item')}
|
testId={selectors.pages.AddDashboard.itemButton('Add new panel from clipboard menu item')}
|
||||||
label={t('dashboard.add-menu.paste-panel', 'Paste panel')}
|
label={t('dashboard.add-menu.paste-panel', 'Paste panel')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
reportInteraction('dashboards_toolbar_add_clicked', { item: 'paste_panel' });
|
DashboardInteractions.toolbarAddButtonClicked({ item: 'paste_panel' });
|
||||||
onPasteCopiedPanel(dashboard, copiedPanelPlugin);
|
onPasteCopiedPanel(dashboard, copiedPanelPlugin);
|
||||||
}}
|
}}
|
||||||
disabled={!copiedPanelPlugin}
|
disabled={!copiedPanelPlugin}
|
||||||
|
@ -27,6 +27,7 @@ import AddPanelButton from 'app/features/dashboard/components/AddPanelButton/Add
|
|||||||
import { SaveDashboardDrawer } from 'app/features/dashboard/components/SaveDashboard/SaveDashboardDrawer';
|
import { SaveDashboardDrawer } from 'app/features/dashboard/components/SaveDashboard/SaveDashboardDrawer';
|
||||||
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||||
import { DashboardModel } from 'app/features/dashboard/state';
|
import { DashboardModel } from 'app/features/dashboard/state';
|
||||||
|
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||||
import { playlistSrv } from 'app/features/playlist/PlaylistSrv';
|
import { playlistSrv } from 'app/features/playlist/PlaylistSrv';
|
||||||
import { updateTimeZoneForSession } from 'app/features/profile/state/reducers';
|
import { updateTimeZoneForSession } from 'app/features/profile/state/reducers';
|
||||||
import { KioskMode } from 'app/types';
|
import { KioskMode } from 'app/types';
|
||||||
@ -35,15 +36,6 @@ import { DashboardMetaChangedEvent, ShowModalReactEvent } from 'app/types/events
|
|||||||
import { DashNavButton } from './DashNavButton';
|
import { DashNavButton } from './DashNavButton';
|
||||||
import { DashNavTimeControls } from './DashNavTimeControls';
|
import { DashNavTimeControls } from './DashNavTimeControls';
|
||||||
import { ShareButton } from './ShareButton';
|
import { ShareButton } from './ShareButton';
|
||||||
import {
|
|
||||||
trackToolbarFavoritesClick,
|
|
||||||
trackToolbarRefreshClick,
|
|
||||||
trackToolbarSettingsClick,
|
|
||||||
trackToolbarTimePickerClick,
|
|
||||||
trackToolbarZoomClick,
|
|
||||||
trackToolbarSaveClick,
|
|
||||||
trackToolbarAddClick,
|
|
||||||
} from './analytics';
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
setStarred,
|
setStarred,
|
||||||
@ -131,7 +123,7 @@ export const DashNav = React.memo<Props>((props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onStarDashboard = () => {
|
const onStarDashboard = () => {
|
||||||
trackToolbarFavoritesClick();
|
DashboardInteractions.toolbarFavoritesClick();
|
||||||
const dashboardSrv = getDashboardSrv();
|
const dashboardSrv = getDashboardSrv();
|
||||||
const { dashboard, setStarred } = props;
|
const { dashboard, setStarred } = props;
|
||||||
|
|
||||||
@ -143,7 +135,7 @@ export const DashNav = React.memo<Props>((props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onOpenSettings = () => {
|
const onOpenSettings = () => {
|
||||||
trackToolbarSettingsClick();
|
DashboardInteractions.toolbarSettingsClick();
|
||||||
locationService.partial({ editview: 'settings' });
|
locationService.partial({ editview: 'settings' });
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -257,9 +249,9 @@ export const DashNav = React.memo<Props>((props) => {
|
|||||||
<DashNavTimeControls
|
<DashNavTimeControls
|
||||||
dashboard={dashboard}
|
dashboard={dashboard}
|
||||||
onChangeTimeZone={updateTimeZoneForSession}
|
onChangeTimeZone={updateTimeZoneForSession}
|
||||||
onToolbarRefreshClick={trackToolbarRefreshClick}
|
onToolbarRefreshClick={DashboardInteractions.toolbarRefreshClick}
|
||||||
onToolbarZoomClick={trackToolbarZoomClick}
|
onToolbarZoomClick={DashboardInteractions.toolbarZoomClick}
|
||||||
onToolbarTimePickerClick={trackToolbarTimePickerClick}
|
onToolbarTimePickerClick={DashboardInteractions.toolbarTimePickerClick}
|
||||||
key="time-controls"
|
key="time-controls"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -283,7 +275,11 @@ export const DashNav = React.memo<Props>((props) => {
|
|||||||
if (canEdit && !isFullscreen) {
|
if (canEdit && !isFullscreen) {
|
||||||
if (config.featureToggles.emptyDashboardPage) {
|
if (config.featureToggles.emptyDashboardPage) {
|
||||||
buttons.push(
|
buttons.push(
|
||||||
<AddPanelButton dashboard={dashboard} onToolbarAddMenuOpen={trackToolbarAddClick} key="panel-add-dropdown" />
|
<AddPanelButton
|
||||||
|
dashboard={dashboard}
|
||||||
|
onToolbarAddMenuOpen={DashboardInteractions.toolbarAddClick}
|
||||||
|
key="panel-add-dropdown"
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
buttons.push(
|
buttons.push(
|
||||||
@ -306,7 +302,7 @@ export const DashNav = React.memo<Props>((props) => {
|
|||||||
tooltip={t('dashboard.toolbar.save', 'Save dashboard')}
|
tooltip={t('dashboard.toolbar.save', 'Save dashboard')}
|
||||||
icon="save"
|
icon="save"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
trackToolbarSaveClick();
|
DashboardInteractions.toolbarSaveClick();
|
||||||
showModal(SaveDashboardDrawer, {
|
showModal(SaveDashboardDrawer, {
|
||||||
dashboard,
|
dashboard,
|
||||||
onDismiss: hideModal,
|
onDismiss: hideModal,
|
||||||
|
@ -4,11 +4,11 @@ import { ModalsContext } from '@grafana/ui';
|
|||||||
import { useQueryParams } from 'app/core/hooks/useQueryParams';
|
import { useQueryParams } from 'app/core/hooks/useQueryParams';
|
||||||
import { t } from 'app/core/internationalization';
|
import { t } from 'app/core/internationalization';
|
||||||
import { DashboardModel } from 'app/features/dashboard/state';
|
import { DashboardModel } from 'app/features/dashboard/state';
|
||||||
|
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||||
|
|
||||||
import { ShareModal } from '../ShareModal';
|
import { ShareModal } from '../ShareModal';
|
||||||
|
|
||||||
import { DashNavButton } from './DashNavButton';
|
import { DashNavButton } from './DashNavButton';
|
||||||
import { trackToolbarShareClick } from './analytics';
|
|
||||||
|
|
||||||
export const ShareButton = ({ dashboard }: { dashboard: DashboardModel }) => {
|
export const ShareButton = ({ dashboard }: { dashboard: DashboardModel }) => {
|
||||||
const [queryParams] = useQueryParams();
|
const [queryParams] = useQueryParams();
|
||||||
@ -33,7 +33,7 @@ export const ShareButton = ({ dashboard }: { dashboard: DashboardModel }) => {
|
|||||||
icon="share-alt"
|
icon="share-alt"
|
||||||
iconSize="lg"
|
iconSize="lg"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
trackToolbarShareClick();
|
DashboardInteractions.toolbarShareClick();
|
||||||
showModal(ShareModal, {
|
showModal(ShareModal, {
|
||||||
dashboard,
|
dashboard,
|
||||||
onDismiss: hideModal,
|
onDismiss: hideModal,
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
import { reportInteraction } from '@grafana/runtime';
|
|
||||||
export const shareAnalyticsEventNames: {
|
|
||||||
[key: string]: string;
|
|
||||||
} = {
|
|
||||||
dashboardsToolbarActionsClicked: 'dashboards_toolbar_actions_clicked',
|
|
||||||
};
|
|
||||||
export function trackToolbarFavoritesClick() {
|
|
||||||
reportInteraction(shareAnalyticsEventNames.dashboardsToolbarActionsClicked, { item: 'favorites' });
|
|
||||||
}
|
|
||||||
export function trackToolbarSettingsClick() {
|
|
||||||
reportInteraction(shareAnalyticsEventNames.dashboardsToolbarActionsClicked, { item: 'settings' });
|
|
||||||
}
|
|
||||||
export function trackToolbarRefreshClick() {
|
|
||||||
reportInteraction(shareAnalyticsEventNames.dashboardsToolbarActionsClicked, { item: 'refresh' });
|
|
||||||
}
|
|
||||||
export function trackToolbarTimePickerClick() {
|
|
||||||
reportInteraction(shareAnalyticsEventNames.dashboardsToolbarActionsClicked, { item: 'time_picker' });
|
|
||||||
}
|
|
||||||
export function trackToolbarZoomClick() {
|
|
||||||
reportInteraction(shareAnalyticsEventNames.dashboardsToolbarActionsClicked, { item: 'zoom_out_time_range' });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function trackToolbarShareClick() {
|
|
||||||
reportInteraction(shareAnalyticsEventNames.dashboardsToolbarActionsClicked, { item: 'share' });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function trackToolbarSaveClick() {
|
|
||||||
reportInteraction(shareAnalyticsEventNames.dashboardsToolbarActionsClicked, { item: 'save' });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function trackToolbarAddClick() {
|
|
||||||
reportInteraction(shareAnalyticsEventNames.dashboardsToolbarActionsClicked, { item: 'add' });
|
|
||||||
}
|
|
@ -5,6 +5,7 @@ import { RawTimeRange, TimeRange } from '@grafana/data';
|
|||||||
import { reportInteraction } from '@grafana/runtime';
|
import { reportInteraction } from '@grafana/runtime';
|
||||||
import { ClipboardButton, Field, Modal, Switch, TextArea } from '@grafana/ui';
|
import { ClipboardButton, Field, Modal, Switch, TextArea } from '@grafana/ui';
|
||||||
import { t, Trans } from 'app/core/internationalization';
|
import { t, Trans } from 'app/core/internationalization';
|
||||||
|
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||||
|
|
||||||
import { ThemePicker } from './ThemePicker';
|
import { ThemePicker } from './ThemePicker';
|
||||||
import { ShareModalTabProps } from './types';
|
import { ShareModalTabProps } from './types';
|
||||||
@ -81,7 +82,7 @@ export function ShareEmbed({ panel, dashboard, range, buildIframe = buildIframeH
|
|||||||
variant="primary"
|
variant="primary"
|
||||||
getText={() => iframeHtml}
|
getText={() => iframeHtml}
|
||||||
onClipboardCopy={() => {
|
onClipboardCopy={() => {
|
||||||
reportInteraction('dashboards_sharing_embed_copy_clicked', {
|
DashboardInteractions.embedSnippetCopy({
|
||||||
currentTimeRange: useCurrentTimeRange,
|
currentTimeRange: useCurrentTimeRange,
|
||||||
theme: selectedTheme,
|
theme: selectedTheme,
|
||||||
});
|
});
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { saveAs } from 'file-saver';
|
import { saveAs } from 'file-saver';
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
import { reportInteraction } from '@grafana/runtime';
|
|
||||||
import { Button, Field, Modal, Switch } from '@grafana/ui';
|
import { Button, Field, Modal, Switch } from '@grafana/ui';
|
||||||
import { appEvents } from 'app/core/core';
|
import { appEvents } from 'app/core/core';
|
||||||
import { t, Trans } from 'app/core/internationalization';
|
import { t, Trans } from 'app/core/internationalization';
|
||||||
import { DashboardExporter } from 'app/features/dashboard/components/DashExportModal';
|
import { DashboardExporter } from 'app/features/dashboard/components/DashExportModal';
|
||||||
|
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||||
import { ShowModalReactEvent } from 'app/types/events';
|
import { ShowModalReactEvent } from 'app/types/events';
|
||||||
|
|
||||||
import { ViewJsonModal } from './ViewJsonModal';
|
import { ViewJsonModal } from './ViewJsonModal';
|
||||||
@ -39,7 +39,7 @@ export class ShareExport extends PureComponent<Props, State> {
|
|||||||
const { dashboard } = this.props;
|
const { dashboard } = this.props;
|
||||||
const { shareExternally } = this.state;
|
const { shareExternally } = this.state;
|
||||||
|
|
||||||
reportInteraction('dashboards_sharing_export_save_json_clicked', { externally: shareExternally });
|
DashboardInteractions.exportSaveJsonClicked();
|
||||||
|
|
||||||
if (shareExternally) {
|
if (shareExternally) {
|
||||||
this.exporter.makeExportable(dashboard).then((dashboardJson) => {
|
this.exporter.makeExportable(dashboard).then((dashboardJson) => {
|
||||||
@ -53,8 +53,7 @@ export class ShareExport extends PureComponent<Props, State> {
|
|||||||
onViewJson = () => {
|
onViewJson = () => {
|
||||||
const { dashboard } = this.props;
|
const { dashboard } = this.props;
|
||||||
const { shareExternally } = this.state;
|
const { shareExternally } = this.state;
|
||||||
|
DashboardInteractions.exportViewJsonClicked();
|
||||||
reportInteraction('dashboards_sharing_export_view_json_clicked', { externally: shareExternally });
|
|
||||||
|
|
||||||
if (shareExternally) {
|
if (shareExternally) {
|
||||||
this.exporter.makeExportable(dashboard).then((dashboardJson) => {
|
this.exporter.makeExportable(dashboard).then((dashboardJson) => {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
|
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
|
||||||
import { reportInteraction } from '@grafana/runtime';
|
|
||||||
import { Alert, ClipboardButton, Field, FieldSet, Input, Switch, TextLink } from '@grafana/ui';
|
import { Alert, ClipboardButton, Field, FieldSet, Input, Switch, TextLink } from '@grafana/ui';
|
||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
import { t, Trans } from 'app/core/internationalization';
|
import { t, Trans } from 'app/core/internationalization';
|
||||||
|
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||||
|
|
||||||
import { ThemePicker } from './ThemePicker';
|
import { ThemePicker } from './ThemePicker';
|
||||||
import { ShareModalTabProps } from './types';
|
import { ShareModalTabProps } from './types';
|
||||||
@ -75,7 +75,7 @@ export class ShareLink extends PureComponent<Props, State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onCopy() {
|
onCopy() {
|
||||||
reportInteraction('dashboards_sharing_link_copy_clicked', {
|
DashboardInteractions.shareLinkCopied({
|
||||||
currentTimeRange: this.state.useCurrentTimeRange,
|
currentTimeRange: this.state.useCurrentTimeRange,
|
||||||
theme: this.state.selectedTheme,
|
theme: this.state.selectedTheme,
|
||||||
shortenURL: this.state.useShortUrl,
|
shortenURL: this.state.useShortUrl,
|
||||||
|
@ -6,6 +6,7 @@ import { contextSrv } from 'app/core/core';
|
|||||||
import { t } from 'app/core/internationalization';
|
import { t } from 'app/core/internationalization';
|
||||||
import { SharePublicDashboard } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard';
|
import { SharePublicDashboard } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard';
|
||||||
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
||||||
|
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||||
import { isPanelModelLibraryPanel } from 'app/features/library-panels/guard';
|
import { isPanelModelLibraryPanel } from 'app/features/library-panels/guard';
|
||||||
|
|
||||||
import { ShareEmbed } from './ShareEmbed';
|
import { ShareEmbed } from './ShareEmbed';
|
||||||
@ -13,7 +14,6 @@ import { ShareExport } from './ShareExport';
|
|||||||
import { ShareLibraryPanel } from './ShareLibraryPanel';
|
import { ShareLibraryPanel } from './ShareLibraryPanel';
|
||||||
import { ShareLink } from './ShareLink';
|
import { ShareLink } from './ShareLink';
|
||||||
import { ShareSnapshot } from './ShareSnapshot';
|
import { ShareSnapshot } from './ShareSnapshot';
|
||||||
import { trackDashboardSharingTypeOpen } from './analytics';
|
|
||||||
import { ShareModalTabModel } from './types';
|
import { ShareModalTabModel } from './types';
|
||||||
import { shareDashboardType } from './utils';
|
import { shareDashboardType } from './utils';
|
||||||
|
|
||||||
@ -102,7 +102,9 @@ class UnthemedShareModal extends React.Component<Props, State> {
|
|||||||
|
|
||||||
onSelectTab: React.ComponentProps<typeof ModalTabsHeader>['onChangeTab'] = (t) => {
|
onSelectTab: React.ComponentProps<typeof ModalTabsHeader>['onChangeTab'] = (t) => {
|
||||||
this.setState((prevState) => ({ ...prevState, activeTab: t.value }));
|
this.setState((prevState) => ({ ...prevState, activeTab: t.value }));
|
||||||
trackDashboardSharingTypeOpen(t.value);
|
DashboardInteractions.sharingTabChanged({
|
||||||
|
item: t.value,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
getActiveTab() {
|
getActiveTab() {
|
||||||
|
@ -4,7 +4,6 @@ import { useForm } from 'react-hook-form';
|
|||||||
|
|
||||||
import { GrafanaTheme2, TimeRange } from '@grafana/data/src';
|
import { GrafanaTheme2, TimeRange } from '@grafana/data/src';
|
||||||
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
|
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
|
||||||
import { reportInteraction } from '@grafana/runtime';
|
|
||||||
import { config, featureEnabled } from '@grafana/runtime/src';
|
import { config, featureEnabled } from '@grafana/runtime/src';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@ -25,6 +24,7 @@ import {
|
|||||||
import { DashboardModel } from 'app/features/dashboard/state';
|
import { DashboardModel } from 'app/features/dashboard/state';
|
||||||
import { getTimeRange } from 'app/features/dashboard/utils/timeRange';
|
import { getTimeRange } from 'app/features/dashboard/utils/timeRange';
|
||||||
import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene';
|
import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene';
|
||||||
|
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||||
import { DeletePublicDashboardModal } from 'app/features/manage-dashboards/components/PublicDashboardListTable/DeletePublicDashboardModal';
|
import { DeletePublicDashboardModal } from 'app/features/manage-dashboards/components/PublicDashboardListTable/DeletePublicDashboardModal';
|
||||||
|
|
||||||
import { contextSrv } from '../../../../../../core/services/context_srv';
|
import { contextSrv } from '../../../../../../core/services/context_srv';
|
||||||
@ -111,7 +111,7 @@ export function ConfigPublicDashboardBase({
|
|||||||
};
|
};
|
||||||
|
|
||||||
function onCopyURL() {
|
function onCopyURL() {
|
||||||
reportInteraction('dashboards_sharing_public_copy_url_clicked');
|
DashboardInteractions.publicDashboardUrlCopied();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -151,7 +151,7 @@ export function ConfigPublicDashboardBase({
|
|||||||
{...register('isPaused')}
|
{...register('isPaused')}
|
||||||
disabled={disableInputs}
|
disabled={disableInputs}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
reportInteraction('dashboards_sharing_public_pause_clicked', {
|
DashboardInteractions.publicDashboardPauseSharingClicked({
|
||||||
paused: e.currentTarget.checked,
|
paused: e.currentTarget.checked,
|
||||||
});
|
});
|
||||||
onChange('isPaused', e.currentTarget.checked);
|
onChange('isPaused', e.currentTarget.checked);
|
||||||
@ -242,7 +242,7 @@ export function ConfigPublicDashboard({ publicDashboard, unsupportedDatasources
|
|||||||
showSaveChangesAlert={hasWritePermissions && dashboard.hasUnsavedChanges()}
|
showSaveChangesAlert={hasWritePermissions && dashboard.hasUnsavedChanges()}
|
||||||
hasTemplateVariables={hasTemplateVariables}
|
hasTemplateVariables={hasTemplateVariables}
|
||||||
onRevoke={() => {
|
onRevoke={() => {
|
||||||
reportInteraction('dashboards_sharing_public_revoke_clicked');
|
DashboardInteractions.revokePublicDashboardClicked();
|
||||||
showModal(DeletePublicDashboardModal, {
|
showModal(DeletePublicDashboardModal, {
|
||||||
dashboardTitle: dashboard.title,
|
dashboardTitle: dashboard.title,
|
||||||
onConfirm: () => onDeletePublicDashboardClick(hideModal),
|
onConfirm: () => onDeletePublicDashboardClick(hideModal),
|
||||||
|
@ -3,9 +3,9 @@ import { UseFormRegister } from 'react-hook-form';
|
|||||||
|
|
||||||
import { TimeRange } from '@grafana/data/src';
|
import { TimeRange } from '@grafana/data/src';
|
||||||
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
|
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
|
||||||
import { reportInteraction } from '@grafana/runtime';
|
|
||||||
import { FieldSet, Label, Switch, TimeRangeInput, VerticalGroup } from '@grafana/ui/src';
|
import { FieldSet, Label, Switch, TimeRangeInput, VerticalGroup } from '@grafana/ui/src';
|
||||||
import { Layout } from '@grafana/ui/src/components/Layout/Layout';
|
import { Layout } from '@grafana/ui/src/components/Layout/Layout';
|
||||||
|
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||||
|
|
||||||
import { ConfigPublicDashboardForm } from './ConfigPublicDashboard';
|
import { ConfigPublicDashboardForm } from './ConfigPublicDashboard';
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ export const Configuration = ({
|
|||||||
data-testid={selectors.EnableTimeRangeSwitch}
|
data-testid={selectors.EnableTimeRangeSwitch}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
onChange('isTimeSelectionEnabled', e.currentTarget.checked);
|
onChange('isTimeSelectionEnabled', e.currentTarget.checked);
|
||||||
reportInteraction('dashboards_sharing_public_time_picker_clicked', {
|
DashboardInteractions.publicDashboardTimeSelectionChanged({
|
||||||
enabled: e.currentTarget.checked,
|
enabled: e.currentTarget.checked,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
@ -50,7 +50,7 @@ export const Configuration = ({
|
|||||||
{...register('isAnnotationsEnabled')}
|
{...register('isAnnotationsEnabled')}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
onChange('isAnnotationsEnabled', e.currentTarget.checked);
|
onChange('isAnnotationsEnabled', e.currentTarget.checked);
|
||||||
reportInteraction('dashboards_sharing_public_annotations_clicked', {
|
DashboardInteractions.publicDashboardAnnotationsSelectionChanged({
|
||||||
enabled: e.currentTarget.checked,
|
enabled: e.currentTarget.checked,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
@ -5,7 +5,6 @@ import { useWindowSize } from 'react-use';
|
|||||||
|
|
||||||
import { GrafanaTheme2, SelectableValue } from '@grafana/data/src';
|
import { GrafanaTheme2, SelectableValue } from '@grafana/data/src';
|
||||||
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
|
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
|
||||||
import { reportInteraction } from '@grafana/runtime';
|
|
||||||
import { FieldSet } from '@grafana/ui';
|
import { FieldSet } from '@grafana/ui';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@ -25,6 +24,7 @@ import {
|
|||||||
useReshareAccessToRecipientMutation,
|
useReshareAccessToRecipientMutation,
|
||||||
useUpdatePublicDashboardMutation,
|
useUpdatePublicDashboardMutation,
|
||||||
} from 'app/features/dashboard/api/publicDashboardApi';
|
} from 'app/features/dashboard/api/publicDashboardApi';
|
||||||
|
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||||
import { AccessControlAction, useSelector } from 'app/types';
|
import { AccessControlAction, useSelector } from 'app/types';
|
||||||
|
|
||||||
import { PublicDashboard, PublicDashboardShareType, validEmailRegex } from '../SharePublicDashboardUtils';
|
import { PublicDashboard, PublicDashboardShareType, validEmailRegex } from '../SharePublicDashboardUtils';
|
||||||
@ -57,12 +57,12 @@ const EmailList = ({
|
|||||||
const isLoading = isDeleteLoading || isReshareLoading;
|
const isLoading = isDeleteLoading || isReshareLoading;
|
||||||
|
|
||||||
const onDeleteEmail = (recipientUid: string) => {
|
const onDeleteEmail = (recipientUid: string) => {
|
||||||
reportInteraction('dashboards_sharing_public_email_revoke_clicked');
|
DashboardInteractions.revokePublicDashboardEmailClicked();
|
||||||
deleteEmail({ recipientUid, dashboardUid: dashboardUid, uid: publicDashboardUid });
|
deleteEmail({ recipientUid, dashboardUid: dashboardUid, uid: publicDashboardUid });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onReshare = (recipientUid: string) => {
|
const onReshare = (recipientUid: string) => {
|
||||||
reportInteraction('dashboards_sharing_public_email_resend_clicked');
|
DashboardInteractions.resendPublicDashboardEmailClicked();
|
||||||
reshareAccess({ recipientUid, uid: publicDashboardUid });
|
reshareAccess({ recipientUid, uid: publicDashboardUid });
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -150,7 +150,7 @@ export const EmailSharingConfiguration = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onSubmit = async (data: EmailSharingConfigurationForm) => {
|
const onSubmit = async (data: EmailSharingConfigurationForm) => {
|
||||||
reportInteraction('dashboards_sharing_public_email_invite_clicked');
|
DashboardInteractions.publicDashboardEmailInviteClicked();
|
||||||
await addEmail({ recipient: data.email, uid: publicDashboard!.uid, dashboardUid: dashboard.uid }).unwrap();
|
await addEmail({ recipient: data.email, uid: publicDashboard!.uid, dashboardUid: dashboard.uid }).unwrap();
|
||||||
reset({ email: '', shareType: PublicDashboardShareType.EMAIL });
|
reset({ email: '', shareType: PublicDashboardShareType.EMAIL });
|
||||||
};
|
};
|
||||||
@ -170,7 +170,7 @@ export const EmailSharingConfiguration = () => {
|
|||||||
size={width < 480 ? 'sm' : 'md'}
|
size={width < 480 ? 'sm' : 'md'}
|
||||||
options={options}
|
options={options}
|
||||||
onChange={(shareType: PublicDashboardShareType) => {
|
onChange={(shareType: PublicDashboardShareType) => {
|
||||||
reportInteraction('dashboards_sharing_public_can_view_clicked', {
|
DashboardInteractions.publicDashboardShareTypeChange({
|
||||||
shareType: shareType === PublicDashboardShareType.EMAIL ? 'email' : 'public',
|
shareType: shareType === PublicDashboardShareType.EMAIL ? 'email' : 'public',
|
||||||
});
|
});
|
||||||
setValue('shareType', shareType);
|
setValue('shareType', shareType);
|
||||||
|
@ -4,11 +4,11 @@ import { FormState, UseFormRegister } from 'react-hook-form';
|
|||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data/src';
|
import { GrafanaTheme2 } from '@grafana/data/src';
|
||||||
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
|
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
|
||||||
import { reportInteraction } from '@grafana/runtime';
|
|
||||||
import { Button, Form, Spinner, useStyles2 } from '@grafana/ui/src';
|
import { Button, Form, Spinner, useStyles2 } from '@grafana/ui/src';
|
||||||
import { useCreatePublicDashboardMutation } from 'app/features/dashboard/api/publicDashboardApi';
|
import { useCreatePublicDashboardMutation } from 'app/features/dashboard/api/publicDashboardApi';
|
||||||
import { DashboardModel } from 'app/features/dashboard/state';
|
import { DashboardModel } from 'app/features/dashboard/state';
|
||||||
import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene';
|
import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene';
|
||||||
|
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||||
|
|
||||||
import { contextSrv } from '../../../../../../core/services/context_srv';
|
import { contextSrv } from '../../../../../../core/services/context_srv';
|
||||||
import { AccessControlAction, useSelector } from '../../../../../../types';
|
import { AccessControlAction, useSelector } from '../../../../../../types';
|
||||||
@ -46,7 +46,7 @@ export const CreatePublicDashboardBase = ({
|
|||||||
const [createPublicDashboard, { isLoading, isError }] = useCreatePublicDashboardMutation();
|
const [createPublicDashboard, { isLoading, isError }] = useCreatePublicDashboardMutation();
|
||||||
const onCreate = () => {
|
const onCreate = () => {
|
||||||
createPublicDashboard({ dashboard, payload: { isEnabled: true } });
|
createPublicDashboard({ dashboard, payload: { isEnabled: true } });
|
||||||
reportInteraction('dashboards_sharing_public_generate_url_clicked', {});
|
DashboardInteractions.generatePublicDashboardUrlClicked({});
|
||||||
};
|
};
|
||||||
|
|
||||||
const disableInputs = !hasWritePermissions || isLoading || isError || hasError;
|
const disableInputs = !hasWritePermissions || isLoading || isError || hasError;
|
||||||
|
@ -13,8 +13,8 @@ import { backendSrv } from 'app/core/services/backend_srv';
|
|||||||
import { contextSrv } from 'app/core/services/context_srv';
|
import { contextSrv } from 'app/core/services/context_srv';
|
||||||
import { Echo } from 'app/core/services/echo/Echo';
|
import { Echo } from 'app/core/services/echo/Echo';
|
||||||
import { createDashboardModelFixture } from 'app/features/dashboard/state/__fixtures__/dashboardFixtures';
|
import { createDashboardModelFixture } from 'app/features/dashboard/state/__fixtures__/dashboardFixtures';
|
||||||
|
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||||
|
|
||||||
import { trackDashboardSharingTypeOpen } from '../analytics';
|
|
||||||
import { shareDashboardType } from '../utils';
|
import { shareDashboardType } from '../utils';
|
||||||
|
|
||||||
import * as sharePublicDashboardUtils from './SharePublicDashboardUtils';
|
import * as sharePublicDashboardUtils from './SharePublicDashboardUtils';
|
||||||
@ -33,9 +33,11 @@ jest.mock('@grafana/runtime', () => ({
|
|||||||
reportInteraction: jest.fn(),
|
reportInteraction: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('../analytics', () => ({
|
jest.mock('app/features/dashboard-scene/utils/interactions', () => ({
|
||||||
...jest.requireActual('../analytics'),
|
DashboardInteractions: {
|
||||||
trackDashboardSharingTypeOpen: jest.fn(),
|
...jest.requireActual('app/features/dashboard-scene/utils/interactions').DashboardInteractions,
|
||||||
|
sharingTabChanged: jest.fn(),
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard;
|
const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard;
|
||||||
@ -345,8 +347,8 @@ describe('SharePublic - Report interactions', () => {
|
|||||||
await renderSharePublicDashboard();
|
await renderSharePublicDashboard();
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(trackDashboardSharingTypeOpen).toHaveBeenCalledTimes(1);
|
expect(DashboardInteractions.sharingTabChanged).toHaveBeenCalledTimes(1);
|
||||||
expect(trackDashboardSharingTypeOpen).lastCalledWith(shareDashboardType.publicDashboard);
|
expect(DashboardInteractions.sharingTabChanged).lastCalledWith({ item: shareDashboardType.publicDashboard });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
import { isEmptyObject, SelectableValue } from '@grafana/data';
|
import { isEmptyObject, SelectableValue } from '@grafana/data';
|
||||||
import { getBackendSrv, reportInteraction } from '@grafana/runtime';
|
import { getBackendSrv } from '@grafana/runtime';
|
||||||
import { Button, ClipboardButton, Field, Input, LinkButton, Modal, Select, Spinner } from '@grafana/ui';
|
import { Button, ClipboardButton, Field, Input, LinkButton, Modal, Select, Spinner } from '@grafana/ui';
|
||||||
import { t, Trans } from 'app/core/internationalization';
|
import { t, Trans } from 'app/core/internationalization';
|
||||||
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||||
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
||||||
|
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||||
|
|
||||||
import { VariableRefresh } from '../../../variables/types';
|
import { VariableRefresh } from '../../../variables/types';
|
||||||
import { getDashboardSnapshotSrv } from '../../services/SnapshotSrv';
|
import { getDashboardSnapshotSrv } from '../../services/SnapshotSrv';
|
||||||
@ -116,12 +117,12 @@ export class ShareSnapshot extends PureComponent<Props, State> {
|
|||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
if (external) {
|
if (external) {
|
||||||
reportInteraction('dashboards_sharing_snapshot_publish_clicked', {
|
DashboardInteractions.publishSnapshotClicked({
|
||||||
expires: snapshotExpires,
|
expires: snapshotExpires,
|
||||||
timeout: timeoutSeconds,
|
timeout: timeoutSeconds,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
reportInteraction('dashboards_sharing_snapshot_local_clicked', {
|
DashboardInteractions.publishSnapshotLocalClicked({
|
||||||
expires: snapshotExpires,
|
expires: snapshotExpires,
|
||||||
timeout: timeoutSeconds,
|
timeout: timeoutSeconds,
|
||||||
});
|
});
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||||
|
|
||||||
import { reportInteraction } from '@grafana/runtime';
|
|
||||||
import { ClipboardButton, CodeEditor, Modal } from '@grafana/ui';
|
import { ClipboardButton, CodeEditor, Modal } from '@grafana/ui';
|
||||||
import { Trans } from 'app/core/internationalization';
|
import { Trans } from 'app/core/internationalization';
|
||||||
|
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||||
|
|
||||||
export interface ViewJsonModalProps {
|
export interface ViewJsonModalProps {
|
||||||
json: string;
|
json: string;
|
||||||
@ -22,7 +22,7 @@ export function ViewJsonModal({ json, onDismiss }: ViewJsonModalProps): JSX.Elem
|
|||||||
icon="copy"
|
icon="copy"
|
||||||
getText={getClipboardText}
|
getText={getClipboardText}
|
||||||
onClipboardCopy={() => {
|
onClipboardCopy={() => {
|
||||||
reportInteraction('dashboards_sharing_export_copy_json_clicked');
|
DashboardInteractions.exportCopyJsonClicked();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Trans i18nKey="share-modal.view-json.copy-button">Copy to Clipboard</Trans>
|
<Trans i18nKey="share-modal.view-json.copy-button">Copy to Clipboard</Trans>
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
import { reportInteraction } from '@grafana/runtime';
|
|
||||||
|
|
||||||
export const shareAnalyticsEventNames: {
|
|
||||||
[key: string]: string;
|
|
||||||
} = {
|
|
||||||
sharingCategoryClicked: 'dashboards_sharing_category_clicked',
|
|
||||||
};
|
|
||||||
|
|
||||||
export function trackDashboardSharingTypeOpen(sharingType: string) {
|
|
||||||
reportInteraction(shareAnalyticsEventNames.sharingCategoryClicked, { item: sharingType });
|
|
||||||
}
|
|
@ -3,11 +3,12 @@ import React from 'react';
|
|||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { config, locationService, reportInteraction } from '@grafana/runtime';
|
import { config, locationService } from '@grafana/runtime';
|
||||||
import { Button, useStyles2, Text, Box, Stack } from '@grafana/ui';
|
import { Button, useStyles2, Text, Box, Stack } from '@grafana/ui';
|
||||||
import { Trans } from 'app/core/internationalization';
|
import { Trans } from 'app/core/internationalization';
|
||||||
import { DashboardModel } from 'app/features/dashboard/state';
|
import { DashboardModel } from 'app/features/dashboard/state';
|
||||||
import { onAddLibraryPanel, onCreateNewPanel, onImportDashboard } from 'app/features/dashboard/utils/dashboard';
|
import { onAddLibraryPanel, onCreateNewPanel, onImportDashboard } from 'app/features/dashboard/utils/dashboard';
|
||||||
|
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||||
import { useDispatch, useSelector } from 'app/types';
|
import { useDispatch, useSelector } from 'app/types';
|
||||||
|
|
||||||
import { setInitialDatasource } from '../state/reducers';
|
import { setInitialDatasource } from '../state/reducers';
|
||||||
@ -47,7 +48,8 @@ const DashboardEmpty = ({ dashboard, canCreate }: Props) => {
|
|||||||
data-testid={selectors.pages.AddDashboard.itemButton('Create new panel button')}
|
data-testid={selectors.pages.AddDashboard.itemButton('Create new panel button')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const id = onCreateNewPanel(dashboard, initialDatasource);
|
const id = onCreateNewPanel(dashboard, initialDatasource);
|
||||||
reportInteraction('dashboards_emptydashboard_clicked', { item: 'add_visualization' });
|
DashboardInteractions.emptyDashboardButtonClicked({ item: 'add_visualization' });
|
||||||
|
|
||||||
locationService.partial({ editPanel: id, firstPanel: true });
|
locationService.partial({ editPanel: id, firstPanel: true });
|
||||||
dispatch(setInitialDatasource(undefined));
|
dispatch(setInitialDatasource(undefined));
|
||||||
}}
|
}}
|
||||||
@ -74,7 +76,7 @@ const DashboardEmpty = ({ dashboard, canCreate }: Props) => {
|
|||||||
fill="outline"
|
fill="outline"
|
||||||
data-testid={selectors.pages.AddDashboard.itemButton('Create new widget button')}
|
data-testid={selectors.pages.AddDashboard.itemButton('Create new widget button')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
reportInteraction('dashboards_emptydashboard_clicked', { item: 'add_widget' });
|
DashboardInteractions.emptyDashboardButtonClicked({ item: 'add_widget' });
|
||||||
locationService.partial({ addWidget: true });
|
locationService.partial({ addWidget: true });
|
||||||
}}
|
}}
|
||||||
disabled={!canCreate}
|
disabled={!canCreate}
|
||||||
@ -101,7 +103,7 @@ const DashboardEmpty = ({ dashboard, canCreate }: Props) => {
|
|||||||
fill="outline"
|
fill="outline"
|
||||||
data-testid={selectors.pages.AddDashboard.itemButton('Add a panel from the panel library button')}
|
data-testid={selectors.pages.AddDashboard.itemButton('Add a panel from the panel library button')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
reportInteraction('dashboards_emptydashboard_clicked', { item: 'import_from_library' });
|
DashboardInteractions.emptyDashboardButtonClicked({ item: 'import_from_library' });
|
||||||
onAddLibraryPanel(dashboard);
|
onAddLibraryPanel(dashboard);
|
||||||
}}
|
}}
|
||||||
disabled={!canCreate}
|
disabled={!canCreate}
|
||||||
@ -128,7 +130,7 @@ const DashboardEmpty = ({ dashboard, canCreate }: Props) => {
|
|||||||
fill="outline"
|
fill="outline"
|
||||||
data-testid={selectors.pages.AddDashboard.itemButton('Import dashboard button')}
|
data-testid={selectors.pages.AddDashboard.itemButton('Import dashboard button')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
reportInteraction('dashboards_emptydashboard_clicked', { item: 'import_dashboard' });
|
DashboardInteractions.emptyDashboardButtonClicked({ item: 'import_dashboard' });
|
||||||
onImportDashboard();
|
onImportDashboard();
|
||||||
}}
|
}}
|
||||||
disabled={!canCreate}
|
disabled={!canCreate}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { LinkModel, PanelData, PanelPlugin, renderMarkdown } from '@grafana/data';
|
import { LinkModel, PanelData, PanelPlugin, renderMarkdown } from '@grafana/data';
|
||||||
import { config, getTemplateSrv, locationService, reportInteraction } from '@grafana/runtime';
|
import { config, getTemplateSrv, locationService } from '@grafana/runtime';
|
||||||
import { PanelPadding } from '@grafana/ui';
|
import { PanelPadding } from '@grafana/ui';
|
||||||
|
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||||
import { InspectTab } from 'app/features/inspector/types';
|
import { InspectTab } from 'app/features/inspector/types';
|
||||||
import { getPanelLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers';
|
import { getPanelLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers';
|
||||||
import { isAngularDatasourcePluginAndNotHidden } from 'app/features/plugins/angularDeprecation/utils';
|
import { isAngularDatasourcePluginAndNotHidden } from 'app/features/plugins/angularDeprecation/utils';
|
||||||
@ -42,7 +43,7 @@ export function getPanelChromeProps(props: CommonProps) {
|
|||||||
|
|
||||||
if (!descriptionInteractionReported) {
|
if (!descriptionInteractionReported) {
|
||||||
// Description rendering function can be called multiple times due to re-renders but we want to report the interaction once.
|
// Description rendering function can be called multiple times due to re-renders but we want to report the interaction once.
|
||||||
reportInteraction('dashboards_panelheader_description_displayed');
|
DashboardInteractions.panelDescriptionShown();
|
||||||
descriptionInteractionReported = true;
|
descriptionInteractionReported = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +60,7 @@ export function getPanelChromeProps(props: CommonProps) {
|
|||||||
return panelLinks.map((panelLink) => ({
|
return panelLinks.map((panelLink) => ({
|
||||||
...panelLink,
|
...panelLink,
|
||||||
onClick: (...args) => {
|
onClick: (...args) => {
|
||||||
reportInteraction('dashboards_panelheader_datalink_clicked', { has_multiple_links: panelLinks.length > 1 });
|
DashboardInteractions.panelLinkClicked({ has_multiple_links: panelLinks.length > 1 });
|
||||||
panelLink.onClick?.(...args);
|
panelLink.onClick?.(...args);
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
@ -73,12 +74,12 @@ export function getPanelChromeProps(props: CommonProps) {
|
|||||||
const onOpenErrorInspect = (e: React.SyntheticEvent) => {
|
const onOpenErrorInspect = (e: React.SyntheticEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
locationService.partial({ inspect: props.panel.id, inspectTab: InspectTab.Error });
|
locationService.partial({ inspect: props.panel.id, inspectTab: InspectTab.Error });
|
||||||
reportInteraction('dashboards_panelheader_statusmessage_clicked');
|
DashboardInteractions.panelStatusMessageClicked();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCancelQuery = () => {
|
const onCancelQuery = () => {
|
||||||
props.panel.getQueryRunner().cancelQuery();
|
props.panel.getQueryRunner().cancelQuery();
|
||||||
reportInteraction('dashboards_panelheader_cancelquery_clicked', { data_state: props.data.state });
|
DashboardInteractions.panelCancelQueryClicked();
|
||||||
};
|
};
|
||||||
|
|
||||||
const padding: PanelPadding = props.plugin.noPadding ? 'none' : 'md';
|
const padding: PanelPadding = props.plugin.noPadding ? 'none' : 'md';
|
||||||
@ -121,7 +122,7 @@ export function getPanelChromeProps(props: CommonProps) {
|
|||||||
const title = props.panel.getDisplayTitle();
|
const title = props.panel.getDisplayTitle();
|
||||||
|
|
||||||
const onOpenMenu = () => {
|
const onOpenMenu = () => {
|
||||||
reportInteraction('dashboards_panelheader_menu', { item: 'menu' });
|
DashboardInteractions.panelMenuShown();
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
urlUtil,
|
urlUtil,
|
||||||
type PluginExtensionPanelContext,
|
type PluginExtensionPanelContext,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { AngularComponent, getPluginLinkExtensions, locationService, reportInteraction } from '@grafana/runtime';
|
import { AngularComponent, getPluginLinkExtensions, locationService } from '@grafana/runtime';
|
||||||
import { PanelCtrl } from 'app/angular/panel/panel_ctrl';
|
import { PanelCtrl } from 'app/angular/panel/panel_ctrl';
|
||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
import { t } from 'app/core/internationalization';
|
import { t } from 'app/core/internationalization';
|
||||||
@ -23,6 +23,7 @@ import {
|
|||||||
toggleLegend,
|
toggleLegend,
|
||||||
unlinkLibraryPanel,
|
unlinkLibraryPanel,
|
||||||
} from 'app/features/dashboard/utils/panel';
|
} from 'app/features/dashboard/utils/panel';
|
||||||
|
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||||
import { InspectTab } from 'app/features/inspector/types';
|
import { InspectTab } from 'app/features/inspector/types';
|
||||||
import { isPanelModelLibraryPanel } from 'app/features/library-panels/guard';
|
import { isPanelModelLibraryPanel } from 'app/features/library-panels/guard';
|
||||||
import { createExtensionSubMenu } from 'app/features/plugins/extensions/utils';
|
import { createExtensionSubMenu } from 'app/features/plugins/extensions/utils';
|
||||||
@ -43,7 +44,7 @@ export function getPanelMenu(
|
|||||||
locationService.partial({
|
locationService.partial({
|
||||||
viewPanel: panel.id,
|
viewPanel: panel.id,
|
||||||
});
|
});
|
||||||
reportInteraction('dashboards_panelheader_menu', { item: 'view' });
|
DashboardInteractions.panelMenuItemClicked('view');
|
||||||
};
|
};
|
||||||
|
|
||||||
const onEditPanel = (event: React.MouseEvent) => {
|
const onEditPanel = (event: React.MouseEvent) => {
|
||||||
@ -52,25 +53,25 @@ export function getPanelMenu(
|
|||||||
editPanel: panel.id,
|
editPanel: panel.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
reportInteraction('dashboards_panelheader_menu', { item: 'edit' });
|
DashboardInteractions.panelMenuItemClicked('edit');
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSharePanel = (event: React.MouseEvent) => {
|
const onSharePanel = (event: React.MouseEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
sharePanel(dashboard, panel);
|
sharePanel(dashboard, panel);
|
||||||
reportInteraction('dashboards_panelheader_menu', { item: 'share' });
|
DashboardInteractions.panelMenuItemClicked('share');
|
||||||
};
|
};
|
||||||
|
|
||||||
const onAddLibraryPanel = (event: React.MouseEvent) => {
|
const onAddLibraryPanel = (event: React.MouseEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
addLibraryPanel(dashboard, panel);
|
addLibraryPanel(dashboard, panel);
|
||||||
reportInteraction('dashboards_panelheader_menu', { item: 'createLibraryPanel' });
|
DashboardInteractions.panelMenuItemClicked('createLibraryPanel');
|
||||||
};
|
};
|
||||||
|
|
||||||
const onUnlinkLibraryPanel = (event: React.MouseEvent) => {
|
const onUnlinkLibraryPanel = (event: React.MouseEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
unlinkLibraryPanel(panel);
|
unlinkLibraryPanel(panel);
|
||||||
reportInteraction('dashboards_panelheader_menu', { item: 'unlinkLibraryPanel' });
|
DashboardInteractions.panelMenuItemClicked('unlinkLibraryPanel');
|
||||||
};
|
};
|
||||||
|
|
||||||
const onInspectPanel = (tab?: InspectTab) => {
|
const onInspectPanel = (tab?: InspectTab) => {
|
||||||
@ -78,7 +79,7 @@ export function getPanelMenu(
|
|||||||
inspect: panel.id,
|
inspect: panel.id,
|
||||||
inspectTab: tab,
|
inspectTab: tab,
|
||||||
});
|
});
|
||||||
reportInteraction('dashboards_panelheader_menu', { item: 'inspect', tab: tab ?? InspectTab.Data });
|
DashboardInteractions.panelMenuInspectClicked(tab ?? InspectTab.Data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onMore = (event: React.MouseEvent) => {
|
const onMore = (event: React.MouseEvent) => {
|
||||||
@ -88,19 +89,19 @@ export function getPanelMenu(
|
|||||||
const onDuplicatePanel = (event: React.MouseEvent) => {
|
const onDuplicatePanel = (event: React.MouseEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
duplicatePanel(dashboard, panel);
|
duplicatePanel(dashboard, panel);
|
||||||
reportInteraction('dashboards_panelheader_menu', { item: 'duplicate' });
|
DashboardInteractions.panelMenuItemClicked('duplicate');
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCopyPanel = (event: React.MouseEvent) => {
|
const onCopyPanel = (event: React.MouseEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
copyPanel(panel);
|
copyPanel(panel);
|
||||||
reportInteraction('dashboards_panelheader_menu', { item: 'copy' });
|
DashboardInteractions.panelMenuItemClicked('copy');
|
||||||
};
|
};
|
||||||
|
|
||||||
const onRemovePanel = (event: React.MouseEvent) => {
|
const onRemovePanel = (event: React.MouseEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
removePanel(dashboard, panel, true);
|
removePanel(dashboard, panel, true);
|
||||||
reportInteraction('dashboards_panelheader_menu', { item: 'remove' });
|
DashboardInteractions.panelMenuItemClicked('remove');
|
||||||
};
|
};
|
||||||
|
|
||||||
const onNavigateToExplore = (event: React.MouseEvent) => {
|
const onNavigateToExplore = (event: React.MouseEvent) => {
|
||||||
@ -114,13 +115,13 @@ export function getPanelMenu(
|
|||||||
openInNewWindow,
|
openInNewWindow,
|
||||||
}) as any
|
}) as any
|
||||||
);
|
);
|
||||||
reportInteraction('dashboards_panelheader_menu', { item: 'explore' });
|
DashboardInteractions.panelMenuItemClicked('explore');
|
||||||
};
|
};
|
||||||
|
|
||||||
const onToggleLegend = (event: React.MouseEvent) => {
|
const onToggleLegend = (event: React.MouseEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
toggleLegend(panel);
|
toggleLegend(panel);
|
||||||
reportInteraction('dashboards_panelheader_menu', { item: 'toggleLegend' });
|
DashboardInteractions.panelMenuItemClicked('toggleLegend');
|
||||||
};
|
};
|
||||||
|
|
||||||
const menu: PanelMenuItem[] = [];
|
const menu: PanelMenuItem[] = [];
|
||||||
@ -218,7 +219,7 @@ export function getPanelMenu(
|
|||||||
const onCreateAlert = (event: React.MouseEvent) => {
|
const onCreateAlert = (event: React.MouseEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
createAlert();
|
createAlert();
|
||||||
reportInteraction('dashboards_panelheader_menu', { item: 'create-alert' });
|
DashboardInteractions.panelMenuItemClicked('create-alert');
|
||||||
};
|
};
|
||||||
|
|
||||||
const subMenu: PanelMenuItem[] = [];
|
const subMenu: PanelMenuItem[] = [];
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { reportInteraction } from '@grafana/runtime';
|
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||||
|
|
||||||
import { DashboardModel } from '../state';
|
import { DashboardModel } from '../state';
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ export function trackDashboardLoaded(dashboard: DashboardModel, versionBeforeMig
|
|||||||
return r;
|
return r;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
reportInteraction('dashboards_init_dashboard_completed', {
|
DashboardInteractions.dashboardInitialized({
|
||||||
uid: dashboard.uid,
|
uid: dashboard.uid,
|
||||||
title: dashboard.title,
|
title: dashboard.title,
|
||||||
theme: dashboard.style,
|
theme: dashboard.style,
|
||||||
|
10
yarn.lock
10
yarn.lock
@ -3302,9 +3302,9 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
"@grafana/scenes@npm:^1.26.0":
|
"@grafana/scenes@npm:1.27.0":
|
||||||
version: 1.26.0
|
version: 1.27.0
|
||||||
resolution: "@grafana/scenes@npm:1.26.0"
|
resolution: "@grafana/scenes@npm:1.27.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@grafana/e2e-selectors": "npm:10.0.2"
|
"@grafana/e2e-selectors": "npm:10.0.2"
|
||||||
react-grid-layout: "npm:1.3.4"
|
react-grid-layout: "npm:1.3.4"
|
||||||
@ -3316,7 +3316,7 @@ __metadata:
|
|||||||
"@grafana/runtime": 10.0.3
|
"@grafana/runtime": 10.0.3
|
||||||
"@grafana/schema": 10.0.3
|
"@grafana/schema": 10.0.3
|
||||||
"@grafana/ui": 10.0.3
|
"@grafana/ui": 10.0.3
|
||||||
checksum: a4d00396552ef048c28a51d4a4d36a7c9a9fb3ca193641002b279a33a5e85c10b1e63b859c0f42c6cc34992016fb18c10566817ac04c637dc48a0e85d21a93ab
|
checksum: e19aa28d6297316676cb4805dde9bc3a52d2d28a3231e2fcdedf610356fc5c1b24eecfec36e8089cf4defc388705d47fce32736f8a86c9176e5bd4e4b7da9acb
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -17332,7 +17332,7 @@ __metadata:
|
|||||||
"@grafana/lezer-traceql": "npm:0.0.11"
|
"@grafana/lezer-traceql": "npm:0.0.11"
|
||||||
"@grafana/monaco-logql": "npm:^0.0.7"
|
"@grafana/monaco-logql": "npm:^0.0.7"
|
||||||
"@grafana/runtime": "workspace:*"
|
"@grafana/runtime": "workspace:*"
|
||||||
"@grafana/scenes": "npm:^1.26.0"
|
"@grafana/scenes": "npm:1.27.0"
|
||||||
"@grafana/schema": "workspace:*"
|
"@grafana/schema": "workspace:*"
|
||||||
"@grafana/tsconfig": "npm:^1.3.0-rc1"
|
"@grafana/tsconfig": "npm:^1.3.0-rc1"
|
||||||
"@grafana/ui": "workspace:*"
|
"@grafana/ui": "workspace:*"
|
||||||
|
Loading…
Reference in New Issue
Block a user