FieldConfig: Add support for Actions (#92874)

Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
This commit is contained in:
Adela Almasan 2024-09-27 22:12:37 -06:00 committed by GitHub
parent 165ca3b4e8
commit f9f85131a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 153 additions and 42 deletions

View File

@ -1,3 +1,4 @@
import { Action } from '../../types/action';
import { Field } from '../../types/dataFrame'; import { Field } from '../../types/dataFrame';
import { DataLink } from '../../types/dataLink'; import { DataLink } from '../../types/dataLink';
import { FieldOverrideContext } from '../../types/fieldOverrides'; import { FieldOverrideContext } from '../../types/fieldOverrides';
@ -59,6 +60,15 @@ export const dataLinksOverrideProcessor = (
return value; return value;
}; };
export const actionsOverrideProcessor = (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: any,
_context: FieldOverrideContext,
_settings?: DataLinksFieldConfigSettings
): Action[] => {
return value;
};
export interface ValueMappingFieldConfigSettings {} export interface ValueMappingFieldConfigSettings {}
export const valueMappingsOverrideProcessor = ( export const valueMappingsOverrideProcessor = (

View File

@ -1,6 +1,7 @@
import { HideSeriesConfig } from '@grafana/schema'; import { HideSeriesConfig } from '@grafana/schema';
import { ScopedVars } from './ScopedVars'; import { ScopedVars } from './ScopedVars';
import { Action } from './action';
import { QueryResultBase, Labels, NullValueMode } from './data'; import { QueryResultBase, Labels, NullValueMode } from './data';
import { DataLink, LinkModel } from './dataLink'; import { DataLink, LinkModel } from './dataLink';
import { DecimalCount, DisplayProcessor, DisplayValue, DisplayValueAlignmentFactors } from './displayValue'; import { DecimalCount, DisplayProcessor, DisplayValue, DisplayValueAlignmentFactors } from './displayValue';
@ -98,6 +99,8 @@ export interface FieldConfig<TOptions = any> {
// The behavior when clicking on a result // The behavior when clicking on a result
links?: DataLink[]; links?: DataLink[];
actions?: Action[];
// Alternative to empty string // Alternative to empty string
noValue?: string; noValue?: string;

View File

@ -31,7 +31,7 @@ func TestList(t *testing.T) {
res, err := List(ctx, "items", nil, Pagination{Limit: 2}, func(ctx context.Context, ns claims.NamespaceInfo, p Pagination) (*ListResponse[item], error) { res, err := List(ctx, "items", nil, Pagination{Limit: 2}, func(ctx context.Context, ns claims.NamespaceInfo, p Pagination) (*ListResponse[item], error) {
return &ListResponse[item]{ return &ListResponse[item]{
Items: []item{item{"1"}, item{"2"}}, Items: []item{{"1"}, {"2"}},
}, nil }, nil
}) })
assert.NoError(t, err) assert.NoError(t, err)
@ -47,7 +47,7 @@ func TestList(t *testing.T) {
}) })
res, err := List(ctx, "items", a, Pagination{Limit: 2}, func(ctx context.Context, ns claims.NamespaceInfo, p Pagination) (*ListResponse[item], error) { res, err := List(ctx, "items", a, Pagination{Limit: 2}, func(ctx context.Context, ns claims.NamespaceInfo, p Pagination) (*ListResponse[item], error) {
return &ListResponse[item]{ return &ListResponse[item]{
Items: []item{item{"1"}, item{"2"}}, Items: []item{{"1"}, {"2"}},
}, nil }, nil
}) })
assert.NoError(t, err) assert.NoError(t, err)
@ -70,13 +70,13 @@ func TestList(t *testing.T) {
res, err := List(ctx, "items", a, Pagination{Limit: 2}, func(ctx context.Context, ns claims.NamespaceInfo, p Pagination) (*ListResponse[item], error) { res, err := List(ctx, "items", a, Pagination{Limit: 2}, func(ctx context.Context, ns claims.NamespaceInfo, p Pagination) (*ListResponse[item], error) {
if called { if called {
return &ListResponse[item]{ return &ListResponse[item]{
Items: []item{item{"3"}}, Items: []item{{"3"}},
}, nil }, nil
} }
called = true called = true
return &ListResponse[item]{ return &ListResponse[item]{
Items: []item{item{"1"}, item{"2"}}, Items: []item{{"1"}, {"2"}},
Continue: 3, Continue: 3,
}, nil }, nil
}) })

View File

@ -0,0 +1,15 @@
import { Action, DataLinksFieldConfigSettings, StandardEditorProps, VariableSuggestionsScope } from '@grafana/data';
import { ActionsInlineEditor } from 'app/features/actions/ActionsInlineEditor';
type Props = StandardEditorProps<Action[], DataLinksFieldConfigSettings>;
export const ActionsValueEditor = ({ value, onChange, context }: Props) => {
return (
<ActionsInlineEditor
actions={value}
onChange={onChange}
data={context.data}
getSuggestions={() => (context.getSuggestions ? context.getSuggestions(VariableSuggestionsScope.Values) : [])}
/>
);
};

View File

@ -26,7 +26,10 @@ import {
displayNameOverrideProcessor, displayNameOverrideProcessor,
FieldNamePickerConfigSettings, FieldNamePickerConfigSettings,
booleanOverrideProcessor, booleanOverrideProcessor,
Action,
} from '@grafana/data'; } from '@grafana/data';
import { actionsOverrideProcessor } from '@grafana/data/src/field/overrides/processors';
import { config } from '@grafana/runtime';
import { FieldConfig } from '@grafana/schema'; import { FieldConfig } from '@grafana/schema';
import { RadioButtonGroup, TimeZonePicker, Switch } from '@grafana/ui'; import { RadioButtonGroup, TimeZonePicker, Switch } from '@grafana/ui';
import { FieldNamePicker } from '@grafana/ui/src/components/MatchersUI/FieldNamePicker'; import { FieldNamePicker } from '@grafana/ui/src/components/MatchersUI/FieldNamePicker';
@ -34,6 +37,7 @@ import { ThresholdsValueEditor } from 'app/features/dimensions/editors/Threshold
import { ValueMappingsEditor } from 'app/features/dimensions/editors/ValueMappingsEditor/ValueMappingsEditor'; import { ValueMappingsEditor } from 'app/features/dimensions/editors/ValueMappingsEditor/ValueMappingsEditor';
import { DashboardPicker, DashboardPickerOptions } from './DashboardPicker'; import { DashboardPicker, DashboardPickerOptions } from './DashboardPicker';
import { ActionsValueEditor } from './actions';
import { ColorValueEditor, ColorValueEditorSettings } from './color'; import { ColorValueEditor, ColorValueEditorSettings } from './color';
import { FieldColorEditor } from './fieldColor'; import { FieldColorEditor } from './fieldColor';
import { DataLinksValueEditor } from './links'; import { DataLinksValueEditor } from './links';
@ -143,6 +147,13 @@ export const getAllOptionEditors = () => {
editor: DataLinksValueEditor, editor: DataLinksValueEditor,
}; };
const actions: StandardEditorsRegistryItem<Action[]> = {
id: 'actions',
name: 'Actions',
description: 'Allows defining actions',
editor: ActionsValueEditor,
};
const statsPicker: StandardEditorsRegistryItem<string[], StatsPickerConfigSettings> = { const statsPicker: StandardEditorsRegistryItem<string[], StatsPickerConfigSettings> = {
id: 'stats-picker', id: 'stats-picker',
name: 'Stats Picker', name: 'Stats Picker',
@ -194,6 +205,7 @@ export const getAllOptionEditors = () => {
select, select,
unit, unit,
links, links,
actions,
statsPicker, statsPicker,
strings, strings,
timeZone, timeZone,
@ -336,6 +348,8 @@ export const getAllStandardFieldConfigs = () => {
category, category,
}; };
const dataLinksCategory = config.featureToggles.vizActions ? 'Data links and actions' : 'Data links';
const links: FieldConfigPropertyItem<FieldConfig, DataLink[], StringFieldConfigSettings> = { const links: FieldConfigPropertyItem<FieldConfig, DataLink[], StringFieldConfigSettings> = {
id: 'links', id: 'links',
path: 'links', path: 'links',
@ -347,10 +361,26 @@ export const getAllStandardFieldConfigs = () => {
placeholder: '-', placeholder: '-',
}, },
shouldApply: () => true, shouldApply: () => true,
category: ['Data links'], category: [dataLinksCategory],
getItemsCount: (value) => (value ? value.length : 0), getItemsCount: (value) => (value ? value.length : 0),
}; };
const actions: FieldConfigPropertyItem<FieldConfig, Action[], StringFieldConfigSettings> = {
id: 'actions',
path: 'actions',
name: 'Actions',
editor: standardEditorsRegistry.get('actions').editor,
override: standardEditorsRegistry.get('actions').editor,
process: actionsOverrideProcessor,
settings: {
placeholder: '-',
},
shouldApply: () => true,
category: [dataLinksCategory],
getItemsCount: (value) => (value ? value.length : 0),
showIf: () => config.featureToggles.vizActions,
};
const color: FieldConfigPropertyItem<FieldConfig, FieldColor | undefined, FieldColorConfigSettings> = { const color: FieldConfigPropertyItem<FieldConfig, FieldColor | undefined, FieldColorConfigSettings> = {
id: 'color', id: 'color',
path: 'color', path: 'color',
@ -415,5 +445,19 @@ export const getAllStandardFieldConfigs = () => {
category, category,
}; };
return [unit, min, max, fieldMinMax, decimals, displayName, color, noValue, links, mappings, thresholds, filterable]; return [
unit,
min,
max,
fieldMinMax,
decimals,
displayName,
color,
noValue,
links,
actions,
mappings,
thresholds,
filterable,
];
}; };

