Explore: Refactor graph component to use PanelRenderer (#38914)

* Move SplitOpenFn type to grafana-data

* Use panel renderer instead of Timeseries panel for graph in Explore

* rename splitopen props on panel context
This commit is contained in:
Zoltán Bedi 2021-09-10 18:18:22 +02:00 committed by GitHub
parent ddd110d0b2
commit 94f1173824
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 67 additions and 75 deletions

View File

@ -1,4 +1,5 @@
import { RawTimeRange } from './time';
import { DataQuery } from './query';
import { RawTimeRange, TimeRange } from './time';
/** @internal */
export interface ExploreUrlState {
@ -8,3 +9,10 @@ export interface ExploreUrlState {
originPanelId?: number;
context?: string;
}
/**
* SplitOpen type is used in Explore and related components.
*/
export type SplitOpen = <T extends DataQuery = any>(
options?: { datasourceUid: string; query: T; range?: TimeRange } | undefined
) => void;

View File

@ -1,4 +1,4 @@
import { EventBusSrv, EventBus, DashboardCursorSync, AnnotationEventUIModel } from '@grafana/data';
import { AnnotationEventUIModel, DashboardCursorSync, EventBus, EventBusSrv, SplitOpen } from '@grafana/data';
import React from 'react';
import { SeriesVisibilityChangeMode } from '.';
@ -22,6 +22,11 @@ export interface PanelContext {
onAnnotationCreate?: (annotation: AnnotationEventUIModel) => void;
onAnnotationUpdate?: (annotation: AnnotationEventUIModel) => void;
onAnnotationDelete?: (id: string) => void;
/**
* onSplitOpen is used in Explore to open the split view. It can be used in panels which has intercations and used in Explore as well.
* For example TimeSeries panel.
*/
onSplitOpen?: SplitOpen;
}
export const PanelContextRoot = React.createContext<PanelContext>({

View File

@ -5,7 +5,6 @@ import { connect, ConnectedProps } from 'react-redux';
import AutoSizer from 'react-virtualized-auto-sizer';
import memoizeOne from 'memoize-one';
import { selectors } from '@grafana/e2e-selectors';
import { TooltipDisplayMode } from '@grafana/schema';
import { ErrorBoundaryAlert, CustomScrollbar, Collapse, withTheme2, Themeable2 } from '@grafana/ui';
import { AbsoluteTimeRange, DataQuery, LoadingState, RawTimeRange, DataFrame, GrafanaTheme2 } from '@grafana/data';
@ -25,10 +24,10 @@ import { NoDataSourceCallToAction } from './NoDataSourceCallToAction';
import { getTimeZone } from '../profile/state/selectors';
import { SecondaryActions } from './SecondaryActions';
import { FILTER_FOR_OPERATOR, FILTER_OUT_OPERATOR, FilterItem } from '@grafana/ui/src/components/Table/types';
import { ExploreGraphNGPanel } from './ExploreGraphNGPanel';
import { NodeGraphContainer } from './NodeGraphContainer';
import { ResponseErrorContainer } from './ResponseErrorContainer';
import { TraceViewContainer } from './TraceView/TraceViewContainer';
import { ExploreGraph } from './ExploreGraph';
const getStyles = (theme: GrafanaTheme2) => {
return {
@ -191,16 +190,15 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
const spacing = parseInt(theme.spacing(2).slice(0, -2), 10);
return (
<Collapse label="Graph" loading={loading} isOpen>
<ExploreGraphNGPanel
<ExploreGraph
data={graphResult!}
height={400}
width={width - spacing}
tooltipDisplayMode={TooltipDisplayMode.Single}
absoluteRange={absoluteRange}
timeZone={timeZone}
onUpdateTimeRange={this.onUpdateTimeRange}
annotations={queryResponse.annotations}
splitOpenFn={splitOpen}
loadingState={queryResponse.state}
/>
</Collapse>
);
@ -219,11 +217,12 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
}
renderLogsPanel(width: number) {
const { exploreId, syncedTimes, theme } = this.props;
const { exploreId, syncedTimes, theme, queryResponse } = this.props;
const spacing = parseInt(theme.spacing(2).slice(0, -2), 10);
return (
<LogsContainer
exploreId={exploreId}
loadingState={queryResponse.state}
syncedTimes={syncedTimes}
width={width - spacing}
onClickFilterLabel={this.onClickFilterLabel}

View File

@ -1,3 +1,4 @@
import { css, cx } from '@emotion/css';
import {
AbsoluteTimeRange,
applyFieldOverrides,
@ -6,36 +7,31 @@ import {
createFieldConfigRegistry,
DataFrame,
dateTime,
Field,
FieldColorModeId,
FieldConfigSource,
getFrameDisplayName,
GrafanaTheme2,
LoadingState,
SplitOpen,
TimeZone,
} from '@grafana/data';
import { PanelRenderer } from '@grafana/runtime';
import { GraphDrawStyle, LegendDisplayMode, TooltipDisplayMode } from '@grafana/schema';
import {
Icon,
PanelContext,
PanelContextProvider,
SeriesVisibilityChangeMode,
TimeSeries,
TooltipPlugin,
useStyles2,
useTheme2,
ZoomPlugin,
} from '@grafana/ui';
import { LegendDisplayMode, TooltipDisplayMode, GraphDrawStyle } from '@grafana/schema';
import { defaultGraphConfig, getGraphFieldConfig } from 'app/plugins/panel/timeseries/config';
import { ContextMenuPlugin } from 'app/plugins/panel/timeseries/plugins/ContextMenuPlugin';
import { ExemplarsPlugin } from 'app/plugins/panel/timeseries/plugins/ExemplarsPlugin';
import { css, cx } from '@emotion/css';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { getFieldLinksForExplore } from './utils/links';
import { usePrevious } from 'react-use';
import appEvents from 'app/core/app_events';
import { seriesVisibilityConfigFactory } from '../dashboard/dashgrid/SeriesVisibilityConfigFactory';
import { defaultGraphConfig, getGraphFieldConfig } from 'app/plugins/panel/timeseries/config';
import { TimeSeriesOptions } from 'app/plugins/panel/timeseries/types';
import { identity } from 'lodash';
import { SplitOpen } from 'app/types/explore';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { usePrevious } from 'react-use';
import { seriesVisibilityConfigFactory } from '../dashboard/dashgrid/SeriesVisibilityConfigFactory';
const MAX_NUMBER_OF_TIME_SERIES = 20;
@ -43,26 +39,26 @@ interface Props {
data: DataFrame[];
height: number;
width: number;
annotations?: DataFrame[];
absoluteRange: AbsoluteTimeRange;
timeZone: TimeZone;
onUpdateTimeRange: (absoluteRange: AbsoluteTimeRange) => void;
loadingState: LoadingState;
annotations?: DataFrame[];
onHiddenSeriesChanged?: (hiddenSeries: string[]) => void;
tooltipDisplayMode: TooltipDisplayMode;
tooltipDisplayMode?: TooltipDisplayMode;
splitOpenFn?: SplitOpen;
}
export function ExploreGraphNGPanel({
export function ExploreGraph({
data,
height,
width,
timeZone,
absoluteRange,
onUpdateTimeRange,
loadingState,
annotations,
tooltipDisplayMode,
splitOpenFn,
onHiddenSeriesChanged,
splitOpenFn,
tooltipDisplayMode = TooltipDisplayMode.Single,
}: Props) {
const theme = useTheme2();
const [showAllTimeSeries, setShowAllTimeSeries] = useState(false);
@ -128,12 +124,9 @@ export function ExploreGraphNGPanel({
const seriesToShow = showAllTimeSeries ? dataWithConfig : dataWithConfig.slice(0, MAX_NUMBER_OF_TIME_SERIES);
const getFieldLinks = (field: Field, rowIndex: number) => {
return getFieldLinksForExplore({ field, rowIndex, splitOpenFn, range: timeRange });
};
const panelContext: PanelContext = {
eventBus: appEvents,
onSplitOpen: splitOpenFn,
onToggleSeriesVisibility(label: string, mode: SeriesVisibilityChangeMode) {
setBaseStructureRev((r) => r + 1);
setFieldConfig(seriesVisibilityConfigFactory(label, mode, fieldConfig, data));
@ -155,33 +148,19 @@ export function ExploreGraphNGPanel({
>{`Show all ${dataWithConfig.length}`}</span>
</div>
)}
<TimeSeries
frames={seriesToShow}
structureRev={structureRev}
<PanelRenderer
data={{ series: seriesToShow, timeRange, structureRev, state: loadingState, annotations }}
pluginId="timeseries"
title=""
width={width}
height={height}
timeRange={timeRange}
legend={{ displayMode: LegendDisplayMode.List, placement: 'bottom', calcs: [] }}
timeZone={timeZone}
>
{(config, alignedDataFrame) => {
return (
<>
<ZoomPlugin config={config} onZoom={onUpdateTimeRange} />
<TooltipPlugin config={config} data={alignedDataFrame} mode={tooltipDisplayMode} timeZone={timeZone} />
<ContextMenuPlugin config={config} data={alignedDataFrame} timeZone={timeZone} />
{annotations && (
<ExemplarsPlugin
config={config}
exemplars={annotations}
timeZone={timeZone}
getFieldLinks={getFieldLinks}
/>
)}
</>
);
}}
</TimeSeries>
options={
{
tooltip: { mode: tooltipDisplayMode },
legend: { displayMode: LegendDisplayMode.List, placement: 'bottom', calcs: [] },
} as TimeSeriesOptions
}
/>
</PanelContextProvider>
);
}

View File

@ -19,6 +19,7 @@ import {
DataQuery,
DataFrame,
GrafanaTheme2,
LoadingState,
} from '@grafana/data';
import {
RadioButtonGroup,
@ -35,7 +36,7 @@ import { dedupLogRows, filterLogLevels } from 'app/core/logs_model';
import { LogsMetaRow } from './LogsMetaRow';
import LogsNavigation from './LogsNavigation';
import { RowContextOptions } from '@grafana/ui/src/components/Logs/LogRowContextProvider';
import { ExploreGraphNGPanel } from './ExploreGraphNGPanel';
import { ExploreGraph } from './ExploreGraph';
const SETTINGS_KEYS = {
showLabels: 'grafana.explore.logs.showLabels',
@ -54,6 +55,7 @@ interface Props extends Themeable2 {
theme: GrafanaTheme2;
highlighterExpressions?: string[];
loading: boolean;
loadingState: LoadingState;
absoluteRange: AbsoluteTimeRange;
timeZone: TimeZone;
scanning?: boolean;
@ -254,6 +256,7 @@ export class UnthemedLogs extends PureComponent<Props, State> {
visibleRange,
highlighterExpressions,
loading = false,
loadingState,
onClickFilterLabel,
onClickFilterOutLabel,
timeZone,
@ -297,14 +300,14 @@ export class UnthemedLogs extends PureComponent<Props, State> {
This datasource does not support full-range histograms. The graph is based on the logs seen in the response.
</div>
{logsSeries && logsSeries.length ? (
<ExploreGraphNGPanel
<ExploreGraph
data={logsSeries}
height={150}
width={width}
tooltipDisplayMode={TooltipDisplayMode.Multi}
absoluteRange={visibleRange || absoluteRange}
timeZone={timeZone}
onUpdateTimeRange={onChangeTime}
loadingState={loadingState}
onHiddenSeriesChanged={this.onToggleLogLevel}
/>
) : undefined}

View File

@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
import { connect, ConnectedProps } from 'react-redux';
import { css } from 'emotion';
import { Collapse } from '@grafana/ui';
import { AbsoluteTimeRange, Field, LogRowModel, RawTimeRange } from '@grafana/data';
import { AbsoluteTimeRange, Field, LoadingState, LogRowModel, RawTimeRange } from '@grafana/data';
import { ExploreId, ExploreItemState } from 'app/types/explore';
import { StoreState } from 'app/types';
import { splitOpen } from './state/main';
@ -20,6 +20,7 @@ interface LogsContainerProps extends PropsFromRedux {
exploreId: ExploreId;
scanRange?: RawTimeRange;
syncedTimes: boolean;
loadingState: LoadingState;
onClickFilterLabel?: (key: string, value: string) => void;
onClickFilterOutLabel?: (key: string, value: string) => void;
onStartScanning: () => void;
@ -60,6 +61,7 @@ export class LogsContainer extends PureComponent<LogsContainerProps> {
render() {
const {
loading,
loadingState,
logsHighlighterExpressions,
logRows,
logsMeta,
@ -123,6 +125,7 @@ export class LogsContainer extends PureComponent<LogsContainerProps> {
width={width}
highlighterExpressions={logsHighlighterExpressions}
loading={loading}
loadingState={loadingState}
onChangeTime={this.onChangeTime}
onClickFilterLabel={onClickFilterLabel}
onClickFilterOutLabel={onClickFilterOutLabel}

View File

@ -1,4 +1,4 @@
import { DataFrame, DataFrameView, TraceSpanRow } from '@grafana/data';
import { DataFrame, DataFrameView, SplitOpen, TraceSpanRow } from '@grafana/data';
import { colors, useTheme } from '@grafana/ui';
import {
ThemeOptions,
@ -17,7 +17,7 @@ import { TraceToLogsData } from 'app/core/components/TraceToLogsSettings';
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { getTimeZone } from 'app/features/profile/state/selectors';
import { StoreState } from 'app/types';
import { ExploreId, SplitOpen } from 'app/types/explore';
import { ExploreId } from 'app/types/explore';
import React, { useCallback, useMemo, useState } from 'react';
import { useSelector } from 'react-redux';
import { createSpanLinkFactory } from './createSpanLink';

View File

@ -1,8 +1,8 @@
import React from 'react';
import { Collapse } from '@grafana/ui';
import { DataFrame } from '@grafana/data';
import { DataFrame, SplitOpen } from '@grafana/data';
import { TraceView } from './TraceView';
import { ExploreId, SplitOpen } from '../../../types';
import { ExploreId } from 'app/types/explore';
interface Props {
dataFrames: DataFrame[];

View File

@ -1,10 +1,9 @@
import { DataLink, dateTime, Field, mapInternalLinkToExplore, rangeUtil, TimeRange } from '@grafana/data';
import { DataLink, dateTime, Field, mapInternalLinkToExplore, rangeUtil, SplitOpen, TimeRange } from '@grafana/data';
import { getTemplateSrv } from '@grafana/runtime';
import { Icon } from '@grafana/ui';
import { TraceSpan } from '@jaegertracing/jaeger-ui-components';
import { TraceToLogsOptions } from 'app/core/components/TraceToLogsSettings';
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { SplitOpen } from 'app/types/explore';
import React from 'react';
import { LokiQuery } from '../../../plugins/datasource/loki/types';

View File

@ -8,9 +8,9 @@ import {
ScopedVars,
DataFrame,
getFieldDisplayValuesProxy,
SplitOpen,
} from '@grafana/data';
import { getTemplateSrv } from '@grafana/runtime';
import { SplitOpen } from 'app/types/explore';
import { getLinkSrv } from '../../panel/panellinks/link_srv';
import { contextSrv } from 'app/core/services/context_srv';

View File

@ -23,10 +23,10 @@ export const TimeSeriesPanel: React.FC<TimeSeriesPanelProps> = ({
onChangeTimeRange,
replaceVariables,
}) => {
const { sync, canAddAnnotations } = usePanelContext();
const { sync, canAddAnnotations, onSplitOpen } = usePanelContext();
const getFieldLinks = (field: Field, rowIndex: number) => {
return getFieldLinksForExplore({ field, rowIndex, range: timeRange });
return getFieldLinksForExplore({ field, rowIndex, splitOpenFn: onSplitOpen, range: timeRange });
};
const { frames, warn } = useMemo(() => prepareGraphableFields(data?.series, config.theme2), [data]);

View File

@ -204,7 +204,3 @@ export interface ExplorePanelData extends PanelData {
tableResult: DataFrame | null;
logsResult: LogsModel | null;
}
export type SplitOpen = <T extends DataQuery = any>(
options?: { datasourceUid: string; query: T; range?: TimeRange } | undefined
) => void;