diff --git a/packages/grafana-data/src/field/fieldDisplay.ts b/packages/grafana-data/src/field/fieldDisplay.ts index 8c56db60dbf..9f284f9505d 100644 --- a/packages/grafana-data/src/field/fieldDisplay.ts +++ b/packages/grafana-data/src/field/fieldDisplay.ts @@ -7,6 +7,7 @@ import { DataFrame, DisplayValue, DisplayValueAlignmentFactors, + Field, FieldConfig, FieldConfigSource, FieldType, @@ -80,6 +81,7 @@ export interface FieldDisplay { colIndex?: number; // The field column index rowIndex?: number; // only filled in when the value is from a row (ie, not a reduction) getLinks?: () => LinkModel[]; + hasLinks: boolean; } export interface GetFieldDisplayValuesOptions { @@ -168,6 +170,7 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi valueRowIndex: j, }) : () => [], + hasLinks: hasLinks(field), }); if (values.length >= limit) { @@ -210,6 +213,7 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi calculatedValue: displayValue, }) : () => [], + hasLinks: hasLinks(field), }); } } @@ -227,6 +231,10 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi return values; }; +export function hasLinks(field: Field): boolean { + return field.config?.links?.length ? field.config.links.length > 0 : false; +} + export function getDisplayValueAlignmentFactors(values: FieldDisplay[]): DisplayValueAlignmentFactors { const info: DisplayValueAlignmentFactors = { title: '', @@ -287,6 +295,7 @@ function createNoValuesFieldDisplay(options: GetFieldDisplayValuesOptions): Fiel numeric: 0, color: display.color, }, + hasLinks: false, }; } diff --git a/packages/grafana-ui/src/components/DataLinks/DataLinksContextMenu.tsx b/packages/grafana-ui/src/components/DataLinks/DataLinksContextMenu.tsx index 8fb639f9f34..0cce3e8e9d8 100644 --- a/packages/grafana-ui/src/components/DataLinks/DataLinksContextMenu.tsx +++ b/packages/grafana-ui/src/components/DataLinks/DataLinksContextMenu.tsx @@ -5,15 +5,16 @@ import { linkModelToContextMenuItems } from '../../utils/dataLinks'; import { css } from 'emotion'; interface DataLinksContextMenuProps { - children: (props: { openMenu?: React.MouseEventHandler; targetClassName?: string }) => JSX.Element; - links?: () => LinkModel[]; + children: (props: DataLinksContextMenuApi) => JSX.Element; + links: () => LinkModel[]; +} + +export interface DataLinksContextMenuApi { + openMenu?: React.MouseEventHandler; + targetClassName?: string; } export const DataLinksContextMenu: React.FC = ({ children, links }) => { - if (!links) { - return children({}); - } - const getDataLinksContextMenuItems = () => { return [{ items: linkModelToContextMenuItems(links), label: 'Data links' }]; }; diff --git a/public/app/features/panel/panellinks/linkSuppliers.test.ts b/public/app/features/panel/panellinks/linkSuppliers.test.ts index ee10e211e8e..007e0e155d2 100644 --- a/public/app/features/panel/panellinks/linkSuppliers.test.ts +++ b/public/app/features/panel/panellinks/linkSuppliers.test.ts @@ -149,6 +149,7 @@ describe('getLinksFromLogsField', () => { rowIndex, colIndex, display: field.display!(field.values.get(rowIndex)), + hasLinks: true, }; const supplier = getFieldLinksSupplier(fieldDisp); diff --git a/public/app/plugins/panel/bargauge/BarGaugePanel.tsx b/public/app/plugins/panel/bargauge/BarGaugePanel.tsx index 7f603142888..ab80e09ca8e 100644 --- a/public/app/plugins/panel/bargauge/BarGaugePanel.tsx +++ b/public/app/plugins/panel/bargauge/BarGaugePanel.tsx @@ -1,45 +1,58 @@ -// Libraries import React, { PureComponent } from 'react'; - -// Services & Utils -import { config } from 'app/core/config'; - -import { BarGauge, VizRepeater, VizRepeaterRenderValueProps, DataLinksContextMenu } from '@grafana/ui'; -import { BarGaugeOptions } from './types'; import { - getFieldDisplayValues, - FieldDisplay, - PanelProps, - getDisplayValueAlignmentFactors, DisplayValueAlignmentFactors, + FieldDisplay, + getDisplayValueAlignmentFactors, + getFieldDisplayValues, + PanelProps, } from '@grafana/data'; +import { BarGauge, DataLinksContextMenu, VizRepeater, VizRepeaterRenderValueProps } from '@grafana/ui'; + +import { config } from 'app/core/config'; +import { BarGaugeOptions } from './types'; +import { DataLinksContextMenuApi } from '@grafana/ui/src/components/DataLinks/DataLinksContextMenu'; export class BarGaugePanel extends PureComponent> { - renderValue = (valueProps: VizRepeaterRenderValueProps): JSX.Element => { + renderComponent = ( + valueProps: VizRepeaterRenderValueProps, + menuProps: DataLinksContextMenuApi + ): JSX.Element => { const { options } = this.props; const { value, alignmentFactors, orientation, width, height } = valueProps; const { field, display, view, colIndex } = value; + const { openMenu, targetClassName } = menuProps; return ( - - {({ openMenu, targetClassName }) => { - return ( - - ); + + ); + }; + + renderValue = (valueProps: VizRepeaterRenderValueProps): JSX.Element => { + const { value } = valueProps; + const { hasLinks, getLinks } = value; + + if (!hasLinks) { + return this.renderComponent(valueProps, {}); + } + + return ( + + {api => { + return this.renderComponent(valueProps, api); }} ); diff --git a/public/app/plugins/panel/gauge/GaugePanel.tsx b/public/app/plugins/panel/gauge/GaugePanel.tsx index de049e42d5d..8aa6f46c76e 100644 --- a/public/app/plugins/panel/gauge/GaugePanel.tsx +++ b/public/app/plugins/panel/gauge/GaugePanel.tsx @@ -1,38 +1,48 @@ -// Libraries import React, { PureComponent } from 'react'; +import { FieldDisplay, getFieldDisplayValues, PanelProps, VizOrientation } from '@grafana/data'; +import { DataLinksContextMenu, Gauge, VizRepeater, VizRepeaterRenderValueProps } from '@grafana/ui'; +import { DataLinksContextMenuApi } from '@grafana/ui/src/components/DataLinks/DataLinksContextMenu'; -// Services & Utils import { config } from 'app/core/config'; - -// Components -import { Gauge, DataLinksContextMenu, VizRepeater, VizRepeaterRenderValueProps } from '@grafana/ui'; - -// Types import { GaugeOptions } from './types'; -import { FieldDisplay, getFieldDisplayValues, VizOrientation, PanelProps } from '@grafana/data'; export class GaugePanel extends PureComponent> { - renderValue = (valueProps: VizRepeaterRenderValueProps): JSX.Element => { + renderComponent = ( + valueProps: VizRepeaterRenderValueProps, + menuProps: DataLinksContextMenuApi + ): JSX.Element => { const { options } = this.props; const { value, width, height } = valueProps; const { field, display } = value; + const { openMenu, targetClassName } = menuProps; return ( - - {({ openMenu, targetClassName }) => { - return ( - - ); + + ); + }; + + renderValue = (valueProps: VizRepeaterRenderValueProps): JSX.Element => { + const { value } = valueProps; + const { getLinks, hasLinks } = value; + + if (!hasLinks) { + return this.renderComponent(valueProps, {}); + } + + return ( + + {api => { + return this.renderComponent(valueProps, api); }} ); diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 225a42af618..184568ac20c 100644 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -24,21 +24,22 @@ import ReactDOM from 'react-dom'; import { GraphLegendProps, Legend } from './Legend/Legend'; import { GraphCtrl } from './module'; -import { ContextMenuGroup, ContextMenuItem, graphTimeFormatter, graphTimeFormat } from '@grafana/ui'; -import { provideTheme, getCurrentTheme } from 'app/core/utils/ConfigProvider'; +import { ContextMenuGroup, ContextMenuItem, graphTimeFormat, graphTimeFormatter } from '@grafana/ui'; +import { getCurrentTheme, provideTheme } from 'app/core/utils/ConfigProvider'; import { - toUtc, - LinkModelSupplier, + DataFrame, DataFrameView, - getValueFormat, FieldDisplay, + FieldType, + formattedValueToString, getDisplayProcessor, getFlotPairsConstant, - PanelEvents, - formattedValueToString, - FieldType, - DataFrame, getTimeField, + getValueFormat, + hasLinks, + LinkModelSupplier, + PanelEvents, + toUtc, } from '@grafana/data'; import { GraphContextMenuCtrl } from './GraphContextMenuCtrl'; import { TimeSrv } from 'app/features/dashboard/services/TimeSrv'; @@ -259,7 +260,8 @@ class GraphElement { const dataIndex = this.getDataIndexWithNullValuesCorrection(item, dataFrame); let links: any[] = this.panel.options.dataLinks || []; - if (field.config.links && field.config.links.length) { + const hasLinksValue = hasLinks(field); + if (hasLinksValue) { // Append the configured links to the panel datalinks links = [...links, ...field.config.links]; } @@ -280,6 +282,7 @@ class GraphElement { rowIndex: dataIndex, colIndex: item.series.fieldIndex, field: fieldConfig, + hasLinks: hasLinksValue, }) : undefined; } diff --git a/public/app/plugins/panel/stat/StatPanel.tsx b/public/app/plugins/panel/stat/StatPanel.tsx index db4985a6dbb..eae1cb33401 100644 --- a/public/app/plugins/panel/stat/StatPanel.tsx +++ b/public/app/plugins/panel/stat/StatPanel.tsx @@ -1,33 +1,33 @@ -// Libraries import React, { PureComponent } from 'react'; - -// Utils & Services -import { config } from 'app/core/config'; - -// Types -import { StatPanelOptions } from './types'; import { + BigValue, + BigValueGraphMode, + BigValueSparkline, + DataLinksContextMenu, VizRepeater, VizRepeaterRenderValueProps, - BigValue, - DataLinksContextMenu, - BigValueSparkline, - BigValueGraphMode, } from '@grafana/ui'; - import { - PanelProps, - getFieldDisplayValues, - FieldDisplay, - ReducerID, - getDisplayValueAlignmentFactors, DisplayValueAlignmentFactors, + FieldDisplay, + getDisplayValueAlignmentFactors, + getFieldDisplayValues, + PanelProps, + ReducerID, } from '@grafana/data'; +import { config } from 'app/core/config'; +import { StatPanelOptions } from './types'; +import { DataLinksContextMenuApi } from '@grafana/ui/src/components/DataLinks/DataLinksContextMenu'; + export class StatPanel extends PureComponent> { - renderValue = (valueProps: VizRepeaterRenderValueProps): JSX.Element => { + renderComponent = ( + valueProps: VizRepeaterRenderValueProps, + menuProps: DataLinksContextMenuApi + ): JSX.Element => { const { timeRange, options } = this.props; const { value, alignmentFactors, width, height } = valueProps; + const { openMenu, targetClassName } = menuProps; let sparkline: BigValueSparkline | undefined; if (value.sparkline) { @@ -46,23 +46,33 @@ export class StatPanel extends PureComponent> { } return ( - - {({ openMenu, targetClassName }) => { - return ( - - ); + + ); + }; + renderValue = (valueProps: VizRepeaterRenderValueProps): JSX.Element => { + const { value } = valueProps; + const { getLinks, hasLinks } = value; + + if (!hasLinks) { + return this.renderComponent(valueProps, {}); + } + + return ( + + {api => { + return this.renderComponent(valueProps, api); }} );