View File

@ -221,6 +221,7 @@ export const SuggestionsInput = ({
{...inputProps} {...inputProps}
ref={handleRef as unknown as React.RefObject<HTMLTextAreaElement>} ref={handleRef as unknown as React.RefObject<HTMLTextAreaElement>}
autoFocus={autoFocus} autoFocus={autoFocus}
rows={5}
/> />
)} )}
</div> </div>

View File

@ -24,16 +24,7 @@ const charWidth = measureText('M', UPLOT_AXIS_FONT_SIZE).width;
const toRads = Math.PI / 180; const toRads = Math.PI / 180;
export const BarChartPanel = (props: PanelProps<Options>) => { export const BarChartPanel = (props: PanelProps<Options>) => {
const { const { data, options, fieldConfig, width, height, timeZone, id, replaceVariables } = props;
data,
options,
fieldConfig,
width,
height,
timeZone,
id,
// replaceVariables
} = props;
// will need this if joining on time to re-create data links // will need this if joining on time to re-create data links
// const { dataLinkPostProcessor } = usePanelContext(); // const { dataLinkPostProcessor } = usePanelContext();
@ -177,6 +168,7 @@ export const BarChartPanel = (props: PanelProps<Options>) => {
sortOrder={options.tooltip.sort} sortOrder={options.tooltip.sort}
isPinned={isPinned} isPinned={isPinned}
maxHeight={options.tooltip.maxHeight} maxHeight={options.tooltip.maxHeight}
replaceVariables={replaceVariables}
/> />
); );
}} }}

