mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: Pass the dataframes along with the time range of the data (#90594)
* Explore: Pass the dataframes along with the time range of the data (#90259)
* Use data time range for rendering instead of selected range from the picker
* Update CustomContainer to use data time range
* Update test
(cherry picked from commit 58285e37a2
)
* Memoize visible range passed to LogsVolumePanel
* Ensure onHiddenSeriesChanged is called only when hidden frames change
This commit is contained in:
parent
6a1e835f12
commit
8d98c9eba7
@ -24,10 +24,6 @@ export const VizWrapper = ({ data, thresholds, thresholdsType }: Props) => {
|
|||||||
const isTimeSeriesData = isTimeSeriesFrames(data.series);
|
const isTimeSeriesData = isTimeSeriesFrames(data.series);
|
||||||
const statusMessage = getStatusMessage(data);
|
const statusMessage = getStatusMessage(data);
|
||||||
const thresholdsStyle = thresholdsType ? { mode: thresholdsType } : undefined;
|
const thresholdsStyle = thresholdsType ? { mode: thresholdsType } : undefined;
|
||||||
const timeRange = {
|
|
||||||
from: data.timeRange.from.valueOf(),
|
|
||||||
to: data.timeRange.to.valueOf(),
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
@ -41,7 +37,7 @@ export const VizWrapper = ({ data, thresholds, thresholdsType }: Props) => {
|
|||||||
eventBus={appEvents}
|
eventBus={appEvents}
|
||||||
height={300}
|
height={300}
|
||||||
width={width}
|
width={width}
|
||||||
absoluteRange={timeRange}
|
timeRange={data.timeRange}
|
||||||
timeZone="browser"
|
timeZone="browser"
|
||||||
onChangeTime={() => {}}
|
onChangeTime={() => {}}
|
||||||
splitOpenFn={() => {}}
|
splitOpenFn={() => {}}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import { useMemo } from 'react';
|
import { DataFrame, EventBus, LoadingState, SplitOpen, TimeRange } from '@grafana/data';
|
||||||
|
|
||||||
import { AbsoluteTimeRange, DataFrame, dateTime, EventBus, LoadingState, SplitOpen } from '@grafana/data';
|
|
||||||
import { PanelRenderer } from '@grafana/runtime';
|
import { PanelRenderer } from '@grafana/runtime';
|
||||||
import { PanelChrome, PanelContext, PanelContextProvider } from '@grafana/ui';
|
import { PanelChrome, PanelContext, PanelContextProvider } from '@grafana/ui';
|
||||||
|
|
||||||
@ -14,7 +12,7 @@ export interface Props {
|
|||||||
timeZone: string;
|
timeZone: string;
|
||||||
pluginId: string;
|
pluginId: string;
|
||||||
frames: DataFrame[];
|
frames: DataFrame[];
|
||||||
absoluteRange: AbsoluteTimeRange;
|
timeRange: TimeRange;
|
||||||
state: LoadingState;
|
state: LoadingState;
|
||||||
splitOpenFn: SplitOpen;
|
splitOpenFn: SplitOpen;
|
||||||
eventBus: EventBus;
|
eventBus: EventBus;
|
||||||
@ -27,22 +25,10 @@ export function CustomContainer({
|
|||||||
state,
|
state,
|
||||||
pluginId,
|
pluginId,
|
||||||
frames,
|
frames,
|
||||||
absoluteRange,
|
timeRange,
|
||||||
splitOpenFn,
|
splitOpenFn,
|
||||||
eventBus,
|
eventBus,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const timeRange = useMemo(
|
|
||||||
() => ({
|
|
||||||
from: dateTime(absoluteRange.from),
|
|
||||||
to: dateTime(absoluteRange.to),
|
|
||||||
raw: {
|
|
||||||
from: dateTime(absoluteRange.from),
|
|
||||||
to: dateTime(absoluteRange.to),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
[absoluteRange.from, absoluteRange.to]
|
|
||||||
);
|
|
||||||
|
|
||||||
const plugin = getPanelPluginMeta(pluginId);
|
const plugin = getPanelPluginMeta(pluginId);
|
||||||
|
|
||||||
const dataLinkPostProcessor = useExploreDataLinkPostProcessor(splitOpenFn, timeRange);
|
const dataLinkPostProcessor = useExploreDataLinkPostProcessor(splitOpenFn, timeRange);
|
||||||
|
@ -83,10 +83,6 @@ const dummyProps: Props = {
|
|||||||
syncedTimes: false,
|
syncedTimes: false,
|
||||||
updateTimeRange: jest.fn(),
|
updateTimeRange: jest.fn(),
|
||||||
graphResult: [],
|
graphResult: [],
|
||||||
absoluteRange: {
|
|
||||||
from: 0,
|
|
||||||
to: 0,
|
|
||||||
},
|
|
||||||
timeZone: 'UTC',
|
timeZone: 'UTC',
|
||||||
queryResponse: makeEmptyQueryResponse(LoadingState.NotStarted),
|
queryResponse: makeEmptyQueryResponse(LoadingState.NotStarted),
|
||||||
addQueryRow: jest.fn(),
|
addQueryRow: jest.fn(),
|
||||||
|
@ -336,7 +336,7 @@ export class Explore extends PureComponent<Props, ExploreState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderCustom(width: number) {
|
renderCustom(width: number) {
|
||||||
const { timeZone, queryResponse, absoluteRange, eventBus } = this.props;
|
const { timeZone, queryResponse, eventBus } = this.props;
|
||||||
|
|
||||||
const groupedByPlugin = groupBy(queryResponse?.customFrames, 'meta.preferredVisualisationPluginId');
|
const groupedByPlugin = groupBy(queryResponse?.customFrames, 'meta.preferredVisualisationPluginId');
|
||||||
|
|
||||||
@ -349,7 +349,7 @@ export class Explore extends PureComponent<Props, ExploreState> {
|
|||||||
pluginId={pluginId}
|
pluginId={pluginId}
|
||||||
frames={frames}
|
frames={frames}
|
||||||
state={queryResponse.state}
|
state={queryResponse.state}
|
||||||
absoluteRange={absoluteRange}
|
timeRange={queryResponse.timeRange}
|
||||||
height={400}
|
height={400}
|
||||||
width={width}
|
width={width}
|
||||||
splitOpenFn={this.onSplitOpen(pluginId)}
|
splitOpenFn={this.onSplitOpen(pluginId)}
|
||||||
@ -361,7 +361,7 @@ export class Explore extends PureComponent<Props, ExploreState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderGraphPanel(width: number) {
|
renderGraphPanel(width: number) {
|
||||||
const { graphResult, absoluteRange, timeZone, queryResponse, showFlameGraph } = this.props;
|
const { graphResult, timeZone, queryResponse, showFlameGraph } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContentOutlineItem panelId="Graph" title="Graph" icon="graph-bar">
|
<ContentOutlineItem panelId="Graph" title="Graph" icon="graph-bar">
|
||||||
@ -369,7 +369,7 @@ export class Explore extends PureComponent<Props, ExploreState> {
|
|||||||
data={graphResult!}
|
data={graphResult!}
|
||||||
height={showFlameGraph ? 180 : 400}
|
height={showFlameGraph ? 180 : 400}
|
||||||
width={width}
|
width={width}
|
||||||
absoluteRange={absoluteRange}
|
timeRange={queryResponse.timeRange}
|
||||||
timeZone={timeZone}
|
timeZone={timeZone}
|
||||||
onChangeTime={this.onUpdateTimeRange}
|
onChangeTime={this.onUpdateTimeRange}
|
||||||
annotations={queryResponse.annotations}
|
annotations={queryResponse.annotations}
|
||||||
@ -676,7 +676,6 @@ function mapStateToProps(state: StoreState, { exploreId }: ExploreProps) {
|
|||||||
showTable,
|
showTable,
|
||||||
showTrace,
|
showTrace,
|
||||||
showCustom,
|
showCustom,
|
||||||
absoluteRange,
|
|
||||||
queryResponse,
|
queryResponse,
|
||||||
showNodeGraph,
|
showNodeGraph,
|
||||||
showFlameGraph,
|
showFlameGraph,
|
||||||
@ -697,7 +696,6 @@ function mapStateToProps(state: StoreState, { exploreId }: ExploreProps) {
|
|||||||
isLive,
|
isLive,
|
||||||
graphResult,
|
graphResult,
|
||||||
logsResult: logsResult ?? undefined,
|
logsResult: logsResult ?? undefined,
|
||||||
absoluteRange,
|
|
||||||
queryResponse,
|
queryResponse,
|
||||||
syncedTimes,
|
syncedTimes,
|
||||||
timeZone,
|
timeZone,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { identity } from 'lodash';
|
import { identity, isEqual, sortBy } from 'lodash';
|
||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { usePrevious } from 'react-use';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AbsoluteTimeRange,
|
AbsoluteTimeRange,
|
||||||
@ -9,7 +8,6 @@ import {
|
|||||||
createFieldConfigRegistry,
|
createFieldConfigRegistry,
|
||||||
DashboardCursorSync,
|
DashboardCursorSync,
|
||||||
DataFrame,
|
DataFrame,
|
||||||
dateTime,
|
|
||||||
EventBus,
|
EventBus,
|
||||||
FieldColorModeId,
|
FieldColorModeId,
|
||||||
FieldConfigSource,
|
FieldConfigSource,
|
||||||
@ -17,6 +15,7 @@ import {
|
|||||||
LoadingState,
|
LoadingState,
|
||||||
SplitOpen,
|
SplitOpen,
|
||||||
ThresholdsConfig,
|
ThresholdsConfig,
|
||||||
|
TimeRange,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { PanelRenderer } from '@grafana/runtime';
|
import { PanelRenderer } from '@grafana/runtime';
|
||||||
import {
|
import {
|
||||||
@ -44,7 +43,7 @@ interface Props {
|
|||||||
data: DataFrame[];
|
data: DataFrame[];
|
||||||
height: number;
|
height: number;
|
||||||
width: number;
|
width: number;
|
||||||
absoluteRange: AbsoluteTimeRange;
|
timeRange: TimeRange;
|
||||||
timeZone: TimeZone;
|
timeZone: TimeZone;
|
||||||
loadingState: LoadingState;
|
loadingState: LoadingState;
|
||||||
annotations?: DataFrame[];
|
annotations?: DataFrame[];
|
||||||
@ -67,7 +66,7 @@ export function ExploreGraph({
|
|||||||
height,
|
height,
|
||||||
width,
|
width,
|
||||||
timeZone,
|
timeZone,
|
||||||
absoluteRange,
|
timeRange,
|
||||||
onChangeTime,
|
onChangeTime,
|
||||||
loadingState,
|
loadingState,
|
||||||
annotations,
|
annotations,
|
||||||
@ -84,19 +83,6 @@ export function ExploreGraph({
|
|||||||
toggleLegendRef,
|
toggleLegendRef,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const theme = useTheme2();
|
const theme = useTheme2();
|
||||||
const previousTimeRange = usePrevious(absoluteRange);
|
|
||||||
const baseTimeRange = loadingState === LoadingState.Loading && previousTimeRange ? previousTimeRange : absoluteRange;
|
|
||||||
const timeRange = useMemo(
|
|
||||||
() => ({
|
|
||||||
from: dateTime(baseTimeRange.from),
|
|
||||||
to: dateTime(baseTimeRange.to),
|
|
||||||
raw: {
|
|
||||||
from: dateTime(baseTimeRange.from),
|
|
||||||
to: dateTime(baseTimeRange.to),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
[baseTimeRange.from, baseTimeRange.to]
|
|
||||||
);
|
|
||||||
|
|
||||||
const fieldConfigRegistry = useMemo(
|
const fieldConfigRegistry = useMemo(
|
||||||
() => createFieldConfigRegistry(getGraphFieldConfig(defaultGraphConfig), 'Explore'),
|
() => createFieldConfigRegistry(getGraphFieldConfig(defaultGraphConfig), 'Explore'),
|
||||||
@ -156,6 +142,7 @@ export function ExploreGraph({
|
|||||||
const structureRev = useStructureRev(dataWithConfig);
|
const structureRev = useStructureRev(dataWithConfig);
|
||||||
|
|
||||||
const onHiddenSeriesChangedRef = useRef(onHiddenSeriesChanged);
|
const onHiddenSeriesChangedRef = useRef(onHiddenSeriesChanged);
|
||||||
|
const previousHiddenFrames = useRef<string[] | undefined>(undefined);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (onHiddenSeriesChangedRef.current) {
|
if (onHiddenSeriesChangedRef.current) {
|
||||||
@ -166,7 +153,13 @@ export function ExploreGraph({
|
|||||||
hiddenFrames.push(getFrameDisplayName(frame));
|
hiddenFrames.push(getFrameDisplayName(frame));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
onHiddenSeriesChangedRef.current(hiddenFrames);
|
if (
|
||||||
|
previousHiddenFrames.current === undefined ||
|
||||||
|
!isEqual(sortBy(hiddenFrames), sortBy(previousHiddenFrames.current))
|
||||||
|
) {
|
||||||
|
previousHiddenFrames.current = hiddenFrames;
|
||||||
|
onHiddenSeriesChangedRef.current(hiddenFrames);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [dataWithConfig]);
|
}, [dataWithConfig]);
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
LoadingState,
|
LoadingState,
|
||||||
ThresholdsConfig,
|
ThresholdsConfig,
|
||||||
GrafanaTheme2,
|
GrafanaTheme2,
|
||||||
|
TimeRange,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import {
|
import {
|
||||||
GraphThresholdsStyleConfig,
|
GraphThresholdsStyleConfig,
|
||||||
@ -38,7 +39,7 @@ interface Props extends Pick<PanelChromeProps, 'statusMessage'> {
|
|||||||
data: DataFrame[];
|
data: DataFrame[];
|
||||||
annotations?: DataFrame[];
|
annotations?: DataFrame[];
|
||||||
eventBus: EventBus;
|
eventBus: EventBus;
|
||||||
absoluteRange: AbsoluteTimeRange;
|
timeRange: TimeRange;
|
||||||
timeZone: TimeZone;
|
timeZone: TimeZone;
|
||||||
onChangeTime: (absoluteRange: AbsoluteTimeRange) => void;
|
onChangeTime: (absoluteRange: AbsoluteTimeRange) => void;
|
||||||
splitOpenFn: SplitOpen;
|
splitOpenFn: SplitOpen;
|
||||||
@ -52,7 +53,7 @@ export const GraphContainer = ({
|
|||||||
eventBus,
|
eventBus,
|
||||||
height,
|
height,
|
||||||
width,
|
width,
|
||||||
absoluteRange,
|
timeRange,
|
||||||
timeZone,
|
timeZone,
|
||||||
annotations,
|
annotations,
|
||||||
onChangeTime,
|
onChangeTime,
|
||||||
@ -112,7 +113,7 @@ export const GraphContainer = ({
|
|||||||
data={slicedData}
|
data={slicedData}
|
||||||
height={innerHeight}
|
height={innerHeight}
|
||||||
width={innerWidth}
|
width={innerWidth}
|
||||||
absoluteRange={absoluteRange}
|
timeRange={timeRange}
|
||||||
onChangeTime={onChangeTime}
|
onChangeTime={onChangeTime}
|
||||||
timeZone={timeZone}
|
timeZone={timeZone}
|
||||||
annotations={annotations}
|
annotations={annotations}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
|
|
||||||
import { DataQueryResponse, LoadingState, EventBusSrv } from '@grafana/data';
|
import { DataQueryResponse, LoadingState, EventBusSrv, dateTime } from '@grafana/data';
|
||||||
|
|
||||||
import { LogsVolumePanel } from './LogsVolumePanel';
|
import { LogsVolumePanel } from './LogsVolumePanel';
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ jest.mock('../Graph/ExploreGraph', () => {
|
|||||||
function renderPanel(logsVolumeData: DataQueryResponse) {
|
function renderPanel(logsVolumeData: DataQueryResponse) {
|
||||||
render(
|
render(
|
||||||
<LogsVolumePanel
|
<LogsVolumePanel
|
||||||
absoluteRange={{ from: 0, to: 1 }}
|
timeRange={{ from: dateTime(0), to: dateTime(1), raw: { from: dateTime(0), to: dateTime(1) } }}
|
||||||
timeZone="timeZone"
|
timeZone="timeZone"
|
||||||
splitOpen={() => {}}
|
splitOpen={() => {}}
|
||||||
width={100}
|
width={100}
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
EventBus,
|
EventBus,
|
||||||
GrafanaTheme2,
|
GrafanaTheme2,
|
||||||
DataFrame,
|
DataFrame,
|
||||||
|
TimeRange,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { TimeZone } from '@grafana/schema';
|
import { TimeZone } from '@grafana/schema';
|
||||||
import { Icon, SeriesVisibilityChangeMode, Tooltip, TooltipDisplayMode, useStyles2, useTheme2 } from '@grafana/ui';
|
import { Icon, SeriesVisibilityChangeMode, Tooltip, TooltipDisplayMode, useStyles2, useTheme2 } from '@grafana/ui';
|
||||||
@ -20,7 +21,7 @@ import { ExploreGraph } from '../Graph/ExploreGraph';
|
|||||||
type Props = {
|
type Props = {
|
||||||
logsVolumeData: DataQueryResponse;
|
logsVolumeData: DataQueryResponse;
|
||||||
allLogsVolumeMaximum: number;
|
allLogsVolumeMaximum: number;
|
||||||
absoluteRange: AbsoluteTimeRange;
|
timeRange: TimeRange;
|
||||||
timeZone: TimeZone;
|
timeZone: TimeZone;
|
||||||
splitOpen: SplitOpen;
|
splitOpen: SplitOpen;
|
||||||
width: number;
|
width: number;
|
||||||
@ -86,7 +87,7 @@ export function LogsVolumePanel(props: Props) {
|
|||||||
data={logsVolumeData.data}
|
data={logsVolumeData.data}
|
||||||
height={height}
|
height={height}
|
||||||
width={width - spacing * 2}
|
width={width - spacing * 2}
|
||||||
absoluteRange={props.absoluteRange}
|
timeRange={props.timeRange}
|
||||||
onChangeTime={onUpdateTimeRange}
|
onChangeTime={onUpdateTimeRange}
|
||||||
timeZone={timeZone}
|
timeZone={timeZone}
|
||||||
splitOpenFn={splitOpen}
|
splitOpenFn={splitOpen}
|
||||||
|
@ -8,10 +8,12 @@ import {
|
|||||||
DataFrame,
|
DataFrame,
|
||||||
DataQueryResponse,
|
DataQueryResponse,
|
||||||
DataTopic,
|
DataTopic,
|
||||||
|
dateTime,
|
||||||
EventBus,
|
EventBus,
|
||||||
GrafanaTheme2,
|
GrafanaTheme2,
|
||||||
LoadingState,
|
LoadingState,
|
||||||
SplitOpen,
|
SplitOpen,
|
||||||
|
TimeRange,
|
||||||
TimeZone,
|
TimeZone,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { Button, InlineField, Alert, useStyles2, SeriesVisibilityChangeMode } from '@grafana/ui';
|
import { Button, InlineField, Alert, useStyles2, SeriesVisibilityChangeMode } from '@grafana/ui';
|
||||||
@ -86,10 +88,9 @@ export const LogsVolumePanelList = ({
|
|||||||
|
|
||||||
const timeoutError = isTimeoutErrorResponse(logsVolumeData);
|
const timeoutError = isTimeoutErrorResponse(logsVolumeData);
|
||||||
|
|
||||||
const visibleRange = {
|
const from = dateTime(Math.max(absoluteRange.from, allLogsVolumeMaximumRange.from));
|
||||||
from: Math.max(absoluteRange.from, allLogsVolumeMaximumRange.from),
|
const to = dateTime(Math.min(absoluteRange.to, allLogsVolumeMaximumRange.to));
|
||||||
to: Math.min(absoluteRange.to, allLogsVolumeMaximumRange.to),
|
const visibleRange: TimeRange = { from, to, raw: { from, to } };
|
||||||
};
|
|
||||||
|
|
||||||
if (logsVolumeData?.state === LoadingState.Loading) {
|
if (logsVolumeData?.state === LoadingState.Loading) {
|
||||||
return <span>Loading...</span>;
|
return <span>Loading...</span>;
|
||||||
@ -126,7 +127,7 @@ export const LogsVolumePanelList = ({
|
|||||||
<LogsVolumePanel
|
<LogsVolumePanel
|
||||||
toggleLegendRef={toggleLegendRef}
|
toggleLegendRef={toggleLegendRef}
|
||||||
key={index}
|
key={index}
|
||||||
absoluteRange={visibleRange}
|
timeRange={visibleRange}
|
||||||
allLogsVolumeMaximum={allLogsVolumeMaximumValue}
|
allLogsVolumeMaximum={allLogsVolumeMaximumValue}
|
||||||
width={width}
|
width={width}
|
||||||
logsVolumeData={logsVolumeData}
|
logsVolumeData={logsVolumeData}
|
||||||
|
Loading…
Reference in New Issue
Block a user