mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: allow changing the graph type (#40522)
* explore: allow switching graph-styles * refactor: simplify code * adjust test to test a case that can really happen * better generate-options approach * explore: graph styles: remove url functionality * not-stacked-bars should be filled
This commit is contained in:
@@ -201,6 +201,25 @@ export const safeStringifyValue = (value: any, space?: number) => {
|
|||||||
return '';
|
return '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const EXPLORE_GRAPH_STYLES = ['lines', 'bars', 'points', 'stacked_lines', 'stacked_bars'] as const;
|
||||||
|
|
||||||
|
export type ExploreGraphStyle = typeof EXPLORE_GRAPH_STYLES[number];
|
||||||
|
|
||||||
|
const DEFAULT_GRAPH_STYLE: ExploreGraphStyle = 'lines';
|
||||||
|
// we use this function to take any kind of data we loaded
|
||||||
|
// from an external source (URL, localStorage, whatever),
|
||||||
|
// and extract the graph-style from it, or return the default
|
||||||
|
// graph-style if we are not able to do that.
|
||||||
|
// it is important that this function is able to take any form of data,
|
||||||
|
// (be it objects, or arrays, or booleans or whatever),
|
||||||
|
// and produce a best-effort graphStyle.
|
||||||
|
// note that typescript makes sure we make no mistake in this function.
|
||||||
|
// we do not rely on ` as ` or ` any `.
|
||||||
|
export const toGraphStyle = (data: unknown): ExploreGraphStyle => {
|
||||||
|
const found = EXPLORE_GRAPH_STYLES.find((v) => v === data);
|
||||||
|
return found ?? DEFAULT_GRAPH_STYLE;
|
||||||
|
};
|
||||||
|
|
||||||
export function parseUrlState(initial: string | undefined): ExploreUrlState {
|
export function parseUrlState(initial: string | undefined): ExploreUrlState {
|
||||||
const parsed = safeParseJson(initial);
|
const parsed = safeParseJson(initial);
|
||||||
const errorResult: any = {
|
const errorResult: any = {
|
||||||
|
@@ -85,6 +85,8 @@ const dummyProps: Props = {
|
|||||||
splitOpen: (() => {}) as any,
|
splitOpen: (() => {}) as any,
|
||||||
logsVolumeData: undefined,
|
logsVolumeData: undefined,
|
||||||
loadLogsVolumeData: () => {},
|
loadLogsVolumeData: () => {},
|
||||||
|
changeGraphStyle: () => {},
|
||||||
|
graphStyle: 'lines',
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('Explore', () => {
|
describe('Explore', () => {
|
||||||
|
@@ -14,7 +14,7 @@ import TableContainer from './TableContainer';
|
|||||||
import RichHistoryContainer from './RichHistory/RichHistoryContainer';
|
import RichHistoryContainer from './RichHistory/RichHistoryContainer';
|
||||||
import ExploreQueryInspector from './ExploreQueryInspector';
|
import ExploreQueryInspector from './ExploreQueryInspector';
|
||||||
import { splitOpen } from './state/main';
|
import { splitOpen } from './state/main';
|
||||||
import { changeSize } from './state/explorePane';
|
import { changeSize, changeGraphStyle } from './state/explorePane';
|
||||||
import { updateTimeRange } from './state/time';
|
import { updateTimeRange } from './state/time';
|
||||||
import { addQueryRow, loadLogsVolumeData, modifyQueries, scanStart, scanStopAction, setQueries } from './state/query';
|
import { addQueryRow, loadLogsVolumeData, modifyQueries, scanStart, scanStopAction, setQueries } from './state/query';
|
||||||
import { ExploreId, ExploreItemState } from 'app/types/explore';
|
import { ExploreId, ExploreItemState } from 'app/types/explore';
|
||||||
@@ -29,6 +29,8 @@ import { ResponseErrorContainer } from './ResponseErrorContainer';
|
|||||||
import { TraceViewContainer } from './TraceView/TraceViewContainer';
|
import { TraceViewContainer } from './TraceView/TraceViewContainer';
|
||||||
import { ExploreGraph } from './ExploreGraph';
|
import { ExploreGraph } from './ExploreGraph';
|
||||||
import { LogsVolumePanel } from './LogsVolumePanel';
|
import { LogsVolumePanel } from './LogsVolumePanel';
|
||||||
|
import { ExploreGraphLabel } from './ExploreGraphLabel';
|
||||||
|
import { ExploreGraphStyle } from 'app/core/utils/explore';
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => {
|
const getStyles = (theme: GrafanaTheme2) => {
|
||||||
return {
|
return {
|
||||||
@@ -162,6 +164,11 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
|||||||
updateTimeRange({ exploreId, absoluteRange });
|
updateTimeRange({ exploreId, absoluteRange });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onChangeGraphStyle = (graphStyle: ExploreGraphStyle) => {
|
||||||
|
const { exploreId, changeGraphStyle } = this.props;
|
||||||
|
changeGraphStyle(exploreId, graphStyle);
|
||||||
|
};
|
||||||
|
|
||||||
toggleShowRichHistory = () => {
|
toggleShowRichHistory = () => {
|
||||||
this.setState((state) => {
|
this.setState((state) => {
|
||||||
return {
|
return {
|
||||||
@@ -187,11 +194,13 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderGraphPanel(width: number) {
|
renderGraphPanel(width: number) {
|
||||||
const { graphResult, absoluteRange, timeZone, splitOpen, queryResponse, loading, theme } = this.props;
|
const { graphResult, absoluteRange, timeZone, splitOpen, queryResponse, loading, theme, graphStyle } = this.props;
|
||||||
const spacing = parseInt(theme.spacing(2).slice(0, -2), 10);
|
const spacing = parseInt(theme.spacing(2).slice(0, -2), 10);
|
||||||
|
const label = <ExploreGraphLabel graphStyle={graphStyle} onChangeGraphStyle={this.onChangeGraphStyle} />;
|
||||||
return (
|
return (
|
||||||
<Collapse label="Graph" loading={loading} isOpen>
|
<Collapse label={label} loading={loading} isOpen>
|
||||||
<ExploreGraph
|
<ExploreGraph
|
||||||
|
graphStyle={graphStyle}
|
||||||
data={graphResult!}
|
data={graphResult!}
|
||||||
height={400}
|
height={400}
|
||||||
width={width - spacing}
|
width={width - spacing}
|
||||||
@@ -390,6 +399,7 @@ function mapStateToProps(state: StoreState, { exploreId }: ExploreProps) {
|
|||||||
queryResponse,
|
queryResponse,
|
||||||
showNodeGraph,
|
showNodeGraph,
|
||||||
loading,
|
loading,
|
||||||
|
graphStyle,
|
||||||
} = item;
|
} = item;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -410,11 +420,13 @@ function mapStateToProps(state: StoreState, { exploreId }: ExploreProps) {
|
|||||||
showTrace,
|
showTrace,
|
||||||
showNodeGraph,
|
showNodeGraph,
|
||||||
loading,
|
loading,
|
||||||
|
graphStyle,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
changeSize,
|
changeSize,
|
||||||
|
changeGraphStyle,
|
||||||
modifyQueries,
|
modifyQueries,
|
||||||
scanStart,
|
scanStart,
|
||||||
scanStopAction,
|
scanStopAction,
|
||||||
|
@@ -26,12 +26,14 @@ import {
|
|||||||
useTheme2,
|
useTheme2,
|
||||||
} from '@grafana/ui';
|
} from '@grafana/ui';
|
||||||
import appEvents from 'app/core/app_events';
|
import appEvents from 'app/core/app_events';
|
||||||
|
import { ExploreGraphStyle } from 'app/core/utils/explore';
|
||||||
import { defaultGraphConfig, getGraphFieldConfig } from 'app/plugins/panel/timeseries/config';
|
import { defaultGraphConfig, getGraphFieldConfig } from 'app/plugins/panel/timeseries/config';
|
||||||
import { TimeSeriesOptions } from 'app/plugins/panel/timeseries/types';
|
import { TimeSeriesOptions } from 'app/plugins/panel/timeseries/types';
|
||||||
import { identity } from 'lodash';
|
import { identity } from 'lodash';
|
||||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { usePrevious } from 'react-use';
|
import { usePrevious } from 'react-use';
|
||||||
import { seriesVisibilityConfigFactory } from '../dashboard/dashgrid/SeriesVisibilityConfigFactory';
|
import { seriesVisibilityConfigFactory } from '../dashboard/dashgrid/SeriesVisibilityConfigFactory';
|
||||||
|
import { applyGraphStyle } from './exploreGraphStyleUtils';
|
||||||
|
|
||||||
const MAX_NUMBER_OF_TIME_SERIES = 20;
|
const MAX_NUMBER_OF_TIME_SERIES = 20;
|
||||||
|
|
||||||
@@ -47,6 +49,7 @@ interface Props {
|
|||||||
tooltipDisplayMode?: TooltipDisplayMode;
|
tooltipDisplayMode?: TooltipDisplayMode;
|
||||||
splitOpenFn?: SplitOpen;
|
splitOpenFn?: SplitOpen;
|
||||||
onChangeTime: (timeRange: AbsoluteTimeRange) => void;
|
onChangeTime: (timeRange: AbsoluteTimeRange) => void;
|
||||||
|
graphStyle: ExploreGraphStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ExploreGraph({
|
export function ExploreGraph({
|
||||||
@@ -60,6 +63,7 @@ export function ExploreGraph({
|
|||||||
annotations,
|
annotations,
|
||||||
onHiddenSeriesChanged,
|
onHiddenSeriesChanged,
|
||||||
splitOpenFn,
|
splitOpenFn,
|
||||||
|
graphStyle,
|
||||||
tooltipDisplayMode = TooltipDisplayMode.Single,
|
tooltipDisplayMode = TooltipDisplayMode.Single,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const theme = useTheme2();
|
const theme = useTheme2();
|
||||||
@@ -101,15 +105,16 @@ export function ExploreGraph({
|
|||||||
|
|
||||||
const dataWithConfig = useMemo(() => {
|
const dataWithConfig = useMemo(() => {
|
||||||
const registry = createFieldConfigRegistry(getGraphFieldConfig(defaultGraphConfig), 'Explore');
|
const registry = createFieldConfigRegistry(getGraphFieldConfig(defaultGraphConfig), 'Explore');
|
||||||
|
const styledFieldConfig = applyGraphStyle(fieldConfig, graphStyle);
|
||||||
return applyFieldOverrides({
|
return applyFieldOverrides({
|
||||||
fieldConfig,
|
fieldConfig: styledFieldConfig,
|
||||||
data,
|
data,
|
||||||
timeZone,
|
timeZone,
|
||||||
replaceVariables: (value) => value, // We don't need proper replace here as it is only used in getLinks and we use getFieldLinks
|
replaceVariables: (value) => value, // We don't need proper replace here as it is only used in getLinks and we use getFieldLinks
|
||||||
theme,
|
theme,
|
||||||
fieldConfigRegistry: registry,
|
fieldConfigRegistry: registry,
|
||||||
});
|
});
|
||||||
}, [fieldConfig, data, timeZone, theme]);
|
}, [fieldConfig, graphStyle, data, timeZone, theme]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (onHiddenSeriesChanged) {
|
if (onHiddenSeriesChanged) {
|
||||||
|
31
public/app/features/explore/ExploreGraphLabel.tsx
Normal file
31
public/app/features/explore/ExploreGraphLabel.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
|
import { SelectableValue } from '@grafana/data';
|
||||||
|
import { RadioButtonGroup } from '@grafana/ui';
|
||||||
|
import { ExploreGraphStyle, EXPLORE_GRAPH_STYLES } from 'app/core/utils/explore';
|
||||||
|
|
||||||
|
const ALL_GRAPH_STYLE_OPTIONS: Array<SelectableValue<ExploreGraphStyle>> = EXPLORE_GRAPH_STYLES.map((style) => ({
|
||||||
|
value: style,
|
||||||
|
// capital-case it and switch `_` to ` `
|
||||||
|
label: style[0].toUpperCase() + style.slice(1).replace(/_/, ' '),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const spacing = css({
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
});
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
graphStyle: ExploreGraphStyle;
|
||||||
|
onChangeGraphStyle: (style: ExploreGraphStyle) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ExploreGraphLabel(props: Props) {
|
||||||
|
const { graphStyle, onChangeGraphStyle } = props;
|
||||||
|
return (
|
||||||
|
<div className={spacing}>
|
||||||
|
Graph
|
||||||
|
<RadioButtonGroup size="sm" options={ALL_GRAPH_STYLE_OPTIONS} value={graphStyle} onChange={onChangeGraphStyle} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@@ -305,6 +305,7 @@ export class UnthemedLogs extends PureComponent<Props, State> {
|
|||||||
response.
|
response.
|
||||||
</div>
|
</div>
|
||||||
<ExploreGraph
|
<ExploreGraph
|
||||||
|
graphStyle="lines"
|
||||||
data={logsSeries}
|
data={logsSeries}
|
||||||
height={150}
|
height={150}
|
||||||
width={width}
|
width={width}
|
||||||
|
@@ -37,6 +37,7 @@ export function LogsVolumePanel(props: Props) {
|
|||||||
if (logsVolumeData.data.length > 0) {
|
if (logsVolumeData.data.length > 0) {
|
||||||
LogsVolumePanelContent = (
|
LogsVolumePanelContent = (
|
||||||
<ExploreGraph
|
<ExploreGraph
|
||||||
|
graphStyle="lines"
|
||||||
loadingState={LoadingState.Done}
|
loadingState={LoadingState.Done}
|
||||||
data={logsVolumeData.data}
|
data={logsVolumeData.data}
|
||||||
height={height}
|
height={height}
|
||||||
|
56
public/app/features/explore/exploreGraphStyleUtils.ts
Normal file
56
public/app/features/explore/exploreGraphStyleUtils.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import produce from 'immer';
|
||||||
|
import { FieldConfigSource } from '@grafana/data';
|
||||||
|
import { GraphDrawStyle, GraphFieldConfig, StackingMode } from '@grafana/schema';
|
||||||
|
import { ExploreGraphStyle } from 'app/core/utils/explore';
|
||||||
|
|
||||||
|
export type FieldConfig = FieldConfigSource<GraphFieldConfig>;
|
||||||
|
|
||||||
|
export function applyGraphStyle(config: FieldConfig, style: ExploreGraphStyle): FieldConfig {
|
||||||
|
return produce(config, (draft) => {
|
||||||
|
if (draft.defaults.custom === undefined) {
|
||||||
|
draft.defaults.custom = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const { custom } = draft.defaults;
|
||||||
|
|
||||||
|
if (custom.stacking === undefined) {
|
||||||
|
custom.stacking = { group: 'A' };
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (style) {
|
||||||
|
case 'lines':
|
||||||
|
custom.drawStyle = GraphDrawStyle.Line;
|
||||||
|
custom.stacking.mode = StackingMode.None;
|
||||||
|
custom.fillOpacity = 0;
|
||||||
|
break;
|
||||||
|
case 'bars':
|
||||||
|
custom.drawStyle = GraphDrawStyle.Bars;
|
||||||
|
custom.stacking.mode = StackingMode.None;
|
||||||
|
custom.fillOpacity = 100;
|
||||||
|
break;
|
||||||
|
case 'points':
|
||||||
|
custom.drawStyle = GraphDrawStyle.Points;
|
||||||
|
custom.stacking.mode = StackingMode.None;
|
||||||
|
custom.fillOpacity = 0;
|
||||||
|
break;
|
||||||
|
case 'stacked_lines':
|
||||||
|
custom.drawStyle = GraphDrawStyle.Line;
|
||||||
|
custom.stacking.mode = StackingMode.Normal;
|
||||||
|
custom.fillOpacity = 100;
|
||||||
|
break;
|
||||||
|
case 'stacked_bars':
|
||||||
|
custom.drawStyle = GraphDrawStyle.Bars;
|
||||||
|
custom.stacking.mode = StackingMode.Normal;
|
||||||
|
custom.fillOpacity = 100;
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
// should never happen
|
||||||
|
// NOTE: casting to `never` will cause typescript
|
||||||
|
// to verify that the switch statement checks every possible
|
||||||
|
// enum-value
|
||||||
|
const invalidValue: never = style;
|
||||||
|
throw new Error(`Invalid graph-style: ${invalidValue}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
@@ -8,6 +8,7 @@ import {
|
|||||||
ensureQueries,
|
ensureQueries,
|
||||||
generateNewKeyAndAddRefIdIfMissing,
|
generateNewKeyAndAddRefIdIfMissing,
|
||||||
getTimeRangeFromUrl,
|
getTimeRangeFromUrl,
|
||||||
|
ExploreGraphStyle,
|
||||||
} from 'app/core/utils/explore';
|
} from 'app/core/utils/explore';
|
||||||
import { ExploreId, ExploreItemState } from 'app/types/explore';
|
import { ExploreId, ExploreItemState } from 'app/types/explore';
|
||||||
import { queryReducer, runQueries, setQueriesAction } from './query';
|
import { queryReducer, runQueries, setQueriesAction } from './query';
|
||||||
@@ -19,6 +20,7 @@ import {
|
|||||||
loadAndInitDatasource,
|
loadAndInitDatasource,
|
||||||
createEmptyQueryResponse,
|
createEmptyQueryResponse,
|
||||||
getUrlStateFromPaneState,
|
getUrlStateFromPaneState,
|
||||||
|
storeGraphStyle,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import { createAction, PayloadAction } from '@reduxjs/toolkit';
|
import { createAction, PayloadAction } from '@reduxjs/toolkit';
|
||||||
import { EventBusExtended, DataQuery, ExploreUrlState, TimeRange, HistoryItem, DataSourceApi } from '@grafana/data';
|
import { EventBusExtended, DataQuery, ExploreUrlState, TimeRange, HistoryItem, DataSourceApi } from '@grafana/data';
|
||||||
@@ -76,6 +78,20 @@ export function changeSize(
|
|||||||
return changeSizeAction({ exploreId, height, width });
|
return changeSizeAction({ exploreId, height, width });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ChangeGraphStylePayload {
|
||||||
|
exploreId: ExploreId;
|
||||||
|
graphStyle: ExploreGraphStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeGraphStyleAction = createAction<ChangeGraphStylePayload>('explore/changeGraphStyle');
|
||||||
|
|
||||||
|
export function changeGraphStyle(exploreId: ExploreId, graphStyle: ExploreGraphStyle): ThunkResult<void> {
|
||||||
|
return async (dispatch, getState) => {
|
||||||
|
storeGraphStyle(graphStyle);
|
||||||
|
dispatch(changeGraphStyleAction({ exploreId, graphStyle }));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize Explore state with state from the URL and the React component.
|
* Initialize Explore state with state from the URL and the React component.
|
||||||
* Call this only on components for with the Explore state has not been initialized.
|
* Call this only on components for with the Explore state has not been initialized.
|
||||||
@@ -200,6 +216,11 @@ export const paneReducer = (state: ExploreItemState = makeExplorePaneState(), ac
|
|||||||
return { ...state, containerWidth };
|
return { ...state, containerWidth };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (changeGraphStyleAction.match(action)) {
|
||||||
|
const { graphStyle } = action.payload;
|
||||||
|
return { ...state, graphStyle };
|
||||||
|
}
|
||||||
|
|
||||||
if (initializeExploreAction.match(action)) {
|
if (initializeExploreAction.match(action)) {
|
||||||
const { containerWidth, eventBridge, queries, range, originPanelId, datasourceInstance, history } = action.payload;
|
const { containerWidth, eventBridge, queries, range, originPanelId, datasourceInstance, history } = action.payload;
|
||||||
|
|
||||||
|
@@ -12,7 +12,12 @@ import {
|
|||||||
import { ExploreItemState } from 'app/types/explore';
|
import { ExploreItemState } from 'app/types/explore';
|
||||||
import { getDatasourceSrv } from '../../plugins/datasource_srv';
|
import { getDatasourceSrv } from '../../plugins/datasource_srv';
|
||||||
import store from '../../../core/store';
|
import store from '../../../core/store';
|
||||||
import { clearQueryKeys, lastUsedDatasourceKeyForOrgId } from '../../../core/utils/explore';
|
import {
|
||||||
|
clearQueryKeys,
|
||||||
|
ExploreGraphStyle,
|
||||||
|
lastUsedDatasourceKeyForOrgId,
|
||||||
|
toGraphStyle,
|
||||||
|
} from '../../../core/utils/explore';
|
||||||
import { toRawTimeRange } from '../utils/time';
|
import { toRawTimeRange } from '../utils/time';
|
||||||
|
|
||||||
export const DEFAULT_RANGE = {
|
export const DEFAULT_RANGE = {
|
||||||
@@ -20,6 +25,16 @@ export const DEFAULT_RANGE = {
|
|||||||
to: 'now',
|
to: 'now',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const GRAPH_STYLE_KEY = 'grafana.explore.style.graph';
|
||||||
|
export const storeGraphStyle = (graphStyle: string): void => {
|
||||||
|
store.set(GRAPH_STYLE_KEY, graphStyle);
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadGraphStyle = (): ExploreGraphStyle => {
|
||||||
|
const data = store.get(GRAPH_STYLE_KEY);
|
||||||
|
return toGraphStyle(data);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a fresh Explore area state
|
* Returns a fresh Explore area state
|
||||||
*/
|
*/
|
||||||
@@ -52,6 +67,7 @@ export const makeExplorePaneState = (): ExploreItemState => ({
|
|||||||
cache: [],
|
cache: [],
|
||||||
logsVolumeDataProvider: undefined,
|
logsVolumeDataProvider: undefined,
|
||||||
logsVolumeData: undefined,
|
logsVolumeData: undefined,
|
||||||
|
graphStyle: loadGraphStyle(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const createEmptyQueryResponse = (): PanelData => ({
|
export const createEmptyQueryResponse = (): PanelData => ({
|
||||||
|
@@ -14,6 +14,7 @@ import {
|
|||||||
EventBusExtended,
|
EventBusExtended,
|
||||||
DataQueryResponse,
|
DataQueryResponse,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
|
import { ExploreGraphStyle } from 'app/core/utils/explore';
|
||||||
|
|
||||||
export enum ExploreId {
|
export enum ExploreId {
|
||||||
left = 'left',
|
left = 'left',
|
||||||
@@ -167,6 +168,9 @@ export interface ExploreItemState {
|
|||||||
logsVolumeDataProvider?: Observable<DataQueryResponse>;
|
logsVolumeDataProvider?: Observable<DataQueryResponse>;
|
||||||
logsVolumeDataSubscription?: SubscriptionLike;
|
logsVolumeDataSubscription?: SubscriptionLike;
|
||||||
logsVolumeData?: DataQueryResponse;
|
logsVolumeData?: DataQueryResponse;
|
||||||
|
|
||||||
|
/* explore graph style */
|
||||||
|
graphStyle: ExploreGraphStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExploreUpdateState {
|
export interface ExploreUpdateState {
|
||||||
|
Reference in New Issue
Block a user