View File

@ -306,6 +306,7 @@ export const CandlestickPanel = ({
isPinned={isPinned} isPinned={isPinned}
annotate={enableAnnotationCreation ? annotate : undefined} annotate={enableAnnotationCreation ? annotate : undefined}
maxHeight={options.tooltip.maxHeight} maxHeight={options.tooltip.maxHeight}
replaceVariables={replaceVariables}
/> />
); );
}} }}

View File

@ -219,6 +219,7 @@ export const HeatmapPanel = ({
annotate={enableAnnotationCreation ? annotate : undefined} annotate={enableAnnotationCreation ? annotate : undefined}
maxHeight={options.tooltip.maxHeight} maxHeight={options.tooltip.maxHeight}
maxWidth={options.tooltip.maxWidth} maxWidth={options.tooltip.maxWidth}
replaceVariables={replaceVariables}
/> />
); );
}} }}

View File

@ -3,11 +3,13 @@ import * as React from 'react';
import uPlot from 'uplot'; import uPlot from 'uplot';
import { import {
ActionModel,
DataFrameType, DataFrameType,
Field, Field,
FieldType, FieldType,
formattedValueToString, formattedValueToString,
getFieldDisplayName, getFieldDisplayName,
InterpolateFunction,
LinkModel, LinkModel,
PanelData, PanelData,
} from '@grafana/data'; } from '@grafana/data';
@ -16,14 +18,14 @@ import { TooltipDisplayMode, useTheme2 } from '@grafana/ui';
import { VizTooltipContent } from '@grafana/ui/src/components/VizTooltip/VizTooltipContent'; import { VizTooltipContent } from '@grafana/ui/src/components/VizTooltip/VizTooltipContent';
import { VizTooltipFooter } from '@grafana/ui/src/components/VizTooltip/VizTooltipFooter'; import { VizTooltipFooter } from '@grafana/ui/src/components/VizTooltip/VizTooltipFooter';
import { VizTooltipHeader } from '@grafana/ui/src/components/VizTooltip/VizTooltipHeader'; import { VizTooltipHeader } from '@grafana/ui/src/components/VizTooltip/VizTooltipHeader';
import { VizTooltipWrapper } from '@grafana/ui/src/components/VizTooltip/VizTooltipWrapper';
import { ColorIndicator, ColorPlacement, VizTooltipItem } from '@grafana/ui/src/components/VizTooltip/types'; import { ColorIndicator, ColorPlacement, VizTooltipItem } from '@grafana/ui/src/components/VizTooltip/types';
import { ColorScale } from 'app/core/components/ColorScale/ColorScale'; import { ColorScale } from 'app/core/components/ColorScale/ColorScale';
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
import { isHeatmapCellsDense, readHeatmapRowsCustomMeta } from 'app/features/transformers/calculateHeatmap/heatmap'; import { isHeatmapCellsDense, readHeatmapRowsCustomMeta } from 'app/features/transformers/calculateHeatmap/heatmap';
import { DataHoverView } from 'app/features/visualization/data-hover/DataHoverView'; import { DataHoverView } from 'app/features/visualization/data-hover/DataHoverView';
import { VizTooltipWrapper } from '../../../../../packages/grafana-ui/src/components/VizTooltip/VizTooltipWrapper'; import { getDataLinks, getFieldActions } from '../status-history/utils';
import { getDataLinks } from '../status-history/utils';
import { isTooltipScrollable } from '../timeseries/utils'; import { isTooltipScrollable } from '../timeseries/utils';
import { HeatmapData } from './fields'; import { HeatmapData } from './fields';
@ -43,6 +45,7 @@ interface HeatmapTooltipProps {
annotate?: () => void; annotate?: () => void;
maxHeight?: number; maxHeight?: number;
maxWidth?: number; maxWidth?: number;
replaceVariables: InterpolateFunction;
} }
export const HeatmapTooltip = (props: HeatmapTooltipProps) => { export const HeatmapTooltip = (props: HeatmapTooltipProps) => {
@ -73,6 +76,7 @@ const HeatmapHoverCell = ({
annotate, annotate,
maxHeight, maxHeight,
maxWidth, maxWidth,
replaceVariables,
}: HeatmapTooltipProps) => { }: HeatmapTooltipProps) => {
const index = dataIdxs[1]!; const index = dataIdxs[1]!;
const data = dataRef.current; const data = dataRef.current;
@ -291,6 +295,7 @@ const HeatmapHoverCell = ({
if (isPinned) { if (isPinned) {
let links: Array<LinkModel<Field>> = []; let links: Array<LinkModel<Field>> = [];
let actions: Array<ActionModel<Field>> = [];
const linksField = data.series?.fields[yValueIdx + 1]; const linksField = data.series?.fields[yValueIdx + 1];
@ -301,9 +306,11 @@ const HeatmapHoverCell = ({
if (visible && hasLinks) { if (visible && hasLinks) {
links = getDataLinks(linksField, xValueIdx); links = getDataLinks(linksField, xValueIdx);
} }
actions = getFieldActions(data.series!, linksField, replaceVariables);
} }
footer = <VizTooltipFooter dataLinks={links} annotate={annotate} />; footer = <VizTooltipFooter dataLinks={links} annotate={annotate} actions={actions} />;
} }
let can = useRef<HTMLCanvasElement>(null); let can = useRef<HTMLCanvasElement>(null);

View File

@ -5,10 +5,10 @@ import { SortOrder, TooltipDisplayMode } from '@grafana/schema/dist/esm/common/c
import { VizTooltipContent } from '@grafana/ui/src/components/VizTooltip/VizTooltipContent'; import { VizTooltipContent } from '@grafana/ui/src/components/VizTooltip/VizTooltipContent';
import { VizTooltipFooter } from '@grafana/ui/src/components/VizTooltip/VizTooltipFooter'; import { VizTooltipFooter } from '@grafana/ui/src/components/VizTooltip/VizTooltipFooter';
import { VizTooltipHeader } from '@grafana/ui/src/components/VizTooltip/VizTooltipHeader'; import { VizTooltipHeader } from '@grafana/ui/src/components/VizTooltip/VizTooltipHeader';
import { VizTooltipWrapper } from '@grafana/ui/src/components/VizTooltip/VizTooltipWrapper';
import { VizTooltipItem } from '@grafana/ui/src/components/VizTooltip/types'; import { VizTooltipItem } from '@grafana/ui/src/components/VizTooltip/types';
import { getContentItems } from '@grafana/ui/src/components/VizTooltip/utils'; import { getContentItems } from '@grafana/ui/src/components/VizTooltip/utils';
import { VizTooltipWrapper } from '../../../../../packages/grafana-ui/src/components/VizTooltip/VizTooltipWrapper';
import { getDataLinks } from '../status-history/utils'; import { getDataLinks } from '../status-history/utils';
import { isTooltipScrollable } from '../timeseries/utils'; import { isTooltipScrollable } from '../timeseries/utils';

View File

@ -198,6 +198,7 @@ export const StateTimelinePanel = ({
annotate={enableAnnotationCreation ? annotate : undefined} annotate={enableAnnotationCreation ? annotate : undefined}
withDuration={true} withDuration={true}
maxHeight={options.tooltip.maxHeight} maxHeight={options.tooltip.maxHeight}
replaceVariables={replaceVariables}
/> />
); );
}} }}

