logs: track the usage of certain features (#50325)

* logs: track the usage of certain features

* Add report interaction for logs interactions

* mock reportInteraction in test

* mock reportInteraction

Co-authored-by: Ivana Huckova <ivana.huckova@gmail.com>
pull/50525/head
Gábor Farkas 3 years ago committed by GitHub
parent a6943cb399
commit c412a3b052
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      public/app/features/dashboard/components/Inspector/InspectContent.tsx
  2. 3
      public/app/features/explore/ExploreQueryInspector.tsx
  3. 36
      public/app/features/explore/ExploreToolbar.tsx
  4. 6
      public/app/features/explore/Logs.tsx
  5. 1
      public/app/features/explore/LogsContainer.tsx
  6. 6
      public/app/features/explore/LogsNavigation.test.tsx
  7. 7
      public/app/features/explore/LogsNavigation.tsx
  8. 9
      public/app/features/explore/LogsNavigationPages.tsx
  9. 1
      public/app/features/explore/spec/queryHistory.test.tsx
  10. 7
      public/app/features/explore/state/query.ts
  11. 21
      public/app/features/inspector/InspectDataTab.tsx
  12. 8
      public/app/plugins/datasource/loki/components/LokiCheatSheet.tsx
  13. 6
      public/app/plugins/datasource/loki/components/LokiLabelBrowser.test.tsx
  14. 16
      public/app/plugins/datasource/loki/components/LokiLabelBrowser.tsx
  15. 13
      public/app/plugins/datasource/loki/components/LokiQueryField.tsx
  16. 10
      public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.tsx
  17. 1
      public/app/plugins/datasource/loki/querybuilder/components/LokiQueryCodeEditor.tsx
  18. 2
      public/app/plugins/datasource/loki/querybuilder/components/LokiQueryEditorSelector.tsx

@ -1,6 +1,6 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { DataSourceApi, formattedValueToString, getValueFormat, PanelData, PanelPlugin } from '@grafana/data'; import { CoreApp, DataSourceApi, formattedValueToString, getValueFormat, PanelData, PanelPlugin } from '@grafana/data';
import { getTemplateSrv } from '@grafana/runtime'; import { getTemplateSrv } from '@grafana/runtime';
import { Drawer, Tab, TabsBar } from '@grafana/ui'; import { Drawer, Tab, TabsBar } from '@grafana/ui';
import { InspectDataTab } from 'app/features/inspector/InspectDataTab'; import { InspectDataTab } from 'app/features/inspector/InspectDataTab';
@ -90,6 +90,7 @@ export const InspectContent: React.FC<Props> = ({
options={dataOptions} options={dataOptions}
onOptionsChange={onDataOptionsChange} onOptionsChange={onDataOptionsChange}
timeZone={dashboard.timezone} timeZone={dashboard.timezone}
app={CoreApp.Dashboard}
/> />
)} )}
{data && activeTab === InspectTab.Meta && ( {data && activeTab === InspectTab.Meta && (

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { connect, ConnectedProps } from 'react-redux'; import { connect, ConnectedProps } from 'react-redux';
import { TimeZone } from '@grafana/data'; import { CoreApp, TimeZone } from '@grafana/data';
import { TabbedContainer, TabConfig } from '@grafana/ui'; import { TabbedContainer, TabConfig } from '@grafana/ui';
import { ExploreDrawer } from 'app/features/explore/ExploreDrawer'; import { ExploreDrawer } from 'app/features/explore/ExploreDrawer';
import { InspectDataTab } from 'app/features/inspector/InspectDataTab'; import { InspectDataTab } from 'app/features/inspector/InspectDataTab';
@ -51,6 +51,7 @@ export function ExploreQueryInspector(props: Props) {
isLoading={loading} isLoading={loading}
options={{ withTransforms: false, withFieldConfig: false }} options={{ withTransforms: false, withFieldConfig: false }}
timeZone={timeZone} timeZone={timeZone}
app={CoreApp.Explore}
/> />
), ),
}; };

@ -2,7 +2,7 @@ import React, { lazy, PureComponent, RefObject, Suspense } from 'react';
import { connect, ConnectedProps } from 'react-redux'; import { connect, ConnectedProps } from 'react-redux';
import { DataSourceInstanceSettings, RawTimeRange } from '@grafana/data'; import { DataSourceInstanceSettings, RawTimeRange } from '@grafana/data';
import { config, DataSourcePicker } from '@grafana/runtime'; import { config, DataSourcePicker, reportInteraction } from '@grafana/runtime';
import { import {
defaultIntervals, defaultIntervals,
PageToolbar, PageToolbar,
@ -192,17 +192,28 @@ class UnConnectedExploreToolbar extends PureComponent<Props> {
{hasLiveOption && ( {hasLiveOption && (
<LiveTailControls exploreId={exploreId}> <LiveTailControls exploreId={exploreId}>
{(controls) => ( {(c) => {
<LiveTailButton const controls = {
splitted={splitted} ...c,
isLive={isLive} start: () => {
isPaused={isPaused} reportInteraction('grafana_explore_logs_result_displayed', {
start={controls.start} datasourceType: this.props.datasourceType,
pause={controls.pause} });
resume={controls.resume} c.start();
stop={controls.stop} },
/> };
)} return (
<LiveTailButton
splitted={splitted}
isLive={isLive}
isPaused={isPaused}
start={controls.start}
pause={controls.pause}
resume={controls.resume}
stop={controls.stop}
/>
);
}}
</LiveTailControls> </LiveTailControls>
)} )}
</ToolbarButtonRow> </ToolbarButtonRow>
@ -223,6 +234,7 @@ const mapStateToProps = (state: StoreState, { exploreId }: OwnProps) => {
return { return {
datasourceMissing, datasourceMissing,
datasourceName: datasourceInstance?.name, datasourceName: datasourceInstance?.name,
datasourceType: datasourceInstance?.type,
loading, loading,
range, range,
timeZone: getTimeZone(state.user), timeZone: getTimeZone(state.user),

@ -23,6 +23,7 @@ import {
SplitOpen, SplitOpen,
DataQueryResponse, DataQueryResponse,
} from '@grafana/data'; } from '@grafana/data';
import { reportInteraction } from '@grafana/runtime';
import { TooltipDisplayMode } from '@grafana/schema'; import { TooltipDisplayMode } from '@grafana/schema';
import { import {
RadioButtonGroup, RadioButtonGroup,
@ -68,6 +69,7 @@ interface Props extends Themeable2 {
scanning?: boolean; scanning?: boolean;
scanRange?: RawTimeRange; scanRange?: RawTimeRange;
exploreId: ExploreId; exploreId: ExploreId;
datasourceType?: string;
logsVolumeData: DataQueryResponse | undefined; logsVolumeData: DataQueryResponse | undefined;
loadLogsVolumeData: (exploreId: ExploreId) => void; loadLogsVolumeData: (exploreId: ExploreId) => void;
showContextToggle?: (row?: LogRowModel) => boolean; showContextToggle?: (row?: LogRowModel) => boolean;
@ -144,6 +146,10 @@ class UnthemedLogs extends PureComponent<Props, State> {
}; };
onChangeDedup = (dedupStrategy: LogsDedupStrategy) => { onChangeDedup = (dedupStrategy: LogsDedupStrategy) => {
reportInteraction('grafana_explore_logs_deduplication_clicked', {
deduplicationType: dedupStrategy,
datasourceType: this.props.datasourceType,
});
this.setState({ dedupStrategy }); this.setState({ dedupStrategy });
}; };

@ -131,6 +131,7 @@ class LogsContainer extends PureComponent<LogsContainerProps> {
<Collapse label="Logs" loading={loading} isOpen className={styleOverridesForStickyNavigation}> <Collapse label="Logs" loading={loading} isOpen className={styleOverridesForStickyNavigation}>
<Logs <Logs
exploreId={exploreId} exploreId={exploreId}
datasourceType={this.props.datasourceInstance?.type}
logRows={logRows} logRows={logRows}
logsMeta={logsMeta} logsMeta={logsMeta}
logsSeries={logsSeries} logsSeries={logsSeries}

@ -5,6 +5,12 @@ import { LogsSortOrder } from '@grafana/data';
import LogsNavigation from './LogsNavigation'; import LogsNavigation from './LogsNavigation';
// we have to mock out reportInteraction, otherwise it crashes the test.
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
reportInteraction: () => null,
}));
type LogsNavigationProps = ComponentProps<typeof LogsNavigation>; type LogsNavigationProps = ComponentProps<typeof LogsNavigation>;
const defaultProps: LogsNavigationProps = { const defaultProps: LogsNavigationProps = {
absoluteRange: { from: 1637319381811, to: 1637322981811 }, absoluteRange: { from: 1637319381811, to: 1637322981811 },

@ -3,6 +3,7 @@ import { isEqual } from 'lodash';
import React, { memo, useState, useEffect, useRef } from 'react'; import React, { memo, useState, useEffect, useRef } from 'react';
import { LogsSortOrder, AbsoluteTimeRange, TimeZone, DataQuery, GrafanaTheme2 } from '@grafana/data'; import { LogsSortOrder, AbsoluteTimeRange, TimeZone, DataQuery, GrafanaTheme2 } from '@grafana/data';
import { reportInteraction } from '@grafana/runtime';
import { Button, Icon, Spinner, useTheme2 } from '@grafana/ui'; import { Button, Icon, Spinner, useTheme2 } from '@grafana/ui';
import { LogsNavigationPages } from './LogsNavigationPages'; import { LogsNavigationPages } from './LogsNavigationPages';
@ -107,6 +108,9 @@ function LogsNavigation({
variant="secondary" variant="secondary"
onClick={() => { onClick={() => {
//If we are not on the last page, use next page's range //If we are not on the last page, use next page's range
reportInteraction('grafana_explore_logs_pagination_clicked', {
pageType: 'olderLogsButton',
});
if (!onLastPage) { if (!onLastPage) {
const indexChange = oldestLogsFirst ? -1 : 1; const indexChange = oldestLogsFirst ? -1 : 1;
changeTime({ changeTime({
@ -133,6 +137,9 @@ function LogsNavigation({
className={styles.navButton} className={styles.navButton}
variant="secondary" variant="secondary"
onClick={() => { onClick={() => {
reportInteraction('grafana_explore_logs_pagination_clicked', {
pageType: 'newerLogsButton',
});
//If we are not on the first page, use previous page's range //If we are not on the first page, use previous page's range
if (!onFirstPage) { if (!onFirstPage) {
const indexChange = oldestLogsFirst ? 1 : -1; const indexChange = oldestLogsFirst ? 1 : -1;

@ -2,6 +2,7 @@ import { css, cx } from '@emotion/css';
import React from 'react'; import React from 'react';
import { dateTimeFormat, systemDateFormats, TimeZone, AbsoluteTimeRange, GrafanaTheme2 } from '@grafana/data'; import { dateTimeFormat, systemDateFormats, TimeZone, AbsoluteTimeRange, GrafanaTheme2 } from '@grafana/data';
import { reportInteraction } from '@grafana/runtime';
import { CustomScrollbar, Spinner, useTheme2 } from '@grafana/ui'; import { CustomScrollbar, Spinner, useTheme2 } from '@grafana/ui';
import { LogsPage } from './LogsNavigation'; import { LogsPage } from './LogsNavigation';
@ -51,7 +52,13 @@ export function LogsNavigationPages({
data-testid={`page${index + 1}`} data-testid={`page${index + 1}`}
className={styles.page} className={styles.page}
key={page.queryRange.to} key={page.queryRange.to}
onClick={() => !loading && changeTime({ from: page.queryRange.from, to: page.queryRange.to })} onClick={() => {
reportInteraction('grafana_explore_logs_pagination_clicked', {
pageType: 'page',
pageNumber: index + 1,
});
!loading && changeTime({ from: page.queryRange.from, to: page.queryRange.to });
}}
> >
<div className={cx(styles.line, { selectedBg: currentPageIndex === index })} /> <div className={cx(styles.line, { selectedBg: currentPageIndex === index })} />
<div className={cx(styles.time, { selectedText: currentPageIndex === index })}> <div className={cx(styles.time, { selectedText: currentPageIndex === index })}>

@ -118,7 +118,6 @@ describe('Explore: Query History', () => {
await openQueryHistory(); await openQueryHistory();
await assertQueryHistoryExists(RAW_QUERY); await assertQueryHistoryExists(RAW_QUERY);
expect(reportInteractionMock).toBeCalledTimes(2);
expect(reportInteractionMock).toBeCalledWith('grafana_explore_query_history_opened', { expect(reportInteractionMock).toBeCalledWith('grafana_explore_query_history_opened', {
queryHistoryEnabled: false, queryHistoryEnabled: false,
}); });

@ -19,7 +19,7 @@ import {
QueryFixAction, QueryFixAction,
toLegacyResponseData, toLegacyResponseData,
} from '@grafana/data'; } from '@grafana/data';
import { config } from '@grafana/runtime'; import { config, reportInteraction } from '@grafana/runtime';
import { import {
buildQueryTransaction, buildQueryTransaction,
ensureQueries, ensureQueries,
@ -442,6 +442,11 @@ export const runQueries = (
) )
.subscribe({ .subscribe({
next(data) { next(data) {
if (data.logsResult !== null) {
reportInteraction('grafana_explore_logs_result_displayed', {
datasourceType: datasourceInstance.type,
});
}
dispatch(queryStreamUpdatedAction({ exploreId, response: data })); dispatch(queryStreamUpdatedAction({ exploreId, response: data }));
// Keep scanning for results if this was the last scanning transaction // Keep scanning for results if this was the last scanning transaction

@ -16,8 +16,10 @@ import {
toCSV, toCSV,
transformDataFrame, transformDataFrame,
TimeZone, TimeZone,
CoreApp,
} from '@grafana/data'; } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
import { reportInteraction } from '@grafana/runtime';
import { Button, Spinner, Table } from '@grafana/ui'; import { Button, Spinner, Table } from '@grafana/ui';
import { config } from 'app/core/config'; import { config } from 'app/core/config';
import { dataFrameToLogsModel } from 'app/core/logs_model'; import { dataFrameToLogsModel } from 'app/core/logs_model';
@ -34,6 +36,7 @@ interface Props {
isLoading: boolean; isLoading: boolean;
options: GetDataOptions; options: GetDataOptions;
timeZone: TimeZone; timeZone: TimeZone;
app?: CoreApp;
data?: DataFrame[]; data?: DataFrame[];
panel?: PanelModel; panel?: PanelModel;
onOptionsChange?: (options: GetDataOptions) => void; onOptionsChange?: (options: GetDataOptions) => void;
@ -107,7 +110,11 @@ export class InspectDataTab extends PureComponent<Props, State> {
}; };
exportLogsAsTxt = () => { exportLogsAsTxt = () => {
const { data, panel } = this.props; const { data, panel, app } = this.props;
reportInteraction('grafana_logs_download_logs_clicked', {
app,
format: 'logs',
});
const logsModel = dataFrameToLogsModel(data || [], undefined); const logsModel = dataFrameToLogsModel(data || [], undefined);
let textToDownload = ''; let textToDownload = '';
@ -223,7 +230,7 @@ export class InspectDataTab extends PureComponent<Props, State> {
} }
render() { render() {
const { isLoading, options, data, panel, onOptionsChange } = this.props; const { isLoading, options, data, panel, onOptionsChange, app } = this.props;
const { dataFrameIndex, transformId, transformationOptions, selectedDataFrame, downloadForExcel } = this.state; const { dataFrameIndex, transformId, transformationOptions, selectedDataFrame, downloadForExcel } = this.state;
const styles = getPanelInspectorStyles(); const styles = getPanelInspectorStyles();
@ -266,7 +273,15 @@ export class InspectDataTab extends PureComponent<Props, State> {
/> />
<Button <Button
variant="primary" variant="primary"
onClick={() => this.exportCsv(dataFrames[dataFrameIndex], { useExcelHeader: this.state.downloadForExcel })} onClick={() => {
if (hasLogs) {
reportInteraction('grafana_logs_download_clicked', {
app,
format: 'csv',
});
}
this.exportCsv(dataFrames[dataFrameIndex], { useExcelHeader: this.state.downloadForExcel });
}}
className={css` className={css`
margin-bottom: 10px; margin-bottom: 10px;
`} `}

@ -2,6 +2,7 @@ import { shuffle } from 'lodash';
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { QueryEditorHelpProps } from '@grafana/data'; import { QueryEditorHelpProps } from '@grafana/data';
import { reportInteraction } from '@grafana/runtime';
import LokiLanguageProvider from '../language_provider'; import LokiLanguageProvider from '../language_provider';
import { LokiQuery } from '../types'; import { LokiQuery } from '../types';
@ -43,6 +44,7 @@ export default class LokiCheatSheet extends PureComponent<QueryEditorHelpProps<L
componentDidMount() { componentDidMount() {
this.scheduleUserLabelChecking(); this.scheduleUserLabelChecking();
reportInteraction('grafana_loki_cheatsheet_opened', {});
} }
componentWillUnmount() { componentWillUnmount() {
@ -73,9 +75,13 @@ export default class LokiCheatSheet extends PureComponent<QueryEditorHelpProps<L
renderExpression(expr: string) { renderExpression(expr: string) {
const { onClickExample } = this.props; const { onClickExample } = this.props;
const onClick = (query: LokiQuery) => {
onClickExample(query);
reportInteraction('grafana_loki_cheatsheet_example_clicked', {});
};
return ( return (
<div className="cheat-sheet-item__example" key={expr} onClick={(e) => onClickExample({ refId: 'A', expr })}> <div className="cheat-sheet-item__example" key={expr} onClick={(e) => onClick({ refId: 'A', expr })}>
<code>{expr}</code> <code>{expr}</code>
</div> </div>
); );

@ -14,6 +14,12 @@ import {
BrowserProps, BrowserProps,
} from './LokiLabelBrowser'; } from './LokiLabelBrowser';
// we have to mock out reportInteraction, otherwise it crashes the test.
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
reportInteraction: () => null,
}));
describe('buildSelector()', () => { describe('buildSelector()', () => {
it('returns an empty selector for no labels', () => { it('returns an empty selector for no labels', () => {
expect(buildSelector([])).toEqual('{}'); expect(buildSelector([])).toEqual('{}');

@ -3,7 +3,8 @@ import { sortBy } from 'lodash';
import React, { ChangeEvent } from 'react'; import React, { ChangeEvent } from 'react';
import { FixedSizeList } from 'react-window'; import { FixedSizeList } from 'react-window';
import { GrafanaTheme2 } from '@grafana/data'; import { CoreApp, GrafanaTheme2 } from '@grafana/data';
import { reportInteraction } from '@grafana/runtime';
import { import {
Button, Button,
HighlightPart, HighlightPart,
@ -31,6 +32,7 @@ export interface BrowserProps {
languageProvider: LokiLanguageProvider | PromQlLanguageProvider; languageProvider: LokiLanguageProvider | PromQlLanguageProvider;
onChange: (selector: string) => void; onChange: (selector: string) => void;
theme: GrafanaTheme2; theme: GrafanaTheme2;
app?: CoreApp;
autoSelect?: number; autoSelect?: number;
hide?: () => void; hide?: () => void;
lastUsedLabels: string[]; lastUsedLabels: string[];
@ -189,17 +191,29 @@ export class UnthemedLokiLabelBrowser extends React.Component<BrowserProps, Brow
}; };
onClickRunLogsQuery = () => { onClickRunLogsQuery = () => {
reportInteraction('grafana_loki_log_browser_closed', {
app: this.props.app,
closeType: 'showLogsButton',
});
const selector = buildSelector(this.state.labels); const selector = buildSelector(this.state.labels);
this.props.onChange(selector); this.props.onChange(selector);
}; };
onClickRunMetricsQuery = () => { onClickRunMetricsQuery = () => {
reportInteraction('grafana_loki_log_browser_closed', {
app: this.props.app,
closeType: 'showLogsRateButton',
});
const selector = buildSelector(this.state.labels); const selector = buildSelector(this.state.labels);
const query = `rate(${selector}[$__interval])`; const query = `rate(${selector}[$__interval])`;
this.props.onChange(query); this.props.onChange(query);
}; };
onClickClear = () => { onClickClear = () => {
reportInteraction('grafana_loki_log_browser_closed', {
app: this.props.app,
closeType: 'clearButton',
});
this.setState((state) => { this.setState((state) => {
const labels: SelectableLabel[] = state.labels.map((label) => ({ const labels: SelectableLabel[] = state.labels.map((label) => ({
...label, ...label,

@ -3,6 +3,7 @@ import React, { ReactNode } from 'react';
import { Plugin, Node } from 'slate'; import { Plugin, Node } from 'slate';
import { QueryEditorProps } from '@grafana/data'; import { QueryEditorProps } from '@grafana/data';
import { reportInteraction } from '@grafana/runtime';
import { import {
SlatePrism, SlatePrism,
TypeaheadOutput, TypeaheadOutput,
@ -145,6 +146,16 @@ export class LokiQueryField extends React.PureComponent<LokiQueryFieldProps, Lok
}; };
onClickChooserButton = () => { onClickChooserButton = () => {
if (!this.state.labelBrowserVisible) {
reportInteraction('grafana_loki_log_browser_opened', {
app: this.props.app,
});
} else {
reportInteraction('grafana_loki_log_browser_closed', {
app: this.props.app,
closeType: 'logBrowserButton',
});
}
this.setState((state) => ({ labelBrowserVisible: !state.labelBrowserVisible })); this.setState((state) => ({ labelBrowserVisible: !state.labelBrowserVisible }));
}; };
@ -170,6 +181,7 @@ export class LokiQueryField extends React.PureComponent<LokiQueryFieldProps, Lok
const { const {
ExtraFieldElement, ExtraFieldElement,
query, query,
app,
datasource, datasource,
placeholder = 'Enter a Loki query (run with Shift+Enter)', placeholder = 'Enter a Loki query (run with Shift+Enter)',
} = this.props; } = this.props;
@ -221,6 +233,7 @@ export class LokiQueryField extends React.PureComponent<LokiQueryFieldProps, Lok
lastUsedLabels={lastUsedLabels || []} lastUsedLabels={lastUsedLabels || []}
storeLastUsedLabels={onLastUsedLabelsSave} storeLastUsedLabels={onLastUsedLabelsSave}
deleteLastUsedLabels={onLastUsedLabelsDelete} deleteLastUsedLabels={onLastUsedLabelsDelete}
app={app}
/> />
</div> </div>
)} )}

@ -1,7 +1,8 @@
import React from 'react'; import React from 'react';
import { SelectableValue } from '@grafana/data'; import { CoreApp, SelectableValue } from '@grafana/data';
import { EditorRow, EditorField } from '@grafana/experimental'; import { EditorRow, EditorField } from '@grafana/experimental';
import { reportInteraction } from '@grafana/runtime';
import { RadioButtonGroup, Select, AutoSizeInput } from '@grafana/ui'; import { RadioButtonGroup, Select, AutoSizeInput } from '@grafana/ui';
import { QueryOptionGroup } from 'app/plugins/datasource/prometheus/querybuilder/shared/QueryOptionGroup'; import { QueryOptionGroup } from 'app/plugins/datasource/prometheus/querybuilder/shared/QueryOptionGroup';
@ -13,15 +14,20 @@ export interface Props {
query: LokiQuery; query: LokiQuery;
onChange: (update: LokiQuery) => void; onChange: (update: LokiQuery) => void;
onRunQuery: () => void; onRunQuery: () => void;
app?: CoreApp;
} }
export const LokiQueryBuilderOptions = React.memo<Props>(({ query, onChange, onRunQuery }) => { export const LokiQueryBuilderOptions = React.memo<Props>(({ app, query, onChange, onRunQuery }) => {
const onQueryTypeChange = (value: LokiQueryType) => { const onQueryTypeChange = (value: LokiQueryType) => {
onChange({ ...query, queryType: value }); onChange({ ...query, queryType: value });
onRunQuery(); onRunQuery();
}; };
const onResolutionChange = (option: SelectableValue<number>) => { const onResolutionChange = (option: SelectableValue<number>) => {
reportInteraction('grafana_loki_resolution_clicked', {
app,
resolution: option.value,
});
onChange({ ...query, resolution: option.value }); onChange({ ...query, resolution: option.value });
onRunQuery(); onRunQuery();
}; };

@ -40,6 +40,7 @@ export function LokiQueryCodeEditor({
history={[]} history={[]}
data={data} data={data}
data-testid={testIds.editor} data-testid={testIds.editor}
app={app}
/> />
</div> </div>
); );

@ -126,7 +126,7 @@ export const LokiQueryEditorSelector = React.memo<LokiQueryEditorProps>((props)
)} )}
{editorMode === QueryEditorMode.Explain && <LokiQueryBuilderExplained query={query.expr} />} {editorMode === QueryEditorMode.Explain && <LokiQueryBuilderExplained query={query.expr} />}
{editorMode !== QueryEditorMode.Explain && ( {editorMode !== QueryEditorMode.Explain && (
<LokiQueryBuilderOptions query={query} onChange={onChange} onRunQuery={onRunQuery} /> <LokiQueryBuilderOptions query={query} onChange={onChange} onRunQuery={onRunQuery} app={app} />
)} )}
</EditorRows> </EditorRows>
</> </>

Loading…
Cancel
Save