mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
DataLinks: enable data links in Gauge, BarGauge and SingleStat2 panel (#18605)
* datalink on field * add dataFrame to view * Use scoped variables to pass series name and value time to data links interpolation * Use scoped variables to pass series name and value time to data links interpolation * Enable value specific variable suggestions when Gauge is displaying values * Fix prettier * Add basic context menu with data links to GaugePanel * Fix incorrect import in grafana/ui * Add custom cursor indicating datalinks available via context menu (in Gauge only now) * Add data links to SingleStat2 * Minor refactor * Retrieve data links in a lazy way * Update test to respect links retrieval being lazy * delay link creation * cleanup * Add origin to LinkModel and introduce field & panel links suppliers * Add value time and series name field link supplier * Remove links prop from visualization and implement common UI for data links context menu * Update snapshot * Rename className prop to clickTargetClassName * Simplify condition * Updated drilldown dashboard and minor changes * Use class name an onClick handler on the top level dom element in visualization * Enable series name interpolation when presented value is a calculation
This commit is contained in:
committed by
Dominik Prokop
parent
e1924608a2
commit
ff6b8c5adc
@@ -5,11 +5,12 @@ import React, { PureComponent } from 'react';
|
||||
import { config } from 'app/core/config';
|
||||
|
||||
// Components
|
||||
import { BarGauge, VizRepeater, getFieldDisplayValues, FieldDisplay } from '@grafana/ui';
|
||||
import { BarGauge, VizRepeater, getFieldDisplayValues, FieldDisplay, DataLinksContextMenu } from '@grafana/ui';
|
||||
|
||||
// Types
|
||||
import { BarGaugeOptions } from './types';
|
||||
import { PanelProps } from '@grafana/ui';
|
||||
import { getFieldLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers';
|
||||
|
||||
export class BarGaugePanel extends PureComponent<PanelProps<BarGaugeOptions>> {
|
||||
renderValue = (value: FieldDisplay, width: number, height: number): JSX.Element => {
|
||||
@@ -17,18 +18,26 @@ export class BarGaugePanel extends PureComponent<PanelProps<BarGaugeOptions>> {
|
||||
const { field, display } = value;
|
||||
|
||||
return (
|
||||
<BarGauge
|
||||
value={display}
|
||||
width={width}
|
||||
height={height}
|
||||
orientation={options.orientation}
|
||||
thresholds={field.thresholds}
|
||||
theme={config.theme}
|
||||
itemSpacing={this.getItemSpacing()}
|
||||
displayMode={options.displayMode}
|
||||
minValue={field.min}
|
||||
maxValue={field.max}
|
||||
/>
|
||||
<DataLinksContextMenu links={getFieldLinksSupplier(value)}>
|
||||
{({ openMenu, targetClassName }) => {
|
||||
return (
|
||||
<BarGauge
|
||||
value={display}
|
||||
width={width}
|
||||
height={height}
|
||||
orientation={options.orientation}
|
||||
thresholds={field.thresholds}
|
||||
theme={config.theme}
|
||||
itemSpacing={this.getItemSpacing()}
|
||||
displayMode={options.displayMode}
|
||||
minValue={field.min}
|
||||
maxValue={field.max}
|
||||
onClick={openMenu}
|
||||
className={targetClassName}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</DataLinksContextMenu>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -12,11 +12,16 @@ import {
|
||||
FormLabel,
|
||||
PanelEditorProps,
|
||||
Select,
|
||||
DataLinksEditor,
|
||||
} from '@grafana/ui';
|
||||
import { FieldConfig } from '@grafana/data';
|
||||
import { FieldConfig, DataLink } from '@grafana/data';
|
||||
|
||||
import { Threshold, ValueMapping } from '@grafana/data';
|
||||
import { BarGaugeOptions, orientationOptions, displayModes } from './types';
|
||||
import {
|
||||
getDataLinksVariableSuggestions,
|
||||
getCalculationValueDataLinksVariableSuggestions,
|
||||
} from 'app/features/panel/panellinks/link_srv';
|
||||
|
||||
export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGaugeOptions>> {
|
||||
onThresholdsChanged = (thresholds: Threshold[]) => {
|
||||
@@ -51,11 +56,20 @@ export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGauge
|
||||
onOrientationChange = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, orientation: value });
|
||||
onDisplayModeChange = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, displayMode: value });
|
||||
|
||||
onDataLinksChanged = (links: DataLink[]) => {
|
||||
this.onDefaultsChange({
|
||||
...this.props.options.fieldOptions.defaults,
|
||||
links,
|
||||
});
|
||||
};
|
||||
render() {
|
||||
const { options } = this.props;
|
||||
const { fieldOptions } = options;
|
||||
const { defaults } = fieldOptions;
|
||||
|
||||
const suggestions = fieldOptions.values
|
||||
? getDataLinksVariableSuggestions()
|
||||
: getCalculationValueDataLinksVariableSuggestions();
|
||||
const labelWidth = 6;
|
||||
|
||||
return (
|
||||
@@ -92,6 +106,15 @@ export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGauge
|
||||
</PanelOptionsGrid>
|
||||
|
||||
<ValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
||||
|
||||
<PanelOptionsGroup title="Data links">
|
||||
<DataLinksEditor
|
||||
value={defaults.links}
|
||||
onChange={this.onDataLinksChanged}
|
||||
suggestions={suggestions}
|
||||
maxLinks={10}
|
||||
/>
|
||||
</PanelOptionsGroup>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,11 +5,12 @@ import React, { PureComponent } from 'react';
|
||||
import { config } from 'app/core/config';
|
||||
|
||||
// Components
|
||||
import { Gauge, FieldDisplay, getFieldDisplayValues, VizOrientation } from '@grafana/ui';
|
||||
import { Gauge, FieldDisplay, getFieldDisplayValues, VizOrientation, DataLinksContextMenu } from '@grafana/ui';
|
||||
|
||||
// Types
|
||||
import { GaugeOptions } from './types';
|
||||
import { PanelProps, VizRepeater } from '@grafana/ui';
|
||||
import { getFieldLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers';
|
||||
|
||||
export class GaugePanel extends PureComponent<PanelProps<GaugeOptions>> {
|
||||
renderValue = (value: FieldDisplay, width: number, height: number): JSX.Element => {
|
||||
@@ -17,17 +18,25 @@ export class GaugePanel extends PureComponent<PanelProps<GaugeOptions>> {
|
||||
const { field, display } = value;
|
||||
|
||||
return (
|
||||
<Gauge
|
||||
value={display}
|
||||
width={width}
|
||||
height={height}
|
||||
thresholds={field.thresholds}
|
||||
showThresholdLabels={options.showThresholdLabels}
|
||||
showThresholdMarkers={options.showThresholdMarkers}
|
||||
minValue={field.min}
|
||||
maxValue={field.max}
|
||||
theme={config.theme}
|
||||
/>
|
||||
<DataLinksContextMenu links={getFieldLinksSupplier(value)}>
|
||||
{({ openMenu, targetClassName }) => {
|
||||
return (
|
||||
<Gauge
|
||||
value={display}
|
||||
width={width}
|
||||
height={height}
|
||||
thresholds={field.thresholds}
|
||||
showThresholdLabels={options.showThresholdLabels}
|
||||
showThresholdMarkers={options.showThresholdMarkers}
|
||||
minValue={field.min}
|
||||
maxValue={field.max}
|
||||
theme={config.theme}
|
||||
onClick={openMenu}
|
||||
className={targetClassName}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</DataLinksContextMenu>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -10,10 +10,15 @@ import {
|
||||
FieldPropertiesEditor,
|
||||
Switch,
|
||||
PanelOptionsGroup,
|
||||
DataLinksEditor,
|
||||
} from '@grafana/ui';
|
||||
import { Threshold, ValueMapping, FieldConfig } from '@grafana/data';
|
||||
import { Threshold, ValueMapping, FieldConfig, DataLink } from '@grafana/data';
|
||||
|
||||
import { GaugeOptions } from './types';
|
||||
import {
|
||||
getCalculationValueDataLinksVariableSuggestions,
|
||||
getDataLinksVariableSuggestions,
|
||||
} from 'app/features/panel/panellinks/link_srv';
|
||||
|
||||
export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOptions>> {
|
||||
labelWidth = 6;
|
||||
@@ -56,10 +61,20 @@ export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOption
|
||||
});
|
||||
};
|
||||
|
||||
onDataLinksChanged = (links: DataLink[]) => {
|
||||
this.onDefaultsChange({
|
||||
...this.props.options.fieldOptions.defaults,
|
||||
links,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { options } = this.props;
|
||||
const { fieldOptions, showThresholdLabels, showThresholdMarkers } = options;
|
||||
const { defaults } = fieldOptions;
|
||||
const suggestions = fieldOptions.values
|
||||
? getDataLinksVariableSuggestions()
|
||||
: getCalculationValueDataLinksVariableSuggestions();
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -92,6 +107,15 @@ export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOption
|
||||
</PanelOptionsGrid>
|
||||
|
||||
<ValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
||||
|
||||
<PanelOptionsGroup title="Data links">
|
||||
<DataLinksEditor
|
||||
value={defaults.links}
|
||||
onChange={this.onDataLinksChanged}
|
||||
suggestions={suggestions}
|
||||
maxLinks={10}
|
||||
/>
|
||||
</PanelOptionsGroup>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import ReactDOM from 'react-dom';
|
||||
import { GraphLegendProps, Legend } from './Legend/Legend';
|
||||
|
||||
import { GraphCtrl } from './module';
|
||||
import { getValueFormat, ContextMenuItem, ContextMenuGroup } from '@grafana/ui';
|
||||
import { getValueFormat, ContextMenuItem, ContextMenuGroup, DataLinkBuiltInVars } from '@grafana/ui';
|
||||
import { provideTheme } from 'app/core/utils/ConfigProvider';
|
||||
import { DataLink, toUtc } from '@grafana/data';
|
||||
import { GraphContextMenuCtrl, FlotDataPoint } from './GraphContextMenuCtrl';
|
||||
@@ -196,10 +196,15 @@ class GraphElement {
|
||||
{
|
||||
items: [
|
||||
...dataLinks.map<ContextMenuItem>(link => {
|
||||
const linkUiModel = this.linkSrv.getDataLinkUIModel(link, this.panel.scopedVars, {
|
||||
seriesName: item.series.alias,
|
||||
datapoint: item.datapoint,
|
||||
});
|
||||
const linkUiModel = this.linkSrv.getDataLinkUIModel(
|
||||
link,
|
||||
{
|
||||
...this.panel.scopedVars,
|
||||
[DataLinkBuiltInVars.seriesName]: { value: item.series.alias, text: item.series.alias },
|
||||
[DataLinkBuiltInVars.valueTime]: { value: item.datapoint[0], text: item.datapoint[0] },
|
||||
},
|
||||
item
|
||||
);
|
||||
return {
|
||||
label: linkUiModel.title,
|
||||
url: linkUiModel.href,
|
||||
|
||||
@@ -24,9 +24,10 @@ import {
|
||||
DisplayValue,
|
||||
fieldReducers,
|
||||
KeyValue,
|
||||
LinkModel,
|
||||
} from '@grafana/data';
|
||||
import { auto } from 'angular';
|
||||
import { LinkSrv, LinkModel } from 'app/features/panel/panellinks/link_srv';
|
||||
import { LinkSrv } from 'app/features/panel/panellinks/link_srv';
|
||||
import { PanelQueryRunnerFormat } from 'app/features/dashboard/state/PanelQueryRunner';
|
||||
import { getProcessedDataFrames } from 'app/features/dashboard/state/PanelQueryState';
|
||||
|
||||
@@ -328,7 +329,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
const $sanitize = this.$sanitize;
|
||||
const panel = ctrl.panel;
|
||||
const templateSrv = this.templateSrv;
|
||||
let linkInfo: LinkModel | null = null;
|
||||
let linkInfo: LinkModel<any> | null = null;
|
||||
const $panelContainer = elem.find('.panel-container');
|
||||
elem = elem.find('.singlestat-panel');
|
||||
|
||||
@@ -592,7 +593,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
elem.toggleClass('pointer', panel.links.length > 0);
|
||||
|
||||
if (panel.links.length > 0) {
|
||||
linkInfo = linkSrv.getDataLinkUIModel(panel.links[0], data.scopedVars);
|
||||
linkInfo = linkSrv.getDataLinkUIModel(panel.links[0], data.scopedVars, {});
|
||||
} else {
|
||||
linkInfo = null;
|
||||
}
|
||||
|
||||
@@ -9,13 +9,18 @@ import {
|
||||
FieldDisplayEditor,
|
||||
FieldPropertiesEditor,
|
||||
PanelOptionsGroup,
|
||||
DataLinksEditor,
|
||||
} from '@grafana/ui';
|
||||
import { Threshold, ValueMapping, FieldConfig } from '@grafana/data';
|
||||
import { Threshold, ValueMapping, FieldConfig, DataLink } from '@grafana/data';
|
||||
|
||||
import { SingleStatOptions, SparklineOptions } from './types';
|
||||
import { ColoringEditor } from './ColoringEditor';
|
||||
import { FontSizeEditor } from './FontSizeEditor';
|
||||
import { SparklineEditor } from './SparklineEditor';
|
||||
import {
|
||||
getDataLinksVariableSuggestions,
|
||||
getCalculationValueDataLinksVariableSuggestions,
|
||||
} from 'app/features/panel/panellinks/link_srv';
|
||||
|
||||
export class SingleStatEditor extends PureComponent<PanelEditorProps<SingleStatOptions>> {
|
||||
onThresholdsChanged = (thresholds: Threshold[]) => {
|
||||
@@ -53,10 +58,20 @@ export class SingleStatEditor extends PureComponent<PanelEditorProps<SingleStatO
|
||||
});
|
||||
};
|
||||
|
||||
onDataLinksChanged = (links: DataLink[]) => {
|
||||
this.onDefaultsChange({
|
||||
...this.props.options.fieldOptions.defaults,
|
||||
links,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { options } = this.props;
|
||||
const { fieldOptions } = options;
|
||||
const { defaults } = fieldOptions;
|
||||
const suggestions = fieldOptions.values
|
||||
? getDataLinksVariableSuggestions()
|
||||
: getCalculationValueDataLinksVariableSuggestions();
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -77,6 +92,15 @@ export class SingleStatEditor extends PureComponent<PanelEditorProps<SingleStatO
|
||||
</PanelOptionsGrid>
|
||||
|
||||
<ValueMappingsEditor onChange={this.onValueMappingsChanged} valueMappings={defaults.mappings} />
|
||||
|
||||
<PanelOptionsGroup title="Data links">
|
||||
<DataLinksEditor
|
||||
value={defaults.links}
|
||||
onChange={this.onDataLinksChanged}
|
||||
suggestions={suggestions}
|
||||
maxLinks={10}
|
||||
/>
|
||||
</PanelOptionsGroup>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,8 +6,16 @@ import { config } from 'app/core/config';
|
||||
|
||||
// Types
|
||||
import { SingleStatOptions } from './types';
|
||||
import { PanelProps, getFieldDisplayValues, VizRepeater, FieldDisplay, BigValue } from '@grafana/ui';
|
||||
import {
|
||||
PanelProps,
|
||||
getFieldDisplayValues,
|
||||
VizRepeater,
|
||||
FieldDisplay,
|
||||
BigValue,
|
||||
DataLinksContextMenu,
|
||||
} from '@grafana/ui';
|
||||
import { BigValueSparkline } from '@grafana/ui/src/components/BigValue/BigValue';
|
||||
import { getFieldLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers';
|
||||
|
||||
export class SingleStatPanel extends PureComponent<PanelProps<SingleStatOptions>> {
|
||||
renderValue = (value: FieldDisplay, width: number, height: number): JSX.Element => {
|
||||
@@ -23,7 +31,23 @@ export class SingleStatPanel extends PureComponent<PanelProps<SingleStatOptions>
|
||||
};
|
||||
}
|
||||
|
||||
return <BigValue value={value.display} sparkline={sparkline} width={width} height={height} theme={config.theme} />;
|
||||
return (
|
||||
<DataLinksContextMenu links={getFieldLinksSupplier(value)}>
|
||||
{({ openMenu, targetClassName }) => {
|
||||
return (
|
||||
<BigValue
|
||||
value={value.display}
|
||||
sparkline={sparkline}
|
||||
width={width}
|
||||
height={height}
|
||||
theme={config.theme}
|
||||
onClick={openMenu}
|
||||
className={targetClassName}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</DataLinksContextMenu>
|
||||
);
|
||||
};
|
||||
|
||||
getValues = (): FieldDisplay[] => {
|
||||
|
||||
Reference in New Issue
Block a user