View File

@ -6,12 +6,12 @@ import { TooltipDisplayMode } from '@grafana/ui';
import { VizTooltipContent } from '@grafana/ui/src/components/VizTooltip/VizTooltipContent'; import { VizTooltipContent } from '@grafana/ui/src/components/VizTooltip/VizTooltipContent';
import { VizTooltipFooter } from '@grafana/ui/src/components/VizTooltip/VizTooltipFooter'; import { VizTooltipFooter } from '@grafana/ui/src/components/VizTooltip/VizTooltipFooter';
import { VizTooltipHeader } from '@grafana/ui/src/components/VizTooltip/VizTooltipHeader'; import { VizTooltipHeader } from '@grafana/ui/src/components/VizTooltip/VizTooltipHeader';
import { VizTooltipWrapper } from '@grafana/ui/src/components/VizTooltip/VizTooltipWrapper';
import { VizTooltipItem } from '@grafana/ui/src/components/VizTooltip/types'; import { VizTooltipItem } from '@grafana/ui/src/components/VizTooltip/types';
import { getContentItems } from '@grafana/ui/src/components/VizTooltip/utils'; import { getContentItems } from '@grafana/ui/src/components/VizTooltip/utils';
import { findNextStateIndex, fmtDuration } from 'app/core/components/TimelineChart/utils'; import { findNextStateIndex, fmtDuration } from 'app/core/components/TimelineChart/utils';
import { VizTooltipWrapper } from '../../../../../packages/grafana-ui/src/components/VizTooltip/VizTooltipWrapper'; import { getDataLinks, getFieldActions } from '../status-history/utils';
import { getDataLinks } from '../status-history/utils';
import { TimeSeriesTooltipProps } from '../timeseries/TimeSeriesTooltip'; import { TimeSeriesTooltipProps } from '../timeseries/TimeSeriesTooltip';
import { isTooltipScrollable } from '../timeseries/utils'; import { isTooltipScrollable } from '../timeseries/utils';
@ -31,6 +31,7 @@ export const StateTimelineTooltip2 = ({
timeRange, timeRange,
withDuration, withDuration,
maxHeight, maxHeight,
replaceVariables,
}: StateTimelineTooltip2Props) => { }: StateTimelineTooltip2Props) => {
const xField = series.fields[0]; const xField = series.fields[0];
@ -70,8 +71,9 @@ export const StateTimelineTooltip2 = ({
const field = series.fields[seriesIdx]; const field = series.fields[seriesIdx];
const dataIdx = dataIdxs[seriesIdx]!; const dataIdx = dataIdxs[seriesIdx]!;
const links = getDataLinks(field, dataIdx); const links = getDataLinks(field, dataIdx);
const actions = getFieldActions(series, field, replaceVariables!);
footer = <VizTooltipFooter dataLinks={links} annotate={annotate} />; footer = <VizTooltipFooter dataLinks={links} annotate={annotate} actions={actions} />;
} }
const headerItem: VizTooltipItem = { const headerItem: VizTooltipItem = {

View File

@ -130,6 +130,7 @@ export const StatusHistoryPanel = ({
annotate={enableAnnotationCreation ? annotate : undefined} annotate={enableAnnotationCreation ? annotate : undefined}
withDuration={false} withDuration={false}
maxHeight={options.tooltip.maxHeight} maxHeight={options.tooltip.maxHeight}
replaceVariables={replaceVariables}
/> />
); );
}} }}

