import { AbsoluteTimeRange, applyFieldOverrides, compareArrayValues, compareDataFrameStructures, createFieldConfigRegistry, DataFrame, dateTime, Field, FieldColorModeId, FieldConfigSource, getFrameDisplayName, GrafanaTheme2, TimeZone, } from '@grafana/data'; 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 { identity } from 'lodash'; import { SplitOpen } from 'app/types/explore'; const MAX_NUMBER_OF_TIME_SERIES = 20; interface Props { data: DataFrame[]; height: number; width: number; annotations?: DataFrame[]; absoluteRange: AbsoluteTimeRange; timeZone: TimeZone; onUpdateTimeRange: (absoluteRange: AbsoluteTimeRange) => void; onHiddenSeriesChanged?: (hiddenSeries: string[]) => void; tooltipDisplayMode: TooltipDisplayMode; splitOpenFn?: SplitOpen; } export function ExploreGraphNGPanel({ data, height, width, timeZone, absoluteRange, onUpdateTimeRange, annotations, tooltipDisplayMode, splitOpenFn, onHiddenSeriesChanged, }: Props) { const theme = useTheme2(); const [showAllTimeSeries, setShowAllTimeSeries] = useState(false); const [baseStructureRev, setBaseStructureRev] = useState(1); const previousData = usePrevious(data); const structureChangesRef = useRef(0); if (data && previousData && !compareArrayValues(previousData, data, compareDataFrameStructures)) { structureChangesRef.current++; } const structureRev = baseStructureRev + structureChangesRef.current; const [fieldConfig, setFieldConfig] = useState({ defaults: { color: { mode: FieldColorModeId.PaletteClassic, }, custom: { drawStyle: GraphDrawStyle.Line, fillOpacity: 0, pointSize: 5, }, }, overrides: [], }); const style = useStyles2(getStyles); const timeRange = { from: dateTime(absoluteRange.from), to: dateTime(absoluteRange.to), raw: { from: dateTime(absoluteRange.from), to: dateTime(absoluteRange.to), }, }; const dataWithConfig = useMemo(() => { const registry = createFieldConfigRegistry(getGraphFieldConfig(defaultGraphConfig), 'Explore'); return applyFieldOverrides({ fieldConfig, data, timeZone, replaceVariables: (value) => value, // We don't need proper replace here as it is only used in getLinks and we use getFieldLinks theme, fieldConfigRegistry: registry, }); }, [fieldConfig, data, timeZone, theme]); useEffect(() => { if (onHiddenSeriesChanged) { const hiddenFrames: string[] = []; dataWithConfig.forEach((frame) => { const allFieldsHidden = frame.fields.map((field) => field.config?.custom?.hideFrom?.viz).every(identity); if (allFieldsHidden) { hiddenFrames.push(getFrameDisplayName(frame)); } }); onHiddenSeriesChanged(hiddenFrames); } }, [dataWithConfig, onHiddenSeriesChanged]); 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, onToggleSeriesVisibility(label: string, mode: SeriesVisibilityChangeMode) { setBaseStructureRev((r) => r + 1); setFieldConfig(seriesVisibilityConfigFactory(label, mode, fieldConfig, data)); }, }; return ( {dataWithConfig.length > MAX_NUMBER_OF_TIME_SERIES && !showAllTimeSeries && (
{`Showing only ${MAX_NUMBER_OF_TIME_SERIES} time series. `} { structureChangesRef.current++; setShowAllTimeSeries(true); }} >{`Show all ${dataWithConfig.length}`}
)} {(config, alignedDataFrame) => { return ( <> {annotations && ( )} ); }}
); } const getStyles = (theme: GrafanaTheme2) => ({ timeSeriesDisclaimer: css` label: time-series-disclaimer; width: 300px; margin: ${theme.spacing(1)} auto; padding: 10px 0; border-radius: ${theme.spacing(2)}; text-align: center; background-color: ${theme.colors.background.primary}; `, disclaimerIcon: css` label: disclaimer-icon; color: ${theme.colors.warning.main}; margin-right: ${theme.spacing(0.5)}; `, showAllTimeSeries: css` label: show-all-time-series; cursor: pointer; color: ${theme.colors.text.link}; `, });