mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboard: Add analytics events for sharing a dashboard (#72613)
* activeTab for public dashboard matches others * add analytics action for going through categories of sharing modal * add analytics for going through actions of any cateogry in sharing modal * change 'tab' to 'category'; add key 'sharing_category' to actions for easier distinction * sharedCategory sounds like a category is shared; changed naming to shareDashboardType * remove hard-coded analytic event name * wrap the reportInteraction call for all actions when sharing dashboard --------- Co-authored-by: Alexandra Vargas <alexa1866@gmail.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { saveAs } from 'file-saver';
|
import { saveAs } from 'file-saver';
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
import { config, reportInteraction } from '@grafana/runtime';
|
import { config } 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';
|
||||||
@@ -10,7 +10,9 @@ import { DashboardExporter } from 'app/features/dashboard/components/DashExportM
|
|||||||
import { ShowModalReactEvent } from 'app/types/events';
|
import { ShowModalReactEvent } from 'app/types/events';
|
||||||
|
|
||||||
import { ViewJsonModal } from './ViewJsonModal';
|
import { ViewJsonModal } from './ViewJsonModal';
|
||||||
|
import { trackDashboardSharingActionPerType } from './analytics';
|
||||||
import { ShareModalTabProps } from './types';
|
import { ShareModalTabProps } from './types';
|
||||||
|
import { shareDashboardType } from './utils';
|
||||||
|
|
||||||
interface Props extends ShareModalTabProps {}
|
interface Props extends ShareModalTabProps {}
|
||||||
|
|
||||||
@@ -32,10 +34,6 @@ export class ShareExport extends PureComponent<Props, State> {
|
|||||||
this.exporter = new DashboardExporter();
|
this.exporter = new DashboardExporter();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
reportInteraction('grafana_dashboards_export_share_viewed');
|
|
||||||
}
|
|
||||||
|
|
||||||
onShareExternallyChange = () => {
|
onShareExternallyChange = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
shareExternally: !this.state.shareExternally,
|
shareExternally: !this.state.shareExternally,
|
||||||
@@ -115,6 +113,7 @@ export class ShareExport extends PureComponent<Props, State> {
|
|||||||
});
|
});
|
||||||
const time = new Date().getTime();
|
const time = new Date().getTime();
|
||||||
saveAs(blob, `${dash.title}-${time}.json`);
|
saveAs(blob, `${dash.title}-${time}.json`);
|
||||||
|
trackDashboardSharingActionPerType('save_export', shareDashboardType.export);
|
||||||
};
|
};
|
||||||
|
|
||||||
openJsonModal = (clone: object) => {
|
openJsonModal = (clone: object) => {
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
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/src';
|
|
||||||
import { Alert, ClipboardButton, Field, FieldSet, Icon, Input, Switch } from '@grafana/ui';
|
import { Alert, ClipboardButton, Field, FieldSet, Icon, Input, Switch } 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 { ThemePicker } from './ThemePicker';
|
import { ThemePicker } from './ThemePicker';
|
||||||
|
import { trackDashboardSharingActionPerType } from './analytics';
|
||||||
import { ShareModalTabProps } from './types';
|
import { ShareModalTabProps } from './types';
|
||||||
import { buildImageUrl, buildShareUrl } from './utils';
|
import { buildImageUrl, buildShareUrl, shareDashboardType } from './utils';
|
||||||
|
|
||||||
export interface Props extends ShareModalTabProps {}
|
export interface Props extends ShareModalTabProps {}
|
||||||
|
|
||||||
@@ -33,7 +33,6 @@ export class ShareLink extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
reportInteraction('grafana_dashboards_link_share_viewed');
|
|
||||||
this.buildUrl();
|
this.buildUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +73,10 @@ export class ShareLink extends PureComponent<Props, State> {
|
|||||||
return this.state.shareUrl;
|
return this.state.shareUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onCopy() {
|
||||||
|
trackDashboardSharingActionPerType('copy_link', shareDashboardType.link);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { panel, dashboard } = this.props;
|
const { panel, dashboard } = this.props;
|
||||||
const isRelativeTime = dashboard ? dashboard.time.to === 'now' : false;
|
const isRelativeTime = dashboard ? dashboard.time.to === 'now' : false;
|
||||||
@@ -118,7 +121,7 @@ export class ShareLink extends PureComponent<Props, State> {
|
|||||||
value={shareUrl}
|
value={shareUrl}
|
||||||
readOnly
|
readOnly
|
||||||
addonAfter={
|
addonAfter={
|
||||||
<ClipboardButton icon="copy" variant="primary" getText={this.getShareUrl}>
|
<ClipboardButton icon="copy" variant="primary" getText={this.getShareUrl} onClipboardCopy={this.onCopy}>
|
||||||
<Trans i18nKey="share-modal.link.copy-link-button">Copy</Trans>
|
<Trans i18nKey="share-modal.link.copy-link-button">Copy</Trans>
|
||||||
</ClipboardButton>
|
</ClipboardButton>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { css } from '@emotion/css';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { reportInteraction } from '@grafana/runtime/src';
|
|
||||||
import { Modal, ModalTabsHeader, TabContent, Themeable2, withTheme2 } from '@grafana/ui';
|
import { Modal, ModalTabsHeader, TabContent, Themeable2, withTheme2 } from '@grafana/ui';
|
||||||
import { config } from 'app/core/config';
|
import { config } from 'app/core/config';
|
||||||
import { contextSrv } from 'app/core/core';
|
import { contextSrv } from 'app/core/core';
|
||||||
@@ -16,7 +15,9 @@ 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';
|
||||||
|
|
||||||
const customDashboardTabs: ShareModalTabModel[] = [];
|
const customDashboardTabs: ShareModalTabModel[] = [];
|
||||||
const customPanelTabs: ShareModalTabModel[] = [];
|
const customPanelTabs: ShareModalTabModel[] = [];
|
||||||
@@ -31,30 +32,38 @@ export function addPanelShareTab(tab: ShareModalTabModel) {
|
|||||||
|
|
||||||
function getTabs(panel?: PanelModel, activeTab?: string) {
|
function getTabs(panel?: PanelModel, activeTab?: string) {
|
||||||
const linkLabel = t('share-modal.tab-title.link', 'Link');
|
const linkLabel = t('share-modal.tab-title.link', 'Link');
|
||||||
const tabs: ShareModalTabModel[] = [{ label: linkLabel, value: 'link', component: ShareLink }];
|
const tabs: ShareModalTabModel[] = [{ label: linkLabel, value: shareDashboardType.link, component: ShareLink }];
|
||||||
|
|
||||||
if (contextSrv.isSignedIn && config.snapshotEnabled) {
|
if (contextSrv.isSignedIn && config.snapshotEnabled) {
|
||||||
const snapshotLabel = t('share-modal.tab-title.snapshot', 'Snapshot');
|
const snapshotLabel = t('share-modal.tab-title.snapshot', 'Snapshot');
|
||||||
tabs.push({ label: snapshotLabel, value: 'snapshot', component: ShareSnapshot });
|
tabs.push({ label: snapshotLabel, value: shareDashboardType.snapshot, component: ShareSnapshot });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (panel) {
|
if (panel) {
|
||||||
const embedLabel = t('share-modal.tab-title.embed', 'Embed');
|
const embedLabel = t('share-modal.tab-title.embed', 'Embed');
|
||||||
tabs.push({ label: embedLabel, value: 'embed', component: ShareEmbed });
|
tabs.push({ label: embedLabel, value: shareDashboardType.embed, component: ShareEmbed });
|
||||||
|
|
||||||
if (!isPanelModelLibraryPanel(panel)) {
|
if (!isPanelModelLibraryPanel(panel)) {
|
||||||
const libraryPanelLabel = t('share-modal.tab-title.library-panel', 'Library panel');
|
const libraryPanelLabel = t('share-modal.tab-title.library-panel', 'Library panel');
|
||||||
tabs.push({ label: libraryPanelLabel, value: 'library_panel', component: ShareLibraryPanel });
|
tabs.push({ label: libraryPanelLabel, value: shareDashboardType.libraryPanel, component: ShareLibraryPanel });
|
||||||
}
|
}
|
||||||
tabs.push(...customPanelTabs);
|
tabs.push(...customPanelTabs);
|
||||||
} else {
|
} else {
|
||||||
const exportLabel = t('share-modal.tab-title.export', 'Export');
|
const exportLabel = t('share-modal.tab-title.export', 'Export');
|
||||||
tabs.push({ label: exportLabel, value: 'export', component: ShareExport });
|
tabs.push({
|
||||||
|
label: exportLabel,
|
||||||
|
value: shareDashboardType.export,
|
||||||
|
component: ShareExport,
|
||||||
|
});
|
||||||
tabs.push(...customDashboardTabs);
|
tabs.push(...customDashboardTabs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Boolean(config.featureToggles['publicDashboards'])) {
|
if (Boolean(config.featureToggles['publicDashboards'])) {
|
||||||
tabs.push({ label: 'Public dashboard', value: 'public-dashboard', component: SharePublicDashboard });
|
tabs.push({
|
||||||
|
label: 'Public dashboard',
|
||||||
|
value: shareDashboardType.publicDashboard,
|
||||||
|
component: SharePublicDashboard,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const at = tabs.find((t) => t.value === activeTab);
|
const at = tabs.find((t) => t.value === activeTab);
|
||||||
@@ -93,12 +102,9 @@ class UnthemedShareModal extends React.Component<Props, State> {
|
|||||||
this.state = getInitialState(props);
|
this.state = getInitialState(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
reportInteraction('grafana_dashboards_share_modal_viewed');
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
getActiveTab() {
|
getActiveTab() {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useForm } 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 { config, featureEnabled, reportInteraction } from '@grafana/runtime/src';
|
import { config, featureEnabled } from '@grafana/runtime/src';
|
||||||
import {
|
import {
|
||||||
ClipboardButton,
|
ClipboardButton,
|
||||||
Field,
|
Field,
|
||||||
@@ -25,6 +25,8 @@ import { isOrgAdmin } from '../../../../../plugins/admin/permissions';
|
|||||||
import { useGetPublicDashboardQuery, useUpdatePublicDashboardMutation } from '../../../../api/publicDashboardApi';
|
import { useGetPublicDashboardQuery, useUpdatePublicDashboardMutation } from '../../../../api/publicDashboardApi';
|
||||||
import { useIsDesktop } from '../../../../utils/screen';
|
import { useIsDesktop } from '../../../../utils/screen';
|
||||||
import { ShareModal } from '../../ShareModal';
|
import { ShareModal } from '../../ShareModal';
|
||||||
|
import { trackDashboardSharingActionPerType } from '../../analytics';
|
||||||
|
import { shareDashboardType } from '../../utils';
|
||||||
import { NoUpsertPermissionsAlert } from '../ModalAlerts/NoUpsertPermissionsAlert';
|
import { NoUpsertPermissionsAlert } from '../ModalAlerts/NoUpsertPermissionsAlert';
|
||||||
import { SaveDashboardChangesAlert } from '../ModalAlerts/SaveDashboardChangesAlert';
|
import { SaveDashboardChangesAlert } from '../ModalAlerts/SaveDashboardChangesAlert';
|
||||||
import { UnsupportedDataSourcesAlert } from '../ModalAlerts/UnsupportedDataSourcesAlert';
|
import { UnsupportedDataSourcesAlert } from '../ModalAlerts/UnsupportedDataSourcesAlert';
|
||||||
@@ -100,10 +102,14 @@ const ConfigPublicDashboard = () => {
|
|||||||
showModal(ShareModal, {
|
showModal(ShareModal, {
|
||||||
dashboard,
|
dashboard,
|
||||||
onDismiss: hideModal,
|
onDismiss: hideModal,
|
||||||
activeTab: 'public-dashboard',
|
activeTab: shareDashboardType.publicDashboard,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function onCopyURL() {
|
||||||
|
trackDashboardSharingActionPerType('copy_public_url', shareDashboardType.publicDashboard);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.configContainer}>
|
<div className={styles.configContainer}>
|
||||||
{hasWritePermissions && dashboard.hasUnsavedChanges() && <SaveDashboardChangesAlert />}
|
{hasWritePermissions && dashboard.hasUnsavedChanges() && <SaveDashboardChangesAlert />}
|
||||||
@@ -127,6 +133,7 @@ const ConfigPublicDashboard = () => {
|
|||||||
variant="primary"
|
variant="primary"
|
||||||
disabled={!publicDashboard?.isEnabled}
|
disabled={!publicDashboard?.isEnabled}
|
||||||
getText={() => generatePublicDashboardUrl(publicDashboard!.accessToken!)}
|
getText={() => generatePublicDashboardUrl(publicDashboard!.accessToken!)}
|
||||||
|
onClipboardCopy={onCopyURL}
|
||||||
>
|
>
|
||||||
Copy
|
Copy
|
||||||
</ClipboardButton>
|
</ClipboardButton>
|
||||||
@@ -140,9 +147,10 @@ const ConfigPublicDashboard = () => {
|
|||||||
{...register('isPaused')}
|
{...register('isPaused')}
|
||||||
disabled={disableInputs}
|
disabled={disableInputs}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
reportInteraction('grafana_dashboards_public_enable_clicked', {
|
trackDashboardSharingActionPerType(
|
||||||
action: e.currentTarget.checked ? 'disable' : 'enable',
|
e.currentTarget.checked ? 'disable_sharing' : 'enable_sharing',
|
||||||
});
|
shareDashboardType.publicDashboard
|
||||||
|
);
|
||||||
onChange('isPaused', e.currentTarget.checked);
|
onChange('isPaused', e.currentTarget.checked);
|
||||||
}}
|
}}
|
||||||
data-testid={selectors.PauseSwitch}
|
data-testid={selectors.PauseSwitch}
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ 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/src';
|
|
||||||
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 { trackDashboardSharingActionPerType } from '../../analytics';
|
||||||
|
import { shareDashboardType } from '../../utils';
|
||||||
|
|
||||||
import { ConfigPublicDashboardForm } from './ConfigPublicDashboard';
|
import { ConfigPublicDashboardForm } from './ConfigPublicDashboard';
|
||||||
|
|
||||||
const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard;
|
const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard;
|
||||||
@@ -37,9 +39,10 @@ export const Configuration = ({
|
|||||||
{...register('isTimeSelectionEnabled')}
|
{...register('isTimeSelectionEnabled')}
|
||||||
data-testid={selectors.EnableTimeRangeSwitch}
|
data-testid={selectors.EnableTimeRangeSwitch}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
reportInteraction('grafana_dashboards_public_time_selection_clicked', {
|
trackDashboardSharingActionPerType(
|
||||||
action: e.currentTarget.checked ? 'enable' : 'disable',
|
e.currentTarget.checked ? 'enable_time' : 'disable_time',
|
||||||
});
|
shareDashboardType.publicDashboard
|
||||||
|
);
|
||||||
onChange('isTimeSelectionEnabled', e.currentTarget.checked);
|
onChange('isTimeSelectionEnabled', e.currentTarget.checked);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -49,9 +52,10 @@ export const Configuration = ({
|
|||||||
<Switch
|
<Switch
|
||||||
{...register('isAnnotationsEnabled')}
|
{...register('isAnnotationsEnabled')}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
reportInteraction('grafana_dashboards_public_annotations_clicked', {
|
trackDashboardSharingActionPerType(
|
||||||
action: e.currentTarget.checked ? 'enable' : 'disable',
|
e.currentTarget.checked ? 'enable_annotations' : 'disable_annotations',
|
||||||
});
|
shareDashboardType.publicDashboard
|
||||||
|
);
|
||||||
onChange('isAnnotationsEnabled', e.currentTarget.checked);
|
onChange('isAnnotationsEnabled', e.currentTarget.checked);
|
||||||
}}
|
}}
|
||||||
data-testid={selectors.EnableAnnotationsSwitch}
|
data-testid={selectors.EnableAnnotationsSwitch}
|
||||||
|
|||||||
@@ -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/src';
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
ButtonGroup,
|
ButtonGroup,
|
||||||
@@ -25,6 +24,8 @@ import {
|
|||||||
} from 'app/features/dashboard/api/publicDashboardApi';
|
} from 'app/features/dashboard/api/publicDashboardApi';
|
||||||
import { useSelector } from 'app/types';
|
import { useSelector } from 'app/types';
|
||||||
|
|
||||||
|
import { trackDashboardSharingActionPerType } from '../../analytics';
|
||||||
|
import { shareDashboardType } from '../../utils';
|
||||||
import { PublicDashboard, PublicDashboardShareType, validEmailRegex } from '../SharePublicDashboardUtils';
|
import { PublicDashboard, PublicDashboardShareType, validEmailRegex } from '../SharePublicDashboardUtils';
|
||||||
|
|
||||||
interface EmailSharingConfigurationForm {
|
interface EmailSharingConfigurationForm {
|
||||||
@@ -55,12 +56,12 @@ const EmailList = ({
|
|||||||
const isLoading = isDeleteLoading || isReshareLoading;
|
const isLoading = isDeleteLoading || isReshareLoading;
|
||||||
|
|
||||||
const onDeleteEmail = (recipientUid: string) => {
|
const onDeleteEmail = (recipientUid: string) => {
|
||||||
reportInteraction('grafana_dashboards_public_delete_sharing_email_clicked');
|
trackDashboardSharingActionPerType('delete_email', shareDashboardType.publicDashboard);
|
||||||
deleteEmail({ recipientUid, dashboardUid: dashboardUid, uid: publicDashboardUid });
|
deleteEmail({ recipientUid, dashboardUid: dashboardUid, uid: publicDashboardUid });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onReshare = (recipientUid: string) => {
|
const onReshare = (recipientUid: string) => {
|
||||||
reportInteraction('grafana_dashboards_public_reshare_email_clicked');
|
trackDashboardSharingActionPerType('reshare_email', shareDashboardType.publicDashboard);
|
||||||
reshareAccess({ recipientUid, uid: publicDashboardUid });
|
reshareAccess({ recipientUid, uid: publicDashboardUid });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -133,7 +134,7 @@ export const EmailSharingConfiguration = () => {
|
|||||||
mode: 'onSubmit',
|
mode: 'onSubmit',
|
||||||
});
|
});
|
||||||
|
|
||||||
const onShareTypeChange = (shareType: PublicDashboardShareType) => {
|
const onUpdateShareType = (shareType: PublicDashboardShareType) => {
|
||||||
const req = {
|
const req = {
|
||||||
dashboard,
|
dashboard,
|
||||||
payload: {
|
payload: {
|
||||||
@@ -147,7 +148,7 @@ export const EmailSharingConfiguration = () => {
|
|||||||
|
|
||||||
const onSubmit = async (data: EmailSharingConfigurationForm) => {
|
const onSubmit = async (data: EmailSharingConfigurationForm) => {
|
||||||
//TODO: add if it's domain or not when developed.
|
//TODO: add if it's domain or not when developed.
|
||||||
reportInteraction('grafana_dashboards_public_add_share_email_clicked');
|
trackDashboardSharingActionPerType('invite_email', shareDashboardType.publicDashboard);
|
||||||
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 });
|
||||||
};
|
};
|
||||||
@@ -166,11 +167,12 @@ export const EmailSharingConfiguration = () => {
|
|||||||
size={width < 480 ? 'sm' : 'md'}
|
size={width < 480 ? 'sm' : 'md'}
|
||||||
options={options}
|
options={options}
|
||||||
onChange={(shareType: PublicDashboardShareType) => {
|
onChange={(shareType: PublicDashboardShareType) => {
|
||||||
reportInteraction('grafana_dashboards_public_share_type_clicked', {
|
trackDashboardSharingActionPerType(
|
||||||
type: shareType,
|
`share_type_${shareType === PublicDashboardShareType.EMAIL ? 'email' : 'public'}`,
|
||||||
});
|
shareDashboardType.publicDashboard
|
||||||
|
);
|
||||||
setValue('shareType', shareType);
|
setValue('shareType', shareType);
|
||||||
onShareTypeChange(shareType);
|
onUpdateShareType(shareType);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ 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/src';
|
|
||||||
import { Button, Form, Spinner, useStyles2 } from '@grafana/ui/src';
|
import { Button, Form, Spinner, useStyles2 } from '@grafana/ui/src';
|
||||||
|
|
||||||
import { contextSrv } from '../../../../../../core/services/context_srv';
|
import { contextSrv } from '../../../../../../core/services/context_srv';
|
||||||
import { AccessControlAction, useSelector } from '../../../../../../types';
|
import { AccessControlAction, useSelector } from '../../../../../../types';
|
||||||
import { isOrgAdmin } from '../../../../../plugins/admin/permissions';
|
import { isOrgAdmin } from '../../../../../plugins/admin/permissions';
|
||||||
import { useCreatePublicDashboardMutation } from '../../../../api/publicDashboardApi';
|
import { useCreatePublicDashboardMutation } from '../../../../api/publicDashboardApi';
|
||||||
|
import { trackDashboardSharingActionPerType } from '../../analytics';
|
||||||
|
import { shareDashboardType } from '../../utils';
|
||||||
import { NoUpsertPermissionsAlert } from '../ModalAlerts/NoUpsertPermissionsAlert';
|
import { NoUpsertPermissionsAlert } from '../ModalAlerts/NoUpsertPermissionsAlert';
|
||||||
import { UnsupportedDataSourcesAlert } from '../ModalAlerts/UnsupportedDataSourcesAlert';
|
import { UnsupportedDataSourcesAlert } from '../ModalAlerts/UnsupportedDataSourcesAlert';
|
||||||
import { UnsupportedTemplateVariablesAlert } from '../ModalAlerts/UnsupportedTemplateVariablesAlert';
|
import { UnsupportedTemplateVariablesAlert } from '../ModalAlerts/UnsupportedTemplateVariablesAlert';
|
||||||
@@ -39,7 +40,7 @@ const CreatePublicDashboard = ({ isError }: { isError: boolean }) => {
|
|||||||
const disableInputs = !hasWritePermissions || isSaveLoading || isError;
|
const disableInputs = !hasWritePermissions || isSaveLoading || isError;
|
||||||
|
|
||||||
const onCreate = async () => {
|
const onCreate = async () => {
|
||||||
reportInteraction('grafana_dashboards_public_create_clicked');
|
trackDashboardSharingActionPerType('generate_public_url', shareDashboardType.publicDashboard);
|
||||||
createPublicDashboard({ dashboard, payload: { isEnabled: true } });
|
createPublicDashboard({ dashboard, payload: { isEnabled: true } });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { setupServer } from 'msw/node';
|
|||||||
import 'whatwg-fetch';
|
import 'whatwg-fetch';
|
||||||
import { BootData, DataQuery } from '@grafana/data/src';
|
import { BootData, DataQuery } from '@grafana/data/src';
|
||||||
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
|
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
|
||||||
import { reportInteraction, setEchoSrv } from '@grafana/runtime';
|
import { setEchoSrv } from '@grafana/runtime';
|
||||||
import { Panel } from '@grafana/schema';
|
import { Panel } from '@grafana/schema';
|
||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
import { backendSrv } from 'app/core/services/backend_srv';
|
import { backendSrv } from 'app/core/services/backend_srv';
|
||||||
@@ -14,6 +14,9 @@ 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 { trackDashboardSharingTypeOpen, trackDashboardSharingActionPerType } from '../analytics';
|
||||||
|
import { shareDashboardType } from '../utils';
|
||||||
|
|
||||||
import * as sharePublicDashboardUtils from './SharePublicDashboardUtils';
|
import * as sharePublicDashboardUtils from './SharePublicDashboardUtils';
|
||||||
import {
|
import {
|
||||||
getExistentPublicDashboardResponse,
|
getExistentPublicDashboardResponse,
|
||||||
@@ -27,7 +30,12 @@ const server = setupServer();
|
|||||||
jest.mock('@grafana/runtime', () => ({
|
jest.mock('@grafana/runtime', () => ({
|
||||||
...jest.requireActual('@grafana/runtime'),
|
...jest.requireActual('@grafana/runtime'),
|
||||||
getBackendSrv: () => backendSrv,
|
getBackendSrv: () => backendSrv,
|
||||||
reportInteraction: jest.fn(),
|
}));
|
||||||
|
|
||||||
|
jest.mock('../analytics', () => ({
|
||||||
|
...jest.requireActual('../analytics'),
|
||||||
|
trackDashboardSharingTypeOpen: jest.fn(),
|
||||||
|
trackDashboardSharingActionPerType: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard;
|
const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard;
|
||||||
@@ -334,6 +342,15 @@ describe('SharePublic - Report interactions', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('reports interaction when public dashboard tab is clicked', async () => {
|
||||||
|
await renderSharePublicDashboard();
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(trackDashboardSharingTypeOpen).toHaveBeenCalledTimes(1);
|
||||||
|
expect(trackDashboardSharingTypeOpen).lastCalledWith(shareDashboardType.publicDashboard);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('reports interaction when time range is clicked', async () => {
|
it('reports interaction when time range is clicked', async () => {
|
||||||
await renderSharePublicDashboard();
|
await renderSharePublicDashboard();
|
||||||
await userEvent.click(screen.getByText('Settings'));
|
await userEvent.click(screen.getByText('Settings'));
|
||||||
@@ -344,11 +361,15 @@ describe('SharePublic - Report interactions', () => {
|
|||||||
await userEvent.click(screen.getByTestId(selectors.EnableTimeRangeSwitch));
|
await userEvent.click(screen.getByTestId(selectors.EnableTimeRangeSwitch));
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(reportInteraction).toHaveBeenCalledWith('grafana_dashboards_public_time_selection_clicked', {
|
expect(trackDashboardSharingActionPerType).toHaveBeenCalledTimes(1);
|
||||||
action: pubdashResponse.timeSelectionEnabled ? 'disable' : 'enable',
|
// if time range was enabled, then the item is now disable_time
|
||||||
});
|
expect(trackDashboardSharingActionPerType).toHaveBeenLastCalledWith(
|
||||||
|
pubdashResponse.timeSelectionEnabled ? 'disable_time' : 'enable_time',
|
||||||
|
shareDashboardType.publicDashboard
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports interaction when show annotations is clicked', async () => {
|
it('reports interaction when show annotations is clicked', async () => {
|
||||||
await renderSharePublicDashboard();
|
await renderSharePublicDashboard();
|
||||||
await userEvent.click(screen.getByText('Settings'));
|
await userEvent.click(screen.getByText('Settings'));
|
||||||
@@ -359,9 +380,12 @@ describe('SharePublic - Report interactions', () => {
|
|||||||
await userEvent.click(screen.getByTestId(selectors.EnableAnnotationsSwitch));
|
await userEvent.click(screen.getByTestId(selectors.EnableAnnotationsSwitch));
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(reportInteraction).toHaveBeenCalledWith('grafana_dashboards_public_annotations_clicked', {
|
expect(trackDashboardSharingActionPerType).toHaveBeenCalledTimes(1);
|
||||||
action: pubdashResponse.annotationsEnabled ? 'disable' : 'enable',
|
// if annotations was enabled, then the item is now disable_annotations
|
||||||
});
|
expect(trackDashboardSharingActionPerType).toHaveBeenCalledWith(
|
||||||
|
pubdashResponse.annotationsEnabled ? 'disable_annotations' : 'enable_annotations',
|
||||||
|
shareDashboardType.publicDashboard
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('reports interaction when pause is clicked', async () => {
|
it('reports interaction when pause is clicked', async () => {
|
||||||
@@ -372,9 +396,12 @@ describe('SharePublic - Report interactions', () => {
|
|||||||
await userEvent.click(screen.getByTestId(selectors.PauseSwitch));
|
await userEvent.click(screen.getByTestId(selectors.PauseSwitch));
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(reportInteraction).toHaveBeenCalledWith('grafana_dashboards_public_enable_clicked', {
|
expect(trackDashboardSharingActionPerType).toHaveBeenCalledTimes(1);
|
||||||
action: pubdashResponse.isEnabled ? 'disable' : 'enable',
|
// if sharing was enabled, then the item is now disable_sharing
|
||||||
});
|
expect(trackDashboardSharingActionPerType).toHaveBeenLastCalledWith(
|
||||||
|
pubdashResponse.isEnabled ? 'disable_sharing' : 'enable_sharing',
|
||||||
|
shareDashboardType.publicDashboard
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import React, { useEffect } from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data/src';
|
import { GrafanaTheme2 } from '@grafana/data/src';
|
||||||
import { reportInteraction } from '@grafana/runtime/src';
|
|
||||||
import { Spinner, useStyles2 } from '@grafana/ui/src';
|
import { Spinner, useStyles2 } from '@grafana/ui/src';
|
||||||
import { useGetPublicDashboardQuery } from 'app/features/dashboard/api/publicDashboardApi';
|
import { useGetPublicDashboardQuery } from 'app/features/dashboard/api/publicDashboardApi';
|
||||||
import { publicDashboardPersisted } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils';
|
import { publicDashboardPersisted } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils';
|
||||||
@@ -30,10 +29,6 @@ const Loader = () => {
|
|||||||
export const SharePublicDashboard = (props: Props) => {
|
export const SharePublicDashboard = (props: Props) => {
|
||||||
const { data: publicDashboard, isLoading, isError } = useGetPublicDashboardQuery(props.dashboard.uid);
|
const { data: publicDashboard, isLoading, isError } = useGetPublicDashboardQuery(props.dashboard.uid);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
reportInteraction('grafana_dashboards_public_share_viewed');
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
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';
|
||||||
@@ -9,7 +9,9 @@ import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
|||||||
|
|
||||||
import { VariableRefresh } from '../../../variables/types';
|
import { VariableRefresh } from '../../../variables/types';
|
||||||
|
|
||||||
|
import { trackDashboardSharingActionPerType } from './analytics';
|
||||||
import { ShareModalTabProps } from './types';
|
import { ShareModalTabProps } from './types';
|
||||||
|
import { shareDashboardType } from './utils';
|
||||||
|
|
||||||
const snapshotApiUrl = '/api/snapshots';
|
const snapshotApiUrl = '/api/snapshots';
|
||||||
|
|
||||||
@@ -68,7 +70,6 @@ export class ShareSnapshot extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
reportInteraction('grafana_dashboards_snapshot_share_viewed');
|
|
||||||
this.getSnaphotShareOptions();
|
this.getSnaphotShareOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,9 +115,7 @@ export class ShareSnapshot extends PureComponent<Props, State> {
|
|||||||
step: 2,
|
step: 2,
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
reportInteraction('grafana_dashboards_snapshot_created', {
|
trackDashboardSharingActionPerType(external ? 'publish_snapshot' : 'local_snapshot', shareDashboardType.snapshot);
|
||||||
location: external ? 'raintank' : 'local',
|
|
||||||
});
|
|
||||||
this.setState({ isLoading: false });
|
this.setState({ isLoading: false });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { reportInteraction } from '@grafana/runtime';
|
||||||
|
|
||||||
|
export const shareAnalyticsEventNames: {
|
||||||
|
[key: string]: string;
|
||||||
|
} = {
|
||||||
|
sharingCategoryClicked: 'dashboards_sharing_category_clicked',
|
||||||
|
sharingActionClicked: 'dashboards_sharing_actions_clicked',
|
||||||
|
};
|
||||||
|
|
||||||
|
export function trackDashboardSharingTypeOpen(sharingType: string) {
|
||||||
|
reportInteraction(shareAnalyticsEventNames.sharingCategoryClicked, { item: sharingType });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function trackDashboardSharingActionPerType(action: string, sharingType: string) {
|
||||||
|
reportInteraction(shareAnalyticsEventNames.sharingActionClicked, {
|
||||||
|
item: action,
|
||||||
|
sharing_category: sharingType,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -137,3 +137,16 @@ export function getLocalTimeZone() {
|
|||||||
|
|
||||||
return '&tz=' + encodeURIComponent(options.timeZone);
|
return '&tz=' + encodeURIComponent(options.timeZone);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const shareDashboardType: {
|
||||||
|
[key: string]: string;
|
||||||
|
} = {
|
||||||
|
link: 'link',
|
||||||
|
snapshot: 'snapshot',
|
||||||
|
export: 'export',
|
||||||
|
embed: 'embed',
|
||||||
|
libraryPanel: 'library_panel',
|
||||||
|
pdf: 'pdf',
|
||||||
|
report: 'report',
|
||||||
|
publicDashboard: 'public_dashboard',
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user