View File

@ -1,4 +1,7 @@
import { Field, LinkModel } from '@grafana/data'; import { ActionModel, Field, InterpolateFunction, LinkModel } from '@grafana/data';
import { DataFrame } from '@grafana/data/';
import { config } from '@grafana/runtime';
import { getActions } from 'app/features/actions/utils';
export const getDataLinks = (field: Field, rowIdx: number) => { export const getDataLinks = (field: Field, rowIdx: number) => {
const links: Array<LinkModel<Field>> = []; const links: Array<LinkModel<Field>> = [];
@ -20,3 +23,31 @@ export const getDataLinks = (field: Field, rowIdx: number) => {
return links; return links;
}; };
export const getFieldActions = (dataFrame: DataFrame, field: Field, replaceVars: InterpolateFunction) => {
if (!config.featureToggles?.vizActions) {
return [];
}
const actions: Array<ActionModel<Field>> = [];
const actionLookup = new Set<string>();
const actionsModel = getActions(
dataFrame,
field,
field.state!.scopedVars!,
replaceVars,
field.config.actions ?? [],
{}
);
actionsModel.forEach((action) => {
const key = `${action.title}`;
if (!actionLookup.has(key)) {
actions.push(action);
actionLookup.add(key);
}
});
return actions;
};

View File

@ -131,6 +131,7 @@ export const TimeSeriesPanel = ({
isPinned={isPinned} isPinned={isPinned}
annotate={enableAnnotationCreation ? annotate : undefined} annotate={enableAnnotationCreation ? annotate : undefined}
maxHeight={options.tooltip.maxHeight} maxHeight={options.tooltip.maxHeight}
replaceVariables={replaceVariables}
/> />
); );
}} }}

