mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: Remove not running query for collapsed elements (#27026)
* Make graph and table collapsing just a UI thing * Remove showingGraph and showingTable, set them defaultly to true * Remove collaapsing for panels in Explore * UI toggle WiP * WIP, add query type * Refactor, clean up * Update tests * Clean uo * Update rangeAll to range and instant * Remove console logs * Update packages/grafana-data/src/types/datasource.ts Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com> * Update public/app/core/utils/explore.ts Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com> * Fix prettier error Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>
This commit is contained in:
@@ -453,6 +453,9 @@ export interface DataQueryRequest<TQuery extends DataQuery = DataQuery> {
|
||||
|
||||
// Explore state used by various datasources
|
||||
liveStreaming?: boolean;
|
||||
/**
|
||||
* @deprecated showingGraph and showingTable are always set to true and set to true
|
||||
*/
|
||||
showingGraph?: boolean;
|
||||
showingTable?: boolean;
|
||||
}
|
||||
|
||||
@@ -160,8 +160,11 @@ export function buildQueryTransaction(
|
||||
maxDataPoints: queryOptions.maxDataPoints,
|
||||
exploreMode: queryOptions.mode,
|
||||
liveStreaming: queryOptions.liveStreaming,
|
||||
showingGraph: queryOptions.showingGraph,
|
||||
showingTable: queryOptions.showingTable,
|
||||
/**
|
||||
* @deprecated (external API) showingGraph and showingTable are always set to true and set to true
|
||||
*/
|
||||
showingGraph: true,
|
||||
showingTable: true,
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -5,7 +5,6 @@ import { ExploreId } from 'app/types/explore';
|
||||
import { shallow } from 'enzyme';
|
||||
import { Explore, ExploreProps } from './Explore';
|
||||
import { scanStopAction } from './state/actionTypes';
|
||||
import { toggleGraph } from './state/actions';
|
||||
import { SecondaryActions } from './SecondaryActions';
|
||||
import { getTheme } from '@grafana/ui';
|
||||
|
||||
@@ -67,11 +66,8 @@ const dummyProps: ExploreProps = {
|
||||
from: 0,
|
||||
to: 0,
|
||||
},
|
||||
showingGraph: false,
|
||||
showingTable: false,
|
||||
timeZone: 'UTC',
|
||||
onHiddenSeriesChanged: jest.fn(),
|
||||
toggleGraph: toggleGraph,
|
||||
queryResponse: {
|
||||
state: LoadingState.NotStarted,
|
||||
series: [],
|
||||
|
||||
@@ -37,7 +37,6 @@ import {
|
||||
refreshExplore,
|
||||
scanStart,
|
||||
setQueries,
|
||||
toggleGraph,
|
||||
updateTimeRange,
|
||||
} from './state/actions';
|
||||
|
||||
@@ -114,11 +113,8 @@ export interface ExploreProps {
|
||||
logsResult?: LogsModel;
|
||||
loading?: boolean;
|
||||
absoluteRange: AbsoluteTimeRange;
|
||||
showingGraph?: boolean;
|
||||
showingTable?: boolean;
|
||||
timeZone?: TimeZone;
|
||||
onHiddenSeriesChanged?: (hiddenSeries: string[]) => void;
|
||||
toggleGraph: typeof toggleGraph;
|
||||
queryResponse: PanelData;
|
||||
originPanelId: number;
|
||||
addQueryRow: typeof addQueryRow;
|
||||
@@ -269,11 +265,6 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
||||
this.props.scanStopAction({ exploreId: this.props.exploreId });
|
||||
};
|
||||
|
||||
onToggleGraph = (showingGraph: boolean) => {
|
||||
const { toggleGraph, exploreId } = this.props;
|
||||
toggleGraph(exploreId, showingGraph);
|
||||
};
|
||||
|
||||
onUpdateTimeRange = (absoluteRange: AbsoluteTimeRange) => {
|
||||
const { exploreId, updateTimeRange } = this.props;
|
||||
updateTimeRange({ exploreId, absoluteRange });
|
||||
@@ -321,8 +312,6 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
||||
graphResult,
|
||||
loading,
|
||||
absoluteRange,
|
||||
showingGraph,
|
||||
showingTable,
|
||||
timeZone,
|
||||
queryResponse,
|
||||
syncedTimes,
|
||||
@@ -396,10 +385,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
||||
absoluteRange={absoluteRange}
|
||||
isStacked={false}
|
||||
showPanel={true}
|
||||
showingGraph={showingGraph}
|
||||
showingTable={showingTable}
|
||||
timeZone={timeZone}
|
||||
onToggleGraph={this.onToggleGraph}
|
||||
onUpdateTimeRange={this.onUpdateTimeRange}
|
||||
showBars={false}
|
||||
showLines={true}
|
||||
@@ -484,8 +470,6 @@ function mapStateToProps(state: StoreState, { exploreId }: ExploreProps): Partia
|
||||
showTable,
|
||||
showTrace,
|
||||
loading,
|
||||
showingGraph,
|
||||
showingTable,
|
||||
absoluteRange,
|
||||
queryResponse,
|
||||
} = item;
|
||||
@@ -514,8 +498,6 @@ function mapStateToProps(state: StoreState, { exploreId }: ExploreProps): Partia
|
||||
graphResult: graphResult ?? undefined,
|
||||
logsResult: logsResult ?? undefined,
|
||||
loading,
|
||||
showingGraph,
|
||||
showingTable,
|
||||
absoluteRange,
|
||||
queryResponse,
|
||||
originPanelId,
|
||||
@@ -537,7 +519,6 @@ const mapDispatchToProps: Partial<ExploreProps> = {
|
||||
scanStopAction,
|
||||
setQueries,
|
||||
updateTimeRange,
|
||||
toggleGraph,
|
||||
addQueryRow,
|
||||
};
|
||||
|
||||
|
||||
@@ -49,11 +49,8 @@ interface Props extends Themeable {
|
||||
showBars: boolean;
|
||||
showLines: boolean;
|
||||
isStacked: boolean;
|
||||
showingGraph?: boolean;
|
||||
showingTable?: boolean;
|
||||
timeZone?: TimeZone;
|
||||
onUpdateTimeRange: (absoluteRange: AbsoluteTimeRange) => void;
|
||||
onToggleGraph?: (showingGraph: boolean) => void;
|
||||
onHiddenSeriesChanged?: (hiddenSeries: string[]) => void;
|
||||
}
|
||||
|
||||
@@ -74,13 +71,6 @@ class UnThemedExploreGraphPanel extends PureComponent<Props, State> {
|
||||
});
|
||||
};
|
||||
|
||||
onClickGraphButton = () => {
|
||||
const { onToggleGraph, showingGraph } = this.props;
|
||||
if (onToggleGraph) {
|
||||
onToggleGraph(showingGraph ?? false);
|
||||
}
|
||||
};
|
||||
|
||||
onChangeTime = (from: number, to: number) => {
|
||||
const { onUpdateTimeRange } = this.props;
|
||||
onUpdateTimeRange({ from, to });
|
||||
@@ -95,8 +85,6 @@ class UnThemedExploreGraphPanel extends PureComponent<Props, State> {
|
||||
timeZone,
|
||||
absoluteRange,
|
||||
showPanel,
|
||||
showingGraph,
|
||||
showingTable,
|
||||
showBars,
|
||||
showLines,
|
||||
isStacked,
|
||||
@@ -116,10 +104,9 @@ class UnThemedExploreGraphPanel extends PureComponent<Props, State> {
|
||||
},
|
||||
};
|
||||
|
||||
const height = showPanel === false ? 100 : showingGraph && showingTable ? 200 : 400;
|
||||
const height = showPanel ? 200 : 100;
|
||||
const lineWidth = showLines ? 1 : 5;
|
||||
const seriesToShow = showAllTimeSeries ? series : series.slice(0, MAX_NUMBER_OF_TIME_SERIES);
|
||||
|
||||
return (
|
||||
<GraphSeriesToggler series={seriesToShow} onHiddenSeriesChanged={onHiddenSeriesChanged}>
|
||||
{({ onSeriesToggle, toggledSeries }: GraphSeriesTogglerAPI) => {
|
||||
@@ -153,7 +140,7 @@ class UnThemedExploreGraphPanel extends PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { series, showPanel, showingGraph, loading, theme } = this.props;
|
||||
const { series, showPanel, loading, theme } = this.props;
|
||||
const { showAllTimeSeries } = this.state;
|
||||
const style = getStyles(theme);
|
||||
|
||||
@@ -171,13 +158,7 @@ class UnThemedExploreGraphPanel extends PureComponent<Props, State> {
|
||||
)}
|
||||
|
||||
{showPanel && (
|
||||
<Collapse
|
||||
label="Graph"
|
||||
collapsible
|
||||
isOpen={showingGraph}
|
||||
loading={loading}
|
||||
onToggle={this.onClickGraphButton}
|
||||
>
|
||||
<Collapse label="Graph" loading={loading} isOpen>
|
||||
{this.renderGraph()}
|
||||
</Collapse>
|
||||
)}
|
||||
|
||||
@@ -266,8 +266,6 @@ export class Logs extends PureComponent<Props, State> {
|
||||
absoluteRange={visibleRange || absoluteRange}
|
||||
isStacked={true}
|
||||
showPanel={false}
|
||||
showingGraph={true}
|
||||
showingTable={true}
|
||||
timeZone={timeZone}
|
||||
showBars={true}
|
||||
showLines={false}
|
||||
|
||||
@@ -62,15 +62,7 @@ interface LogsContainerProps {
|
||||
splitOpen: typeof splitOpen;
|
||||
}
|
||||
|
||||
interface LogsContainerState {
|
||||
logsContainerOpen: boolean;
|
||||
}
|
||||
|
||||
export class LogsContainer extends PureComponent<LogsContainerProps, LogsContainerState> {
|
||||
state: LogsContainerState = {
|
||||
logsContainerOpen: true,
|
||||
};
|
||||
|
||||
export class LogsContainer extends PureComponent<LogsContainerProps> {
|
||||
onChangeTime = (absoluteRange: AbsoluteTimeRange) => {
|
||||
const { exploreId, updateTimeRange } = this.props;
|
||||
updateTimeRange({ exploreId, absoluteRange });
|
||||
@@ -102,12 +94,6 @@ export class LogsContainer extends PureComponent<LogsContainerProps, LogsContain
|
||||
return getFieldLinksForExplore(field, rowIndex, this.props.splitOpen, this.props.range);
|
||||
};
|
||||
|
||||
onToggleCollapse = () => {
|
||||
this.setState(state => ({
|
||||
logsContainerOpen: !state.logsContainerOpen,
|
||||
}));
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
loading,
|
||||
@@ -130,8 +116,6 @@ export class LogsContainer extends PureComponent<LogsContainerProps, LogsContain
|
||||
exploreId,
|
||||
} = this.props;
|
||||
|
||||
const { logsContainerOpen } = this.state;
|
||||
|
||||
return (
|
||||
<>
|
||||
<LogsCrossFadeTransition visible={isLive}>
|
||||
@@ -151,13 +135,7 @@ export class LogsContainer extends PureComponent<LogsContainerProps, LogsContain
|
||||
</Collapse>
|
||||
</LogsCrossFadeTransition>
|
||||
<LogsCrossFadeTransition visible={!isLive}>
|
||||
<Collapse
|
||||
label="Logs"
|
||||
loading={loading}
|
||||
isOpen={logsContainerOpen}
|
||||
onToggle={this.onToggleCollapse}
|
||||
collapsible
|
||||
>
|
||||
<Collapse label="Logs" loading={loading} isOpen>
|
||||
<Logs
|
||||
dedupStrategy={this.props.dedupStrategy || LogsDedupStrategy.none}
|
||||
logRows={logRows}
|
||||
|
||||
@@ -2,7 +2,6 @@ import React from 'react';
|
||||
import { render, shallow } from 'enzyme';
|
||||
import { TableContainer } from './TableContainer';
|
||||
import { DataFrame } from '@grafana/data';
|
||||
import { toggleTable } from './state/actions';
|
||||
import { ExploreId } from 'app/types/explore';
|
||||
|
||||
describe('TableContainer', () => {
|
||||
@@ -12,9 +11,7 @@ describe('TableContainer', () => {
|
||||
loading: false,
|
||||
width: 800,
|
||||
onCellFilterAdded: jest.fn(),
|
||||
showingTable: true,
|
||||
tableResult: {} as DataFrame,
|
||||
toggleTable: {} as typeof toggleTable,
|
||||
splitOpen: (() => {}) as any,
|
||||
range: {} as any,
|
||||
};
|
||||
@@ -29,13 +26,11 @@ describe('TableContainer', () => {
|
||||
loading: false,
|
||||
width: 800,
|
||||
onCellFilterAdded: jest.fn(),
|
||||
showingTable: true,
|
||||
tableResult: {
|
||||
name: 'TableResultName',
|
||||
fields: [],
|
||||
length: 0,
|
||||
} as DataFrame,
|
||||
toggleTable: {} as typeof toggleTable,
|
||||
splitOpen: (() => {}) as any,
|
||||
range: {} as any,
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ import { DataFrame, TimeRange, ValueLinkConfig } from '@grafana/data';
|
||||
import { Collapse, Table } from '@grafana/ui';
|
||||
import { ExploreId, ExploreItemState } from 'app/types/explore';
|
||||
import { StoreState } from 'app/types';
|
||||
import { splitOpen, toggleTable } from './state/actions';
|
||||
import { splitOpen } from './state/actions';
|
||||
import { config } from 'app/core/config';
|
||||
import { PANEL_BORDER } from 'app/core/constants';
|
||||
import { MetaInfoText } from './MetaInfoText';
|
||||
@@ -18,18 +18,12 @@ interface TableContainerProps {
|
||||
loading: boolean;
|
||||
width: number;
|
||||
onCellFilterAdded?: (filter: FilterItem) => void;
|
||||
showingTable: boolean;
|
||||
tableResult?: DataFrame;
|
||||
toggleTable: typeof toggleTable;
|
||||
splitOpen: typeof splitOpen;
|
||||
range: TimeRange;
|
||||
}
|
||||
|
||||
export class TableContainer extends PureComponent<TableContainerProps> {
|
||||
onClickTableButton = () => {
|
||||
this.props.toggleTable(this.props.exploreId, this.props.showingTable);
|
||||
};
|
||||
|
||||
getTableHeight() {
|
||||
const { tableResult } = this.props;
|
||||
|
||||
@@ -42,7 +36,7 @@ export class TableContainer extends PureComponent<TableContainerProps> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loading, onCellFilterAdded, showingTable, tableResult, width, splitOpen, range, ariaLabel } = this.props;
|
||||
const { loading, onCellFilterAdded, tableResult, width, splitOpen, range, ariaLabel } = this.props;
|
||||
|
||||
const height = this.getTableHeight();
|
||||
const tableWidth = width - config.theme.panelPadding * 2 - PANEL_BORDER;
|
||||
@@ -60,7 +54,7 @@ export class TableContainer extends PureComponent<TableContainerProps> {
|
||||
}
|
||||
|
||||
return (
|
||||
<Collapse label="Table" loading={loading} collapsible isOpen={showingTable} onToggle={this.onClickTableButton}>
|
||||
<Collapse label="Table" loading={loading} isOpen>
|
||||
{hasTableResult ? (
|
||||
<Table
|
||||
ariaLabel={ariaLabel}
|
||||
@@ -81,13 +75,12 @@ function mapStateToProps(state: StoreState, { exploreId }: { exploreId: string }
|
||||
const explore = state.explore;
|
||||
// @ts-ignore
|
||||
const item: ExploreItemState = explore[exploreId];
|
||||
const { loading: loadingInState, showingTable, tableResult, range } = item;
|
||||
const { loading: loadingInState, tableResult, range } = item;
|
||||
const loading = tableResult && tableResult.length > 0 ? false : loadingInState;
|
||||
return { loading, showingTable, tableResult, range };
|
||||
return { loading, tableResult, range };
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
toggleTable,
|
||||
splitOpen,
|
||||
};
|
||||
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
|
||||
exports[`TableContainer should render component 1`] = `
|
||||
<Collapse
|
||||
collapsible={true}
|
||||
isOpen={true}
|
||||
label="Table"
|
||||
loading={false}
|
||||
onToggle={[Function]}
|
||||
>
|
||||
<Memo(MetaInfoText)
|
||||
metaItems={
|
||||
|
||||
@@ -126,14 +126,6 @@ export interface SyncTimesPayload {
|
||||
syncedTimes: boolean;
|
||||
}
|
||||
|
||||
export interface ToggleTablePayload {
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
|
||||
export interface ToggleGraphPayload {
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
|
||||
export interface UpdateUIStatePayload extends Partial<ExploreUIState> {
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
@@ -296,16 +288,6 @@ export const richHistoryUpdatedAction = createAction<any>('explore/richHistoryUp
|
||||
*/
|
||||
export const updateUIStateAction = createAction<UpdateUIStatePayload>('explore/updateUIState');
|
||||
|
||||
/**
|
||||
* Expand/collapse the table result viewer. When collapsed, table queries won't be run.
|
||||
*/
|
||||
export const toggleTableAction = createAction<ToggleTablePayload>('explore/toggleTable');
|
||||
|
||||
/**
|
||||
* Expand/collapse the graph result viewer. When collapsed, graph queries won't be run.
|
||||
*/
|
||||
export const toggleGraphAction = createAction<ToggleGraphPayload>('explore/toggleGraph');
|
||||
|
||||
/**
|
||||
* Updates datasource instance before datasouce loading has started
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Libraries
|
||||
import { map, throttleTime } from 'rxjs/operators';
|
||||
import { identity } from 'rxjs';
|
||||
import { ActionCreatorWithPayload, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { DataSourceSrv } from '@grafana/runtime';
|
||||
import { RefreshPicker } from '@grafana/ui';
|
||||
import {
|
||||
@@ -77,10 +77,6 @@ import {
|
||||
splitCloseAction,
|
||||
splitOpenAction,
|
||||
syncTimesAction,
|
||||
toggleGraphAction,
|
||||
ToggleGraphPayload,
|
||||
toggleTableAction,
|
||||
ToggleTablePayload,
|
||||
updateDatasourceInstanceAction,
|
||||
updateUIStateAction,
|
||||
changeLoadingStateAction,
|
||||
@@ -429,8 +425,6 @@ export const runQueries = (exploreId: ExploreId): ThunkResult<void> => {
|
||||
queryResponse,
|
||||
querySubscription,
|
||||
history,
|
||||
showingGraph,
|
||||
showingTable,
|
||||
} = exploreItemState;
|
||||
|
||||
if (!hasNonEmptyQuery(queries)) {
|
||||
@@ -461,8 +455,6 @@ export const runQueries = (exploreId: ExploreId): ThunkResult<void> => {
|
||||
// maxDataPoints: mode === ExploreMode.Logs && datasourceId === 'loki' ? undefined : containerWidth,
|
||||
maxDataPoints: containerWidth,
|
||||
liveStreaming: live,
|
||||
showingGraph,
|
||||
showingTable,
|
||||
};
|
||||
|
||||
const datasourceName = exploreItemState.requestedDatasourceName;
|
||||
@@ -577,9 +569,9 @@ export const stateSave = (): ThunkResult<void> => {
|
||||
queries: left.queries.map(clearQueryKeys),
|
||||
range: toRawTimeRange(left.range),
|
||||
ui: {
|
||||
showingGraph: left.showingGraph,
|
||||
showingGraph: true,
|
||||
showingLogs: true,
|
||||
showingTable: left.showingTable,
|
||||
showingTable: true,
|
||||
dedupStrategy: left.dedupStrategy,
|
||||
},
|
||||
};
|
||||
@@ -590,9 +582,9 @@ export const stateSave = (): ThunkResult<void> => {
|
||||
queries: right.queries.map(clearQueryKeys),
|
||||
range: toRawTimeRange(right.range),
|
||||
ui: {
|
||||
showingGraph: right.showingGraph,
|
||||
showingGraph: true,
|
||||
showingLogs: true,
|
||||
showingTable: right.showingTable,
|
||||
showingTable: true,
|
||||
dedupStrategy: right.dedupStrategy,
|
||||
},
|
||||
};
|
||||
@@ -753,46 +745,6 @@ export function syncTimes(exploreId: ExploreId): ThunkResult<void> {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates action to collapse graph/logs/table panel. When panel is collapsed,
|
||||
* queries won't be run
|
||||
*/
|
||||
const togglePanelActionCreator = (
|
||||
actionCreator: ActionCreatorWithPayload<ToggleGraphPayload> | ActionCreatorWithPayload<ToggleTablePayload>
|
||||
) => (exploreId: ExploreId, isPanelVisible: boolean): ThunkResult<void> => {
|
||||
return dispatch => {
|
||||
let uiFragmentStateUpdate: Partial<ExploreUIState>;
|
||||
const shouldRunQueries = !isPanelVisible;
|
||||
|
||||
switch (actionCreator.type) {
|
||||
case toggleGraphAction.type:
|
||||
uiFragmentStateUpdate = { showingGraph: !isPanelVisible };
|
||||
break;
|
||||
case toggleTableAction.type:
|
||||
uiFragmentStateUpdate = { showingTable: !isPanelVisible };
|
||||
break;
|
||||
}
|
||||
|
||||
dispatch(actionCreator({ exploreId }));
|
||||
// The switch further up is exhaustive so uiFragmentStateUpdate should definitely be initialized
|
||||
dispatch(updateExploreUIState(exploreId, uiFragmentStateUpdate!));
|
||||
|
||||
if (shouldRunQueries) {
|
||||
dispatch(runQueries(exploreId));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Expand/collapse the graph result viewer. When collapsed, graph queries won't be run.
|
||||
*/
|
||||
export const toggleGraph = togglePanelActionCreator(toggleGraphAction);
|
||||
|
||||
/**
|
||||
* Expand/collapse the table result viewer. When collapsed, table queries won't be run.
|
||||
*/
|
||||
export const toggleTable = togglePanelActionCreator(toggleTableAction);
|
||||
|
||||
/**
|
||||
* Change logs deduplication strategy and update URL.
|
||||
*/
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
LoadingState,
|
||||
LogsDedupStrategy,
|
||||
RawTimeRange,
|
||||
toDataFrame,
|
||||
UrlQueryMap,
|
||||
ExploreUrlState,
|
||||
} from '@grafana/data';
|
||||
@@ -28,8 +27,6 @@ import {
|
||||
scanStopAction,
|
||||
splitCloseAction,
|
||||
splitOpenAction,
|
||||
toggleGraphAction,
|
||||
toggleTableAction,
|
||||
updateDatasourceInstanceAction,
|
||||
addQueryRowAction,
|
||||
removeQueryRowAction,
|
||||
@@ -160,41 +157,6 @@ describe('Explore item reducer', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('toggling panels', () => {
|
||||
describe('when toggleGraphAction is dispatched', () => {
|
||||
it('then it should set correct state', () => {
|
||||
reducerTester<ExploreItemState>()
|
||||
.givenReducer(itemReducer, ({ graphResult: [] } as unknown) as ExploreItemState)
|
||||
.whenActionIsDispatched(toggleGraphAction({ exploreId: ExploreId.left }))
|
||||
.thenStateShouldEqual(({ showingGraph: true, graphResult: [] } as unknown) as ExploreItemState)
|
||||
.whenActionIsDispatched(toggleGraphAction({ exploreId: ExploreId.left }))
|
||||
.thenStateShouldEqual(({ showingGraph: false, graphResult: [] } as unknown) as ExploreItemState);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when toggleTableAction is dispatched', () => {
|
||||
it('then it should set correct state', () => {
|
||||
const table = toDataFrame({
|
||||
name: 'logs',
|
||||
fields: [
|
||||
{
|
||||
name: 'time',
|
||||
type: 'number',
|
||||
values: [1, 2],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
reducerTester<ExploreItemState>()
|
||||
.givenReducer(itemReducer, ({ tableResult: table } as unknown) as ExploreItemState)
|
||||
.whenActionIsDispatched(toggleTableAction({ exploreId: ExploreId.left }))
|
||||
.thenStateShouldEqual(({ showingTable: true, tableResult: table } as unknown) as ExploreItemState)
|
||||
.whenActionIsDispatched(toggleTableAction({ exploreId: ExploreId.left }))
|
||||
.thenStateShouldEqual(({ showingTable: false, tableResult: table } as unknown) as ExploreItemState);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('changing range', () => {
|
||||
describe('when changeRangeAction is dispatched', () => {
|
||||
it('then it should set correct state', () => {
|
||||
|
||||
@@ -60,9 +60,7 @@ import {
|
||||
SplitCloseActionPayload,
|
||||
splitOpenAction,
|
||||
syncTimesAction,
|
||||
toggleGraphAction,
|
||||
toggleLogLevelAction,
|
||||
toggleTableAction,
|
||||
updateDatasourceInstanceAction,
|
||||
updateUIStateAction,
|
||||
cancelQueriesAction,
|
||||
@@ -106,8 +104,6 @@ export const makeExploreItemState = (): ExploreItemState => ({
|
||||
to: null,
|
||||
} as any,
|
||||
scanning: false,
|
||||
showingGraph: true,
|
||||
showingTable: true,
|
||||
loading: false,
|
||||
queryKeys: [],
|
||||
urlState: null,
|
||||
@@ -409,24 +405,6 @@ export const itemReducer = (state: ExploreItemState = makeExploreItemState(), ac
|
||||
return { ...state, ...action.payload };
|
||||
}
|
||||
|
||||
if (toggleGraphAction.match(action)) {
|
||||
const showingGraph = !state.showingGraph;
|
||||
if (showingGraph) {
|
||||
return { ...state, showingGraph };
|
||||
}
|
||||
|
||||
return { ...state, showingGraph };
|
||||
}
|
||||
|
||||
if (toggleTableAction.match(action)) {
|
||||
const showingTable = !state.showingTable;
|
||||
if (showingTable) {
|
||||
return { ...state, showingTable };
|
||||
}
|
||||
|
||||
return { ...state, showingTable };
|
||||
}
|
||||
|
||||
if (queriesImportedAction.match(action)) {
|
||||
const { queries } = action.payload;
|
||||
return {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import React, { memo, FC } from 'react';
|
||||
import { css } from 'emotion';
|
||||
|
||||
// Types
|
||||
import { ExploreQueryFieldProps } from '@grafana/data';
|
||||
import { RadioButtonGroup } from '@grafana/ui';
|
||||
|
||||
import { PrometheusDatasource } from '../datasource';
|
||||
import { PromQuery, PromOptions } from '../types';
|
||||
@@ -26,6 +28,19 @@ export const PromExploreQueryEditor: FC<Props> = (props: Props) => {
|
||||
}
|
||||
}
|
||||
|
||||
function onQueryTypeChange(value: string) {
|
||||
const { query, onChange } = props;
|
||||
let nextQuery;
|
||||
if (value === 'instant') {
|
||||
nextQuery = { ...query, instant: true, range: false };
|
||||
} else if (value === 'range') {
|
||||
nextQuery = { ...query, instant: false, range: true };
|
||||
} else {
|
||||
nextQuery = { ...query, instant: true, range: true };
|
||||
}
|
||||
onChange(nextQuery);
|
||||
}
|
||||
|
||||
function onReturnKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
|
||||
if (e.key === 'Enter') {
|
||||
onRunQuery();
|
||||
@@ -33,27 +48,62 @@ export const PromExploreQueryEditor: FC<Props> = (props: Props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<PromQueryField
|
||||
datasource={datasource}
|
||||
query={query}
|
||||
onRunQuery={onRunQuery}
|
||||
onChange={onChange}
|
||||
onBlur={() => {}}
|
||||
history={history}
|
||||
data={data}
|
||||
ExtraFieldElement={
|
||||
<PromExploreExtraField
|
||||
label={'Step'}
|
||||
onChangeFunc={onStepChange}
|
||||
onKeyDownFunc={onReturnKeyDown}
|
||||
value={query.interval || ''}
|
||||
hasTooltip={true}
|
||||
tooltipContent={
|
||||
'Time units can be used here, for example: 5s, 1m, 3h, 1d, 1y (Default if no unit is specified: s)'
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<>
|
||||
<PromQueryField
|
||||
datasource={datasource}
|
||||
query={query}
|
||||
onRunQuery={onRunQuery}
|
||||
onChange={onChange}
|
||||
onBlur={() => {}}
|
||||
history={history}
|
||||
data={data}
|
||||
ExtraFieldElement={
|
||||
<PromExploreExtraField
|
||||
label={'Step'}
|
||||
onChangeFunc={onStepChange}
|
||||
onKeyDownFunc={onReturnKeyDown}
|
||||
value={query.interval || ''}
|
||||
hasTooltip={true}
|
||||
tooltipContent={
|
||||
'Time units can be used here, for example: 5s, 1m, 3h, 1d, 1y (Default if no unit is specified: s)'
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<PromExploreRadioButton
|
||||
selected={query.range && query.instant ? 'both' : query.instant ? 'instant' : 'range'}
|
||||
onQueryTypeChange={onQueryTypeChange}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
type PromExploreRadioButtonProps = {
|
||||
selected: string;
|
||||
onQueryTypeChange: (value: string) => void;
|
||||
};
|
||||
|
||||
const PromExploreRadioButton: React.FunctionComponent<PromExploreRadioButtonProps> = ({
|
||||
selected,
|
||||
onQueryTypeChange,
|
||||
}) => {
|
||||
const rangeOptions = [
|
||||
{ value: 'range', label: 'Range' },
|
||||
{ value: 'instant', label: 'Instant' },
|
||||
{ value: 'both', label: 'Both' },
|
||||
];
|
||||
|
||||
return (
|
||||
<div
|
||||
className={css`
|
||||
display: flex;
|
||||
`}
|
||||
>
|
||||
<button className={`gf-form-label gf-form-label--btn width-5`}>
|
||||
<span className="btn-title">Query type</span>
|
||||
</button>
|
||||
<RadioButtonGroup options={rangeOptions} value={selected} onChange={onQueryTypeChange} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -326,7 +326,7 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
||||
return (
|
||||
<>
|
||||
<div className="gf-form-inline gf-form-inline--xs-view-flex-column flex-grow-1">
|
||||
<div className="gf-form flex-shrink-0">
|
||||
<div className="gf-form flex-shrink-0 min-width-5">
|
||||
<ButtonCascader options={metricsOptions} disabled={buttonDisabled} onChange={this.onChangeMetrics}>
|
||||
{chooserText}
|
||||
</ButtonCascader>
|
||||
|
||||
@@ -1,26 +1,43 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`PromExploreQueryEditor should render component 1`] = `
|
||||
<PromQueryField
|
||||
ExtraFieldElement={
|
||||
<PromExploreExtraField
|
||||
hasTooltip={true}
|
||||
label="Step"
|
||||
onChangeFunc={[Function]}
|
||||
onKeyDownFunc={[Function]}
|
||||
tooltipContent="Time units can be used here, for example: 5s, 1m, 3h, 1d, 1y (Default if no unit is specified: s)"
|
||||
value="1s"
|
||||
/>
|
||||
}
|
||||
data={
|
||||
Object {
|
||||
"request": Object {
|
||||
"app": "Grafana",
|
||||
"dashboardId": 1,
|
||||
"interval": "1s",
|
||||
"intervalMs": 1000,
|
||||
"panelId": 1,
|
||||
"range": Object {
|
||||
<Fragment>
|
||||
<PromQueryField
|
||||
ExtraFieldElement={
|
||||
<PromExploreExtraField
|
||||
hasTooltip={true}
|
||||
label="Step"
|
||||
onChangeFunc={[Function]}
|
||||
onKeyDownFunc={[Function]}
|
||||
tooltipContent="Time units can be used here, for example: 5s, 1m, 3h, 1d, 1y (Default if no unit is specified: s)"
|
||||
value="1s"
|
||||
/>
|
||||
}
|
||||
data={
|
||||
Object {
|
||||
"request": Object {
|
||||
"app": "Grafana",
|
||||
"dashboardId": 1,
|
||||
"interval": "1s",
|
||||
"intervalMs": 1000,
|
||||
"panelId": 1,
|
||||
"range": Object {
|
||||
"from": "2020-01-01T00:00:00.000Z",
|
||||
"raw": Object {
|
||||
"from": "2020-01-01T00:00:00.000Z",
|
||||
"to": "2020-01-02T00:00:00.000Z",
|
||||
},
|
||||
"to": "2020-01-02T00:00:00.000Z",
|
||||
},
|
||||
"requestId": "1",
|
||||
"scopedVars": Object {},
|
||||
"startTime": 0,
|
||||
"targets": Array [],
|
||||
"timezone": "GMT",
|
||||
},
|
||||
"series": Array [],
|
||||
"state": "NotStarted",
|
||||
"timeRange": Object {
|
||||
"from": "2020-01-01T00:00:00.000Z",
|
||||
"raw": Object {
|
||||
"from": "2020-01-01T00:00:00.000Z",
|
||||
@@ -28,35 +45,24 @@ exports[`PromExploreQueryEditor should render component 1`] = `
|
||||
},
|
||||
"to": "2020-01-02T00:00:00.000Z",
|
||||
},
|
||||
"requestId": "1",
|
||||
"scopedVars": Object {},
|
||||
"startTime": 0,
|
||||
"targets": Array [],
|
||||
"timezone": "GMT",
|
||||
},
|
||||
"series": Array [],
|
||||
"state": "NotStarted",
|
||||
"timeRange": Object {
|
||||
"from": "2020-01-01T00:00:00.000Z",
|
||||
"raw": Object {
|
||||
"from": "2020-01-01T00:00:00.000Z",
|
||||
"to": "2020-01-02T00:00:00.000Z",
|
||||
},
|
||||
"to": "2020-01-02T00:00:00.000Z",
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
datasource={Object {}}
|
||||
history={Array []}
|
||||
onBlur={[Function]}
|
||||
onChange={[MockFunction]}
|
||||
onRunQuery={[MockFunction]}
|
||||
query={
|
||||
Object {
|
||||
"expr": "",
|
||||
"interval": "1s",
|
||||
"refId": "A",
|
||||
datasource={Object {}}
|
||||
history={Array []}
|
||||
onBlur={[Function]}
|
||||
onChange={[MockFunction]}
|
||||
onRunQuery={[MockFunction]}
|
||||
query={
|
||||
Object {
|
||||
"expr": "",
|
||||
"interval": "1s",
|
||||
"refId": "A",
|
||||
}
|
||||
}
|
||||
}
|
||||
/>
|
||||
/>
|
||||
<PromExploreRadioButton
|
||||
onQueryTypeChange={[Function]}
|
||||
selected="range"
|
||||
/>
|
||||
</Fragment>
|
||||
`;
|
||||
|
||||
@@ -1810,17 +1810,16 @@ describe('prepareTargets', () => {
|
||||
});
|
||||
|
||||
describe('when run from Explore', () => {
|
||||
describe('and both Graph and Table are shown', () => {
|
||||
describe('when query type Both is selected', () => {
|
||||
it('then it should return both instant and time series related objects', () => {
|
||||
const target: PromQuery = {
|
||||
refId: 'A',
|
||||
expr: 'up',
|
||||
range: true,
|
||||
instant: true,
|
||||
};
|
||||
|
||||
const { queries, activeTargets, panelId, end, start } = getPrepareTargetsContext(target, CoreApp.Explore, {
|
||||
showingGraph: true,
|
||||
showingTable: true,
|
||||
});
|
||||
const { queries, activeTargets, panelId, end, start } = getPrepareTargetsContext(target, CoreApp.Explore);
|
||||
|
||||
expect(queries.length).toBe(2);
|
||||
expect(activeTargets.length).toBe(2);
|
||||
@@ -1868,33 +1867,16 @@ describe('prepareTargets', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('and both Graph and Table are hidden', () => {
|
||||
it('then it should return empty arrays', () => {
|
||||
describe('when query type Instant is selected', () => {
|
||||
it('then it should just add targets', () => {
|
||||
const target: PromQuery = {
|
||||
refId: 'A',
|
||||
expr: 'up',
|
||||
showingGraph: false,
|
||||
showingTable: false,
|
||||
instant: true,
|
||||
range: false,
|
||||
};
|
||||
|
||||
const { queries, activeTargets } = getPrepareTargetsContext(target, CoreApp.Explore);
|
||||
|
||||
expect(queries.length).toBe(0);
|
||||
expect(activeTargets.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and Graph is hidden', () => {
|
||||
it('then it should return only intant related objects', () => {
|
||||
const target: PromQuery = {
|
||||
refId: 'A',
|
||||
expr: 'up',
|
||||
};
|
||||
|
||||
const { queries, activeTargets, panelId, end, start } = getPrepareTargetsContext(target, CoreApp.Explore, {
|
||||
showingGraph: false,
|
||||
showingTable: true,
|
||||
});
|
||||
const { queries, activeTargets, panelId, end, start } = getPrepareTargetsContext(target, CoreApp.Explore);
|
||||
|
||||
expect(queries.length).toBe(1);
|
||||
expect(activeTargets.length).toBe(1);
|
||||
@@ -1908,55 +1890,43 @@ describe('prepareTargets', () => {
|
||||
hinting: undefined,
|
||||
instant: true,
|
||||
refId: target.refId,
|
||||
requestId: panelId + target.refId + '_instant',
|
||||
requestId: panelId + target.refId,
|
||||
start,
|
||||
step: 1,
|
||||
});
|
||||
expect(activeTargets[0]).toEqual({
|
||||
...target,
|
||||
format: 'table',
|
||||
instant: true,
|
||||
requestId: panelId + target.refId + '_instant',
|
||||
valueWithRefId: true,
|
||||
});
|
||||
expect(activeTargets[0]).toEqual(target);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('and Table is hidden', () => {
|
||||
it('then it should return only time series related objects', () => {
|
||||
const target: PromQuery = {
|
||||
refId: 'A',
|
||||
expr: 'up',
|
||||
};
|
||||
describe('when query type Range is selected', () => {
|
||||
it('then it should just add targets', () => {
|
||||
const target: PromQuery = {
|
||||
refId: 'A',
|
||||
expr: 'up',
|
||||
range: true,
|
||||
instant: false,
|
||||
};
|
||||
|
||||
const { queries, activeTargets, panelId, end, start } = getPrepareTargetsContext(target, CoreApp.Explore, {
|
||||
showingGraph: true,
|
||||
showingTable: false,
|
||||
});
|
||||
const { queries, activeTargets, panelId, end, start } = getPrepareTargetsContext(target, CoreApp.Explore);
|
||||
|
||||
expect(queries.length).toBe(1);
|
||||
expect(activeTargets.length).toBe(1);
|
||||
expect(queries[0]).toEqual({
|
||||
end,
|
||||
expr: 'up',
|
||||
headers: {
|
||||
'X-Dashboard-Id': undefined,
|
||||
'X-Panel-Id': panelId,
|
||||
},
|
||||
hinting: undefined,
|
||||
instant: false,
|
||||
refId: target.refId,
|
||||
requestId: panelId + target.refId,
|
||||
start,
|
||||
step: 1,
|
||||
});
|
||||
expect(activeTargets[0]).toEqual({
|
||||
...target,
|
||||
format: 'time_series',
|
||||
instant: false,
|
||||
requestId: panelId + target.refId,
|
||||
});
|
||||
expect(queries.length).toBe(1);
|
||||
expect(activeTargets.length).toBe(1);
|
||||
expect(queries[0]).toEqual({
|
||||
end,
|
||||
expr: 'up',
|
||||
headers: {
|
||||
'X-Dashboard-Id': undefined,
|
||||
'X-Panel-Id': panelId,
|
||||
},
|
||||
hinting: undefined,
|
||||
instant: false,
|
||||
refId: target.refId,
|
||||
requestId: panelId + target.refId,
|
||||
start,
|
||||
step: 1,
|
||||
});
|
||||
expect(activeTargets[0]).toEqual(target);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -176,7 +176,8 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
|
||||
query: PromQueryRequest,
|
||||
target: PromQuery,
|
||||
responseListLength: number,
|
||||
scopedVars?: ScopedVars
|
||||
scopedVars?: ScopedVars,
|
||||
mixedQueries?: boolean
|
||||
) => {
|
||||
// Keeping original start/end for transformers
|
||||
const transformerOptions = {
|
||||
@@ -191,8 +192,10 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
|
||||
refId: target.refId,
|
||||
valueWithRefId: target.valueWithRefId,
|
||||
meta: {
|
||||
/** Fix for showing of Prometheus results in Explore table. We want to show result of instant query in table and the rest of time series in graph */
|
||||
preferredVisualisationType: query.instant ? 'table' : 'graph',
|
||||
/** Fix for showing of Prometheus results in Explore table.
|
||||
* We want to show result of instant query always in table and result of range query based on target.runAll;
|
||||
*/
|
||||
preferredVisualisationType: target.instant ? 'table' : mixedQueries ? 'graph' : undefined,
|
||||
},
|
||||
};
|
||||
const series = this.resultTransformer.transform(response, transformerOptions);
|
||||
@@ -211,32 +214,32 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
|
||||
|
||||
target.requestId = options.panelId + target.refId;
|
||||
|
||||
if (options.app !== CoreApp.Explore) {
|
||||
activeTargets.push(target);
|
||||
queries.push(this.createQuery(target, options, start, end));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (options.showingTable) {
|
||||
// create instant target only if Table is showed in Explore
|
||||
if (target.range && target.instant) {
|
||||
// If running both (only available in Explore) - instant and range query, prepare both targets
|
||||
// Create instant target
|
||||
const instantTarget: any = cloneDeep(target);
|
||||
instantTarget.format = 'table';
|
||||
instantTarget.instant = true;
|
||||
instantTarget.range = false;
|
||||
instantTarget.valueWithRefId = true;
|
||||
delete instantTarget.maxDataPoints;
|
||||
instantTarget.requestId += '_instant';
|
||||
|
||||
activeTargets.push(instantTarget);
|
||||
queries.push(this.createQuery(instantTarget, options, start, end));
|
||||
}
|
||||
// Create range target
|
||||
const rangeTarget: any = cloneDeep(target);
|
||||
rangeTarget.format = 'time_series';
|
||||
rangeTarget.instant = false;
|
||||
instantTarget.range = true;
|
||||
|
||||
if (options.showingGraph) {
|
||||
// create time series target only if Graph is showed in Explore
|
||||
target.format = 'time_series';
|
||||
target.instant = false;
|
||||
|
||||
activeTargets.push(target);
|
||||
// Add both targets to activeTargets and queries arrays
|
||||
activeTargets.push(instantTarget, rangeTarget);
|
||||
queries.push(
|
||||
this.createQuery(instantTarget, options, start, end),
|
||||
this.createQuery(rangeTarget, options, start, end)
|
||||
);
|
||||
} else {
|
||||
queries.push(this.createQuery(target, options, start, end));
|
||||
activeTargets.push(target);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,6 +271,8 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
|
||||
|
||||
private exploreQuery(queries: PromQueryRequest[], activeTargets: PromQuery[], end: number) {
|
||||
let runningQueriesCount = queries.length;
|
||||
const mixedQueries = activeTargets.some(t => t.range) && activeTargets.some(t => t.instant);
|
||||
|
||||
const subQueries = queries.map((query, index) => {
|
||||
const target = activeTargets[index];
|
||||
|
||||
@@ -281,7 +286,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
|
||||
tap(() => runningQueriesCount--),
|
||||
filter((response: any) => (response.cancelled ? false : true)),
|
||||
map((response: any) => {
|
||||
const data = this.processResult(response, query, target, queries.length);
|
||||
const data = this.processResult(response, query, target, queries.length, undefined, mixedQueries);
|
||||
return {
|
||||
data,
|
||||
key: query.requestId,
|
||||
|
||||
@@ -4,6 +4,7 @@ export interface PromQuery extends DataQuery {
|
||||
expr: string;
|
||||
format?: string;
|
||||
instant?: boolean;
|
||||
range?: boolean;
|
||||
hinting?: boolean;
|
||||
interval?: string;
|
||||
intervalFactor?: number;
|
||||
|
||||
@@ -118,14 +118,6 @@ export interface ExploreItemState {
|
||||
* Current scanning range to be shown to the user while scanning is active.
|
||||
*/
|
||||
scanRange?: RawTimeRange;
|
||||
/**
|
||||
* True if graph result viewer is expanded. Query runs will contain graph queries.
|
||||
*/
|
||||
showingGraph: boolean;
|
||||
/**
|
||||
* True if table result viewer is expanded. Query runs will contain table queries.
|
||||
*/
|
||||
showingTable: boolean;
|
||||
|
||||
loading: boolean;
|
||||
/**
|
||||
@@ -206,8 +198,6 @@ export interface QueryOptions {
|
||||
minInterval?: string;
|
||||
maxDataPoints?: number;
|
||||
liveStreaming?: boolean;
|
||||
showingGraph?: boolean;
|
||||
showingTable?: boolean;
|
||||
mode?: ExploreMode;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user