Data Links: Allow passing data links post processor via context (#110590)
* Data Links: Allow passing data links post processor via context * Deprecate data links post processor in PanelContext * Mark new context as internal
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { isNumber, set, unset, get, cloneDeep } from 'lodash';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { createContext, useContext, useMemo, useRef } from 'react';
|
||||
import { usePrevious } from 'react-use';
|
||||
|
||||
import { ThresholdsMode, VariableFormatID } from '@grafana/schema';
|
||||
@@ -583,13 +583,14 @@ export function useFieldOverrides(
|
||||
data: PanelData | undefined,
|
||||
timeZone: string,
|
||||
theme: GrafanaTheme2,
|
||||
replace: InterpolateFunction,
|
||||
dataLinkPostProcessor?: DataLinkPostProcessor
|
||||
replace: InterpolateFunction
|
||||
): PanelData | undefined {
|
||||
const fieldConfigRegistry = plugin?.fieldConfigRegistry;
|
||||
const structureRev = useRef(0);
|
||||
const prevSeries = usePrevious(data?.series);
|
||||
|
||||
const { dataLinkPostProcessor } = useDataLinksContext();
|
||||
|
||||
return useMemo(() => {
|
||||
if (!fieldConfigRegistry || !fieldConfig || !data) {
|
||||
return;
|
||||
@@ -650,3 +651,15 @@ export function getFieldDataContextClone(frame: DataFrame, field: Field, fieldSc
|
||||
|
||||
return { value: { frame, field, data: [frame] } };
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const DataLinksContext = createContext<{
|
||||
dataLinkPostProcessor: DataLinkPostProcessor;
|
||||
}>({ dataLinkPostProcessor: defaultInternalLinkPostProcessor });
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const useDataLinksContext = () => useContext(DataLinksContext);
|
||||
|
||||
@@ -150,6 +150,8 @@ export {
|
||||
applyRawFieldOverrides,
|
||||
useFieldOverrides,
|
||||
getFieldDataContextClone,
|
||||
DataLinksContext,
|
||||
useDataLinksContext,
|
||||
} from './field/fieldOverrides';
|
||||
export { getFieldDisplayValuesProxy } from './field/getFieldDisplayValuesProxy';
|
||||
export {
|
||||
|
||||
@@ -96,6 +96,7 @@ export interface PanelContext {
|
||||
/**
|
||||
* Optional supplier for internal data links. If not provided a link pointing to Explore will be generated.
|
||||
* @internal
|
||||
* @deprecated Please use DataLinksContext instead. This property will be removed in next major.
|
||||
*/
|
||||
dataLinkPostProcessor?: DataLinkPostProcessor;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { DataFrame, EventBus, LoadingState, SplitOpen, TimeRange } from '@grafana/data';
|
||||
import { DataFrame, DataLinksContext, EventBus, LoadingState, SplitOpen, TimeRange } from '@grafana/data';
|
||||
import { PanelRenderer } from '@grafana/runtime';
|
||||
import { PanelChrome, PanelContext, PanelContextProvider } from '@grafana/ui';
|
||||
import { PanelChrome } from '@grafana/ui';
|
||||
|
||||
import { getPanelPluginMeta } from '../plugins/importPanelPlugin';
|
||||
|
||||
@@ -18,29 +18,13 @@ export interface Props {
|
||||
eventBus: EventBus;
|
||||
}
|
||||
|
||||
export function CustomContainer({
|
||||
width,
|
||||
height,
|
||||
timeZone,
|
||||
state,
|
||||
pluginId,
|
||||
frames,
|
||||
timeRange,
|
||||
splitOpenFn,
|
||||
eventBus,
|
||||
}: Props) {
|
||||
export function CustomContainer({ width, height, timeZone, state, pluginId, frames, timeRange, splitOpenFn }: Props) {
|
||||
const plugin = getPanelPluginMeta(pluginId);
|
||||
|
||||
const dataLinkPostProcessor = useExploreDataLinkPostProcessor(splitOpenFn, timeRange);
|
||||
|
||||
const panelContext: PanelContext = {
|
||||
dataLinkPostProcessor,
|
||||
eventBus,
|
||||
eventsScope: 'explore',
|
||||
};
|
||||
|
||||
return (
|
||||
<PanelContextProvider value={panelContext}>
|
||||
<DataLinksContext.Provider value={{ dataLinkPostProcessor }}>
|
||||
<PanelChrome title={plugin.name} width={width} height={height} loadingState={state}>
|
||||
{(innerWidth, innerHeight) => (
|
||||
<PanelRenderer
|
||||
@@ -53,6 +37,6 @@ export function CustomContainer({
|
||||
/>
|
||||
)}
|
||||
</PanelChrome>
|
||||
</PanelContextProvider>
|
||||
</DataLinksContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
createFieldConfigRegistry,
|
||||
DashboardCursorSync,
|
||||
DataFrame,
|
||||
DataLinksContext,
|
||||
EventBus,
|
||||
FieldColorModeId,
|
||||
FieldConfigSource,
|
||||
@@ -121,9 +122,8 @@ export function ExploreGraph({
|
||||
replaceVariables: (value) => value, // We don't need proper replace here as it is only used in getLinks and we use getFieldLinks
|
||||
theme,
|
||||
fieldConfigRegistry,
|
||||
dataLinkPostProcessor,
|
||||
});
|
||||
}, [fieldConfigRegistry, data, timeZone, theme, styledFieldConfig, dataLinkPostProcessor]);
|
||||
}, [fieldConfigRegistry, data, timeZone, theme, styledFieldConfig]);
|
||||
|
||||
const annotationsWithConfig = useMemo(() => {
|
||||
return applyFieldOverrides({
|
||||
@@ -171,7 +171,6 @@ export function ExploreGraph({
|
||||
onToggleSeriesVisibility(label: string, mode: SeriesVisibilityChangeMode) {
|
||||
setFieldConfig(seriesVisibilityConfigFactory(label, mode, fieldConfig, data));
|
||||
},
|
||||
dataLinkPostProcessor,
|
||||
};
|
||||
|
||||
function toggleLegend(name: string | undefined, mode: SeriesVisibilityChangeMode) {
|
||||
@@ -204,23 +203,25 @@ export function ExploreGraph({
|
||||
);
|
||||
|
||||
return (
|
||||
<PanelContextProvider value={panelContext}>
|
||||
<PanelRenderer
|
||||
data={{
|
||||
series: dataWithConfig,
|
||||
timeRange,
|
||||
state: loadingState,
|
||||
annotations: annotationsWithConfig,
|
||||
structureRev,
|
||||
}}
|
||||
pluginId="timeseries"
|
||||
title=""
|
||||
width={width}
|
||||
height={height}
|
||||
onChangeTimeRange={onChangeTime}
|
||||
timeZone={timeZone}
|
||||
options={panelOptions}
|
||||
/>
|
||||
</PanelContextProvider>
|
||||
<DataLinksContext.Provider value={{ dataLinkPostProcessor }}>
|
||||
<PanelContextProvider value={panelContext}>
|
||||
<PanelRenderer
|
||||
data={{
|
||||
series: dataWithConfig,
|
||||
timeRange,
|
||||
state: loadingState,
|
||||
annotations: annotationsWithConfig,
|
||||
structureRev,
|
||||
}}
|
||||
pluginId="timeseries"
|
||||
title=""
|
||||
width={width}
|
||||
height={height}
|
||||
onChangeTimeRange={onChangeTime}
|
||||
timeZone={timeZone}
|
||||
options={panelOptions}
|
||||
/>
|
||||
</PanelContextProvider>
|
||||
</DataLinksContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from '@grafana/data';
|
||||
import { Trans } from '@grafana/i18n';
|
||||
import { getTemplateSrv, PanelRendererProps } from '@grafana/runtime';
|
||||
import { ErrorBoundaryAlert, usePanelContext, useTheme2 } from '@grafana/ui';
|
||||
import { ErrorBoundaryAlert, useTheme2 } from '@grafana/ui';
|
||||
import { appEvents } from 'app/core/core';
|
||||
|
||||
import { importPanelPlugin, syncGetPanelPlugin } from '../../plugins/importPanelPlugin';
|
||||
@@ -39,16 +39,7 @@ export function PanelRenderer<P extends object = {}, F extends object = {}>(prop
|
||||
const [plugin, setPlugin] = useState(syncGetPanelPlugin(pluginId));
|
||||
const [error, setError] = useState<string | undefined>();
|
||||
const optionsWithDefaults = useOptionDefaults(plugin, options, fieldConfig);
|
||||
const { dataLinkPostProcessor } = usePanelContext();
|
||||
const dataWithOverrides = useFieldOverrides(
|
||||
plugin,
|
||||
optionsWithDefaults?.fieldConfig,
|
||||
data,
|
||||
timeZone,
|
||||
theme,
|
||||
replace,
|
||||
dataLinkPostProcessor
|
||||
);
|
||||
const dataWithOverrides = useFieldOverrides(plugin, optionsWithDefaults?.fieldConfig, data, timeZone, theme, replace);
|
||||
|
||||
useEffect(() => {
|
||||
// If we already have a plugin and it's correct one do nothing
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import uPlot from 'uplot';
|
||||
|
||||
import { Field, getDisplayProcessor, PanelProps } from '@grafana/data';
|
||||
import { Field, getDisplayProcessor, PanelProps, useDataLinksContext } from '@grafana/data';
|
||||
import { PanelDataErrorView } from '@grafana/runtime';
|
||||
import { DashboardCursorSync, TooltipDisplayMode } from '@grafana/schema';
|
||||
import {
|
||||
@@ -50,11 +50,12 @@ export const CandlestickPanel = ({
|
||||
onThresholdsChange,
|
||||
canEditThresholds,
|
||||
showThresholds,
|
||||
dataLinkPostProcessor,
|
||||
eventBus,
|
||||
canExecuteActions,
|
||||
} = usePanelContext();
|
||||
|
||||
const { dataLinkPostProcessor } = useDataLinksContext();
|
||||
|
||||
const userCanExecuteActions = useMemo(() => canExecuteActions?.() ?? false, [canExecuteActions]);
|
||||
|
||||
const theme = useTheme2();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
import { DashboardCursorSync, PanelProps } from '@grafana/data';
|
||||
import { DashboardCursorSync, PanelProps, useDataLinksContext } from '@grafana/data';
|
||||
import { PanelDataErrorView } from '@grafana/runtime';
|
||||
import {
|
||||
AxisPlacement,
|
||||
@@ -47,8 +47,9 @@ export const StateTimelinePanel = ({
|
||||
|
||||
// temp range set for adding new annotation set by TooltipPlugin2, consumed by AnnotationPlugin2
|
||||
const [newAnnotationRange, setNewAnnotationRange] = useState<TimeRange2 | null>(null);
|
||||
const { sync, eventsScope, canAddAnnotations, dataLinkPostProcessor, eventBus, canExecuteActions } =
|
||||
usePanelContext();
|
||||
const { sync, eventsScope, canAddAnnotations, eventBus, canExecuteActions } = usePanelContext();
|
||||
|
||||
const { dataLinkPostProcessor } = useDataLinksContext();
|
||||
|
||||
const userCanExecuteActions = useMemo(() => canExecuteActions?.() ?? false, [canExecuteActions]);
|
||||
const cursorSync = sync?.() ?? DashboardCursorSync.Off;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
import { DashboardCursorSync, PanelProps } from '@grafana/data';
|
||||
import { DashboardCursorSync, PanelProps, useDataLinksContext } from '@grafana/data';
|
||||
import { Trans } from '@grafana/i18n';
|
||||
import { PanelDataErrorView } from '@grafana/runtime';
|
||||
import {
|
||||
@@ -48,7 +48,8 @@ export const StatusHistoryPanel = ({
|
||||
|
||||
// temp range set for adding new annotation set by TooltipPlugin2, consumed by AnnotationPlugin2
|
||||
const [newAnnotationRange, setNewAnnotationRange] = useState<TimeRange2 | null>(null);
|
||||
const { sync, eventsScope, canAddAnnotations, dataLinkPostProcessor, eventBus } = usePanelContext();
|
||||
const { sync, eventsScope, canAddAnnotations, eventBus } = usePanelContext();
|
||||
const { dataLinkPostProcessor } = useDataLinksContext();
|
||||
const cursorSync = sync?.() ?? DashboardCursorSync.Off;
|
||||
|
||||
const enableAnnotationCreation = Boolean(canAddAnnotations && canAddAnnotations());
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
DataFrame,
|
||||
alignTimeRangeCompareData,
|
||||
shouldAlignTimeCompare,
|
||||
useDataLinksContext,
|
||||
FieldType,
|
||||
} from '@grafana/data';
|
||||
import { PanelDataErrorView } from '@grafana/runtime';
|
||||
@@ -46,11 +47,12 @@ export const TimeSeriesPanel = ({
|
||||
onThresholdsChange,
|
||||
canEditThresholds,
|
||||
showThresholds,
|
||||
dataLinkPostProcessor,
|
||||
eventBus,
|
||||
canExecuteActions,
|
||||
} = usePanelContext();
|
||||
|
||||
const { dataLinkPostProcessor } = useDataLinksContext();
|
||||
|
||||
const userCanExecuteActions = useMemo(() => canExecuteActions?.() ?? false, [canExecuteActions]);
|
||||
// Vertical orientation is not available for users through config.
|
||||
// It is simplified version of horizontal time series panel and it does not support all plugins.
|
||||
|
||||
@@ -8,9 +8,10 @@ import {
|
||||
FieldType,
|
||||
PanelProps,
|
||||
TimeRange,
|
||||
useDataLinksContext,
|
||||
} from '@grafana/data';
|
||||
import { config, PanelDataErrorView } from '@grafana/runtime';
|
||||
import { KeyboardPlugin, TooltipDisplayMode, usePanelContext, TooltipPlugin2 } from '@grafana/ui';
|
||||
import { KeyboardPlugin, TooltipDisplayMode, TooltipPlugin2 } from '@grafana/ui';
|
||||
import { TooltipHoverMode } from '@grafana/ui/internal';
|
||||
import { XYFieldMatchers } from 'app/core/components/GraphNG/types';
|
||||
import { preparePlotFrame } from 'app/core/components/GraphNG/utils';
|
||||
@@ -33,7 +34,7 @@ export const TrendPanel = ({
|
||||
replaceVariables,
|
||||
id,
|
||||
}: PanelProps<Options>) => {
|
||||
const { dataLinkPostProcessor } = usePanelContext();
|
||||
const { dataLinkPostProcessor } = useDataLinksContext();
|
||||
// Need to fallback to first number field if no xField is set in options otherwise panel crashes 😬
|
||||
const trendXFieldName =
|
||||
options.xField ?? data.series[0]?.fields.find((field) => field.type === FieldType.number)?.name;
|
||||
|
||||
Reference in New Issue
Block a user