View File

@ -1,16 +1,15 @@
import { css } from '@emotion/css';
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { DataFrame, Field, FieldType, formattedValueToString } from '@grafana/data'; import { DataFrame, Field, FieldType, formattedValueToString, InterpolateFunction } from '@grafana/data';
import { SortOrder, TooltipDisplayMode } from '@grafana/schema/dist/esm/common/common.gen'; import { SortOrder, TooltipDisplayMode } from '@grafana/schema/dist/esm/common/common.gen';
import { VizTooltipContent } from '@grafana/ui/src/components/VizTooltip/VizTooltipContent'; import { VizTooltipContent } from '@grafana/ui/src/components/VizTooltip/VizTooltipContent';
import { VizTooltipFooter } from '@grafana/ui/src/components/VizTooltip/VizTooltipFooter'; import { VizTooltipFooter } from '@grafana/ui/src/components/VizTooltip/VizTooltipFooter';
import { VizTooltipHeader } from '@grafana/ui/src/components/VizTooltip/VizTooltipHeader'; import { VizTooltipHeader } from '@grafana/ui/src/components/VizTooltip/VizTooltipHeader';
import { VizTooltipWrapper } from '@grafana/ui/src/components/VizTooltip/VizTooltipWrapper';
import { VizTooltipItem } from '@grafana/ui/src/components/VizTooltip/types'; import { VizTooltipItem } from '@grafana/ui/src/components/VizTooltip/types';
import { getContentItems } from '@grafana/ui/src/components/VizTooltip/utils'; import { getContentItems } from '@grafana/ui/src/components/VizTooltip/utils';
import { VizTooltipWrapper } from '../../../../../packages/grafana-ui/src/components/VizTooltip/VizTooltipWrapper'; import { getDataLinks, getFieldActions } from '../status-history/utils';
import { getDataLinks } from '../status-history/utils';
import { fmt } from '../xychart/utils'; import { fmt } from '../xychart/utils';
import { isTooltipScrollable } from './utils'; import { isTooltipScrollable } from './utils';
@ -36,6 +35,8 @@ export interface TimeSeriesTooltipProps {
annotate?: () => void; annotate?: () => void;
maxHeight?: number; maxHeight?: number;
replaceVariables?: InterpolateFunction;
} }
export const TimeSeriesTooltip = ({ export const TimeSeriesTooltip = ({
@ -48,6 +49,7 @@ export const TimeSeriesTooltip = ({
isPinned, isPinned,
annotate, annotate,
maxHeight, maxHeight,
replaceVariables,
}: TimeSeriesTooltipProps) => { }: TimeSeriesTooltipProps) => {
const xField = series.fields[0]; const xField = series.fields[0];
const xVal = formattedValueToString(xField.display!(xField.values[dataIdxs[0]!])); const xVal = formattedValueToString(xField.display!(xField.values[dataIdxs[0]!]));
@ -77,8 +79,9 @@ export const TimeSeriesTooltip = ({
const field = series.fields[seriesIdx]; const field = series.fields[seriesIdx];
const dataIdx = dataIdxs[seriesIdx]!; const dataIdx = dataIdxs[seriesIdx]!;
const links = getDataLinks(field, dataIdx); const links = getDataLinks(field, dataIdx);
const actions = getFieldActions(series, field, replaceVariables!);
footer = <VizTooltipFooter dataLinks={links} annotate={annotate} />; footer = <VizTooltipFooter dataLinks={links} actions={actions} annotate={annotate} />;
} }
const headerItem: VizTooltipItem | null = xField.config.custom?.hideFrom?.tooltip const headerItem: VizTooltipItem | null = xField.config.custom?.hideFrom?.tooltip
@ -101,10 +104,3 @@ export const TimeSeriesTooltip = ({
</VizTooltipWrapper> </VizTooltipWrapper>
); );
}; };
export const getStyles = () => ({
wrapper: css({
display: 'flex',
flexDirection: 'column',
}),
});

View File

@ -130,6 +130,7 @@ export const TrendPanel = ({
sortOrder={options.tooltip.sort} sortOrder={options.tooltip.sort}
isPinned={isPinned} isPinned={isPinned}
maxHeight={options.tooltip.maxHeight} maxHeight={options.tooltip.maxHeight}
replaceVariables={replaceVariables}
/> />
); );
}} }}

