Instrument transformations (#64960)

* Instrument transformations

* Fix any

* Fix any

* Fix unit test
This commit is contained in:
Ludovic Viaud
2023-03-20 15:30:07 +01:00
committed by GitHub
parent e1321f8721
commit 07be0643f0
4 changed files with 63 additions and 14 deletions

View File

@@ -2900,10 +2900,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "1"], [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "1"],
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "2"] [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "2"]
], ],
"public/app/features/dashboard/components/TransformationsEditor/TransformationOperationRow.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx:5381": [ "public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "1"], [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "1"],

View File

@@ -1,9 +1,9 @@
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import React, { useEffect } from 'react'; import React, { useEffect, useCallback } from 'react';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { config } from '@grafana/runtime'; import { config, reportInteraction } from '@grafana/runtime';
import { Tab, TabContent, TabsBar, toIconName, useForceUpdate, useStyles2 } from '@grafana/ui'; import { Tab, TabContent, TabsBar, toIconName, useForceUpdate, useStyles2 } from '@grafana/ui';
import AlertTabIndex from 'app/features/alerting/AlertTabIndex'; import AlertTabIndex from 'app/features/alerting/AlertTabIndex';
import { PanelAlertTab } from 'app/features/alerting/unified/PanelAlertTab'; import { PanelAlertTab } from 'app/features/alerting/unified/PanelAlertTab';
@@ -26,6 +26,17 @@ export const PanelEditorTabs = React.memo(({ panel, dashboard, tabs, onChangeTab
const forceUpdate = useForceUpdate(); const forceUpdate = useForceUpdate();
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
const instrumentedOnChangeTab = useCallback(
(tab) => {
if (!tab.active) {
reportInteraction('panel_editor_tabs_changed', { tab_id: tab.id });
}
onChangeTab(tab);
},
[onChangeTab]
);
useEffect(() => { useEffect(() => {
const eventSubs = new Subscription(); const eventSubs = new Subscription();
eventSubs.add(panel.events.subscribe(PanelQueriesChangedEvent, forceUpdate)); eventSubs.add(panel.events.subscribe(PanelQueriesChangedEvent, forceUpdate));
@@ -44,14 +55,14 @@ export const PanelEditorTabs = React.memo(({ panel, dashboard, tabs, onChangeTab
<TabsBar className={styles.tabBar} hideBorder> <TabsBar className={styles.tabBar} hideBorder>
{tabs.map((tab) => { {tabs.map((tab) => {
if (tab.id === PanelEditorTabId.Alert) { if (tab.id === PanelEditorTabId.Alert) {
return renderAlertTab(tab, panel, dashboard, onChangeTab); return renderAlertTab(tab, panel, dashboard, instrumentedOnChangeTab);
} }
return ( return (
<Tab <Tab
key={tab.id} key={tab.id}
label={tab.text} label={tab.text}
active={tab.active} active={tab.active}
onChangeTab={() => onChangeTab(tab)} onChangeTab={() => instrumentedOnChangeTab(tab)}
icon={toIconName(tab.icon)} icon={toIconName(tab.icon)}
counter={getCounter(panel, tab)} counter={getCounter(panel, tab)}
/> />

View File

@@ -2,6 +2,7 @@ import React, { useCallback } from 'react';
import { useToggle } from 'react-use'; import { useToggle } from 'react-use';
import { DataFrame, DataTransformerConfig, TransformerRegistryItem, FrameMatcherID } from '@grafana/data'; import { DataFrame, DataTransformerConfig, TransformerRegistryItem, FrameMatcherID } from '@grafana/data';
import { reportInteraction } from '@grafana/runtime';
import { HorizontalGroup } from '@grafana/ui'; import { HorizontalGroup } from '@grafana/ui';
import { OperationRowHelp } from 'app/core/components/QueryOperationRow/OperationRowHelp'; import { OperationRowHelp } from 'app/core/components/QueryOperationRow/OperationRowHelp';
import { QueryOperationAction } from 'app/core/components/QueryOperationRow/QueryOperationAction'; import { QueryOperationAction } from 'app/core/components/QueryOperationRow/QueryOperationAction';
@@ -19,7 +20,7 @@ interface TransformationOperationRowProps {
id: string; id: string;
index: number; index: number;
data: DataFrame[]; data: DataFrame[];
uiConfig: TransformerRegistryItem<any>; uiConfig: TransformerRegistryItem<null>;
configs: TransformationsEditorTransformation[]; configs: TransformationsEditorTransformation[];
onRemove: (index: number) => void; onRemove: (index: number) => void;
onChange: (index: number, config: DataTransformerConfig) => void; onChange: (index: number, config: DataTransformerConfig) => void;
@@ -65,6 +66,21 @@ export const TransformationOperationRow = ({
onChange(index, current); onChange(index, current);
}, [onChange, index, configs]); }, [onChange, index, configs]);
// Instrument toggle callback
const instrumentToggleCallback = useCallback(
(callback: (e: React.MouseEvent) => void, toggleId: string, active: boolean | undefined) =>
(e: React.MouseEvent) => {
reportInteraction('panel_editor_tabs_transformations_toggle', {
action: active ? 'off' : 'on',
toggleId,
transformationId: configs[index].transformation.id,
});
callback(e);
},
[configs, index]
);
const renderActions = ({ isOpen }: QueryOperationRowRenderProps) => { const renderActions = ({ isOpen }: QueryOperationRowRenderProps) => {
return ( return (
<HorizontalGroup align="center" width="auto"> <HorizontalGroup align="center" width="auto">
@@ -72,15 +88,28 @@ export const TransformationOperationRow = ({
<QueryOperationAction <QueryOperationAction
title="Show/hide transform help" title="Show/hide transform help"
icon="info-circle" icon="info-circle"
onClick={toggleHelp} onClick={instrumentToggleCallback(toggleHelp, 'help', showHelp)}
active={showHelp} active={showHelp}
/> />
{showFilter && <QueryOperationAction title="Filter" icon="filter" onClick={toggleFilter} active={filter} />} {showFilter && (
<QueryOperationAction title="Debug" disabled={!isOpen} icon="bug" onClick={toggleDebug} active={showDebug} /> <QueryOperationAction
title="Filter"
icon="filter"
onClick={instrumentToggleCallback(toggleFilter, 'filter', filter)}
active={filter}
/>
)}
<QueryOperationAction
title="Debug"
disabled={!isOpen}
icon="bug"
onClick={instrumentToggleCallback(toggleDebug, 'debug', showDebug)}
active={showDebug}
/>
<QueryOperationAction <QueryOperationAction
title="Disable/Enable transformation" title="Disable/Enable transformation"
icon={disabled ? 'eye-slash' : 'eye'} icon={disabled ? 'eye-slash' : 'eye'}
onClick={() => onDisableToggle(index)} onClick={instrumentToggleCallback(() => onDisableToggle(index), 'disabled', disabled)}
active={disabled} active={disabled}
/> />
<QueryOperationAction title="Remove" icon="trash-alt" onClick={() => onRemove(index)} /> <QueryOperationAction title="Remove" icon="trash-alt" onClick={() => onRemove(index)} />
@@ -113,7 +142,7 @@ export const TransformationOperationRow = ({
); );
}; };
function prepMarkdown(uiConfig: TransformerRegistryItem<any>) { function prepMarkdown(uiConfig: TransformerRegistryItem<null>) {
let helpMarkdown = uiConfig.help ?? uiConfig.description; let helpMarkdown = uiConfig.help ?? uiConfig.description;
return ` return `

View File

@@ -14,6 +14,7 @@ import {
TransformerRegistryItem, TransformerRegistryItem,
} from '@grafana/data'; } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
import { reportInteraction } from '@grafana/runtime';
import { import {
Alert, Alert,
Button, Button,
@@ -144,6 +145,10 @@ class UnThemedTransformationsEditor extends React.PureComponent<TransformationsE
}; };
onTransformationAdd = (selectable: SelectableValue<string>) => { onTransformationAdd = (selectable: SelectableValue<string>) => {
reportInteraction('panel_editor_tabs_transformations_management', {
action: 'add',
transformationId: selectable.value,
});
const { transformations } = this.state; const { transformations } = this.state;
const nextId = this.getTransformationNextId(selectable.value!); const nextId = this.getTransformationNextId(selectable.value!);
@@ -163,6 +168,10 @@ class UnThemedTransformationsEditor extends React.PureComponent<TransformationsE
onTransformationChange = (idx: number, config: DataTransformerConfig) => { onTransformationChange = (idx: number, config: DataTransformerConfig) => {
const { transformations } = this.state; const { transformations } = this.state;
const next = Array.from(transformations); const next = Array.from(transformations);
reportInteraction('panel_editor_tabs_transformations_management', {
action: 'change',
transformationId: next[idx].transformation.id,
});
next[idx].transformation = config; next[idx].transformation = config;
this.onChange(next); this.onChange(next);
}; };
@@ -170,6 +179,10 @@ class UnThemedTransformationsEditor extends React.PureComponent<TransformationsE
onTransformationRemove = (idx: number) => { onTransformationRemove = (idx: number) => {
const { transformations } = this.state; const { transformations } = this.state;
const next = Array.from(transformations); const next = Array.from(transformations);
reportInteraction('panel_editor_tabs_transformations_management', {
action: 'remove',
transformationId: next[idx].transformation.id,
});
next.splice(idx, 1); next.splice(idx, 1);
this.onChange(next); this.onChange(next);
}; };