diff --git a/public/app/features/explore/ExploreQueryInspector.test.tsx b/public/app/features/explore/ExploreQueryInspector.test.tsx index 49af7ff1a678..2265599e8183 100644 --- a/public/app/features/explore/ExploreQueryInspector.test.tsx +++ b/public/app/features/explore/ExploreQueryInspector.test.tsx @@ -29,6 +29,11 @@ jest.mock('app/core/services/context_srv', () => ({ }, })); +jest.mock('@grafana/runtime', () => ({ + ...jest.requireActual('@grafana/runtime'), + reportInteraction: () => null, +})); + const setup = (propOverrides = {}) => { const props: ExploreQueryInspectorProps = { loading: false, diff --git a/public/app/features/explore/ExploreQueryInspector.tsx b/public/app/features/explore/ExploreQueryInspector.tsx index bc39f5ea0ebf..681f7e7a747f 100644 --- a/public/app/features/explore/ExploreQueryInspector.tsx +++ b/public/app/features/explore/ExploreQueryInspector.tsx @@ -1,7 +1,8 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { connect, ConnectedProps } from 'react-redux'; import { CoreApp, TimeZone } from '@grafana/data'; +import { reportInteraction } from '@grafana/runtime/src'; import { TabbedContainer, TabConfig } from '@grafana/ui'; import { ExploreDrawer } from 'app/features/explore/ExploreDrawer'; import { InspectDataTab } from 'app/features/inspector/InspectDataTab'; @@ -27,6 +28,10 @@ export function ExploreQueryInspector(props: Props) { const dataFrames = queryResponse?.series || []; const error = queryResponse?.error; + useEffect(() => { + reportInteraction('grafana_explore_query_inspector_opened'); + }, []); + const statsTab: TabConfig = { label: 'Stats', value: 'stats', diff --git a/public/app/features/explore/ExploreTimeControls.tsx b/public/app/features/explore/ExploreTimeControls.tsx index aa32dbde1567..1f38d470659a 100644 --- a/public/app/features/explore/ExploreTimeControls.tsx +++ b/public/app/features/explore/ExploreTimeControls.tsx @@ -1,6 +1,7 @@ import React, { Component } from 'react'; import { TimeRange, TimeZone, RawTimeRange, dateTimeForTimeZone, dateMath } from '@grafana/data'; +import { reportInteraction } from '@grafana/runtime'; import { TimePickerWithHistory } from 'app/core/components/TimePicker/TimePickerWithHistory'; import { getShiftedTimeRange, getZoomedTimeRange } from 'app/core/utils/timePicker'; import { ExploreId } from 'app/types'; @@ -44,6 +45,11 @@ export class ExploreTimeControls extends Component { from: adjustedFrom, to: adjustedTo, }); + + reportInteraction('grafana_explore_time_picker_time_range_changed', { + timeRangeFrom: adjustedFrom, + timeRangeTo: adjustedTo, + }); }; onZoom = () => { diff --git a/public/app/features/explore/ExploreToolbar.tsx b/public/app/features/explore/ExploreToolbar.tsx index 2098ccde72c2..dfaced53e30e 100644 --- a/public/app/features/explore/ExploreToolbar.tsx +++ b/public/app/features/explore/ExploreToolbar.tsx @@ -38,7 +38,8 @@ type Props = OwnProps & ConnectedProps; class UnConnectedExploreToolbar extends PureComponent { onChangeDatasource = async (dsSettings: DataSourceInstanceSettings) => { - this.props.changeDatasource(this.props.exploreId, dsSettings.uid, { importQueries: true }); + const { changeDatasource, exploreId } = this.props; + changeDatasource(exploreId, dsSettings.uid, { importQueries: true }); }; onRunQuery = (loading = false) => { @@ -60,6 +61,23 @@ class UnConnectedExploreToolbar extends PureComponent { syncTimes(exploreId); }; + onCopyShortLink = async () => { + await createAndCopyShortLink(window.location.href); + reportInteraction('grafana_explore_shortened_link_clicked'); + }; + + onOpenSplitView = () => { + const { split } = this.props; + split(); + reportInteraction('grafana_explore_splitView_opened'); + }; + + onCloseSplitView = () => { + const { closeSplit, exploreId } = this.props; + closeSplit(exploreId); + reportInteraction('grafana_explore_splitView_closed'); + }; + renderRefreshPicker = (showSmallTimePicker: boolean) => { const { loading, refreshInterval, isLive } = this.props; @@ -92,7 +110,6 @@ class UnConnectedExploreToolbar extends PureComponent { render() { const { datasourceMissing, - closeSplit, exploreId, loading, range, @@ -102,7 +119,6 @@ class UnConnectedExploreToolbar extends PureComponent { syncedTimes, refreshInterval, onChangeTime, - split, hasLiveOption, isLive, isPaused, @@ -131,7 +147,7 @@ class UnConnectedExploreToolbar extends PureComponent { key="share" tooltip="Copy shortened link" icon="share-alt" - onClick={() => createAndCopyShortLink(window.location.href)} + onClick={this.onCopyShortLink} aria-label="Copy shortened link" /> ), @@ -149,11 +165,11 @@ class UnConnectedExploreToolbar extends PureComponent { > <> {!splitted ? ( - split()} icon="columns" disabled={isLive}> + Split ) : ( - closeSplit(exploreId)} icon="times"> + Close )} diff --git a/public/app/features/explore/QueryRows.test.tsx b/public/app/features/explore/QueryRows.test.tsx index d4ddb45221f2..48b4d4ca6502 100644 --- a/public/app/features/explore/QueryRows.test.tsx +++ b/public/app/features/explore/QueryRows.test.tsx @@ -12,6 +12,11 @@ import { UserState } from '../profile/state/reducers'; import { QueryRows } from './QueryRows'; import { makeExplorePaneState } from './state/utils'; +jest.mock('@grafana/runtime', () => ({ + ...jest.requireActual('@grafana/runtime'), + reportInteraction: () => null, +})); + function setup(queries: DataQuery[]) { const defaultDs = { name: 'newDs', diff --git a/public/app/features/explore/QueryRows.tsx b/public/app/features/explore/QueryRows.tsx index 772202c93492..489922184968 100644 --- a/public/app/features/explore/QueryRows.tsx +++ b/public/app/features/explore/QueryRows.tsx @@ -3,7 +3,7 @@ import React, { useCallback, useMemo } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { CoreApp, DataQuery, DataSourceInstanceSettings } from '@grafana/data'; -import { getDataSourceSrv } from '@grafana/runtime'; +import { getDataSourceSrv, reportInteraction } from '@grafana/runtime'; import { getNextRefIdChar } from 'app/core/utils/query'; import { ExploreId } from 'app/types/explore'; @@ -73,6 +73,18 @@ export const QueryRows = ({ exploreId }: Props) => { dispatch(importQueries(exploreId, queries, queryDatasource, targetDS, query.refId)); }; + const onQueryCopied = () => { + reportInteraction('grafana_explore_query_row_copy'); + }; + + const onQueryRemoved = () => { + reportInteraction('grafana_explore_query_row_remove'); + }; + + const onQueryToggled = (queryStatus?: boolean) => { + reportInteraction('grafana_query_row_toggle', queryStatus === undefined ? {} : { queryEnabled: queryStatus }); + }; + return ( { onQueriesChange={onChange} onAddQuery={onAddQuery} onRunQueries={onRunQueries} + onQueryCopied={onQueryCopied} + onQueryRemoved={onQueryRemoved} + onQueryToggled={onQueryToggled} data={queryResponse} app={CoreApp.Explore} history={history} diff --git a/public/app/features/query/components/QueryEditorRow.tsx b/public/app/features/query/components/QueryEditorRow.tsx index 73c8f5b56a22..8034777cd52f 100644 --- a/public/app/features/query/components/QueryEditorRow.tsx +++ b/public/app/features/query/components/QueryEditorRow.tsx @@ -57,6 +57,9 @@ interface Props { history?: Array>; eventBus?: EventBusExtended; alerting?: boolean; + onQueryCopied?: () => void; + onQueryRemoved?: () => void; + onQueryToggled?: (queryStatus?: boolean | undefined) => void; } interface State { @@ -274,18 +277,32 @@ export class QueryEditorRow extends PureComponent { - this.props.onRemoveQuery(this.props.query); + const { onRemoveQuery, query, onQueryRemoved } = this.props; + onRemoveQuery(query); + + if (onQueryRemoved) { + onQueryRemoved(); + } }; onCopyQuery = () => { - const copy = cloneDeep(this.props.query); - this.props.onAddQuery(copy); + const { query, onAddQuery, onQueryCopied } = this.props; + const copy = cloneDeep(query); + onAddQuery(copy); + + if (onQueryCopied) { + onQueryCopied(); + } }; onDisableQuery = () => { - const { query } = this.props; - this.props.onChange({ ...query, hide: !query.hide }); - this.props.onRunQuery(); + const { query, onChange, onRunQuery, onQueryToggled } = this.props; + onChange({ ...query, hide: !query.hide }); + onRunQuery(); + + if (onQueryToggled) { + onQueryToggled(query.hide); + } }; onToggleHelp = () => { diff --git a/public/app/features/query/components/QueryEditorRows.tsx b/public/app/features/query/components/QueryEditorRows.tsx index b99ed48a0409..05eec743cb89 100644 --- a/public/app/features/query/components/QueryEditorRows.tsx +++ b/public/app/features/query/components/QueryEditorRows.tsx @@ -31,7 +31,9 @@ interface Props { app?: CoreApp; history?: Array>; eventBus?: EventBusExtended; - + onQueryCopied?: () => void; + onQueryRemoved?: () => void; + onQueryToggled?: (queryStatus?: boolean | undefined) => void; onDatasourceChange?: (dataSource: DataSourceInstanceSettings, query: DataQuery) => void; } @@ -135,7 +137,19 @@ export class QueryEditorRows extends PureComponent { }; render() { - const { dsSettings, data, queries, app, history, eventBus } = this.props; + const { + dsSettings, + data, + queries, + app, + history, + eventBus, + onAddQuery, + onRunQueries, + onQueryCopied, + onQueryRemoved, + onQueryToggled, + } = this.props; return ( @@ -160,8 +174,11 @@ export class QueryEditorRows extends PureComponent { onChangeDataSource={onChangeDataSourceSettings} onChange={(query) => this.onChangeQuery(query, index)} onRemoveQuery={this.onRemoveQuery} - onAddQuery={this.props.onAddQuery} - onRunQuery={this.props.onRunQueries} + onAddQuery={onAddQuery} + onRunQuery={onRunQueries} + onQueryCopied={onQueryCopied} + onQueryRemoved={onQueryRemoved} + onQueryToggled={onQueryToggled} queries={queries} app={app} history={history}