View File

@ -5,9 +5,9 @@ import { alpha } from '@grafana/data/src/themes/colorManipulator';
import { VizTooltipContent } from '@grafana/ui/src/components/VizTooltip/VizTooltipContent'; import { VizTooltipContent } from '@grafana/ui/src/components/VizTooltip/VizTooltipContent';
import { VizTooltipFooter } from '@grafana/ui/src/components/VizTooltip/VizTooltipFooter'; import { VizTooltipFooter } from '@grafana/ui/src/components/VizTooltip/VizTooltipFooter';
import { VizTooltipHeader } from '@grafana/ui/src/components/VizTooltip/VizTooltipHeader'; import { VizTooltipHeader } from '@grafana/ui/src/components/VizTooltip/VizTooltipHeader';
import { VizTooltipWrapper } from '@grafana/ui/src/components/VizTooltip/VizTooltipWrapper';
import { ColorIndicator, VizTooltipItem } from '@grafana/ui/src/components/VizTooltip/types'; import { ColorIndicator, VizTooltipItem } from '@grafana/ui/src/components/VizTooltip/types';
import { VizTooltipWrapper } from '../../../../../packages/grafana-ui/src/components/VizTooltip/VizTooltipWrapper';
import { getDataLinks } from '../status-history/utils'; import { getDataLinks } from '../status-history/utils';
import { Options } from './panelcfg.gen'; import { Options } from './panelcfg.gen';

View File

@ -122,6 +122,7 @@ export const XYChartPanel2 = (props: Props2) => {
dismiss={dismiss} dismiss={dismiss}
isPinned={isPinned} isPinned={isPinned}
seriesIdx={seriesIdx!} seriesIdx={seriesIdx!}
replaceVariables={props.replaceVariables}
/> />
); );
}} }}

View File

@ -1,14 +1,14 @@
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { DataFrame } from '@grafana/data'; import { DataFrame, InterpolateFunction } from '@grafana/data';
import { alpha } from '@grafana/data/src/themes/colorManipulator'; import { alpha } from '@grafana/data/src/themes/colorManipulator';
import { VizTooltipContent } from '@grafana/ui/src/components/VizTooltip/VizTooltipContent'; import { VizTooltipContent } from '@grafana/ui/src/components/VizTooltip/VizTooltipContent';
import { VizTooltipFooter } from '@grafana/ui/src/components/VizTooltip/VizTooltipFooter'; import { VizTooltipFooter } from '@grafana/ui/src/components/VizTooltip/VizTooltipFooter';
import { VizTooltipHeader } from '@grafana/ui/src/components/VizTooltip/VizTooltipHeader'; import { VizTooltipHeader } from '@grafana/ui/src/components/VizTooltip/VizTooltipHeader';
import { VizTooltipWrapper } from '@grafana/ui/src/components/VizTooltip/VizTooltipWrapper';
import { ColorIndicator, VizTooltipItem } from '@grafana/ui/src/components/VizTooltip/types'; import { ColorIndicator, VizTooltipItem } from '@grafana/ui/src/components/VizTooltip/types';
import { VizTooltipWrapper } from '../../../../../../packages/grafana-ui/src/components/VizTooltip/VizTooltipWrapper'; import { getDataLinks, getFieldActions } from '../../status-history/utils';
import { getDataLinks } from '../../status-history/utils';
import { XYSeries } from './types2'; import { XYSeries } from './types2';
import { fmt } from './utils'; import { fmt } from './utils';
@ -20,6 +20,7 @@ export interface Props {
dismiss: () => void; dismiss: () => void;
data: DataFrame[]; data: DataFrame[];
xySeries: XYSeries[]; xySeries: XYSeries[];
replaceVariables: InterpolateFunction;
} }
function stripSeriesName(fieldName: string, seriesName: string) { function stripSeriesName(fieldName: string, seriesName: string) {
@ -30,7 +31,7 @@ function stripSeriesName(fieldName: string, seriesName: string) {
return fieldName; return fieldName;
} }
export const XYChartTooltip = ({ dataIdxs, seriesIdx, data, xySeries, dismiss, isPinned }: Props) => { export const XYChartTooltip = ({ dataIdxs, seriesIdx, data, xySeries, dismiss, isPinned, replaceVariables }: Props) => {
const rowIndex = dataIdxs.find((idx) => idx !== null)!; const rowIndex = dataIdxs.find((idx) => idx !== null)!;
const series = xySeries[seriesIdx! - 1]; const series = xySeries[seriesIdx! - 1];
@ -94,8 +95,10 @@ export const XYChartTooltip = ({ dataIdxs, seriesIdx, data, xySeries, dismiss, i
if (isPinned && seriesIdx != null) { if (isPinned && seriesIdx != null) {
const links = getDataLinks(yField, rowIndex); const links = getDataLinks(yField, rowIndex);
const yFieldFrame = data.find((frame) => frame.fields.includes(yField))!;
const actions = getFieldActions(yFieldFrame, yField, replaceVariables);
footer = <VizTooltipFooter dataLinks={links} />; footer = <VizTooltipFooter dataLinks={links} actions={actions} />;
} }
return ( return (