Stat/Gauge/BarGauge: shows default cursor when missing links (#24284)

* Stat/Gauge/BarGauge: shows default cursor when missing links

* Stat/Gauge/BarGauge: shows default cursor when missing links

* Refactor: changes after PR comments
This commit is contained in:
Hugo Häggmark
2020-05-05 15:26:42 +02:00
committed by GitHub
parent 9b1c0455c4
commit 428b4ae565
7 changed files with 153 additions and 106 deletions

View File

@@ -7,6 +7,7 @@ import {
DataFrame, DataFrame,
DisplayValue, DisplayValue,
DisplayValueAlignmentFactors, DisplayValueAlignmentFactors,
Field,
FieldConfig, FieldConfig,
FieldConfigSource, FieldConfigSource,
FieldType, FieldType,
@@ -80,6 +81,7 @@ export interface FieldDisplay {
colIndex?: number; // The field column index colIndex?: number; // The field column index
rowIndex?: number; // only filled in when the value is from a row (ie, not a reduction) rowIndex?: number; // only filled in when the value is from a row (ie, not a reduction)
getLinks?: () => LinkModel[]; getLinks?: () => LinkModel[];
hasLinks: boolean;
} }
export interface GetFieldDisplayValuesOptions { export interface GetFieldDisplayValuesOptions {
@@ -168,6 +170,7 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi
valueRowIndex: j, valueRowIndex: j,
}) })
: () => [], : () => [],
hasLinks: hasLinks(field),
}); });
if (values.length >= limit) { if (values.length >= limit) {
@@ -210,6 +213,7 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi
calculatedValue: displayValue, calculatedValue: displayValue,
}) })
: () => [], : () => [],
hasLinks: hasLinks(field),
}); });
} }
} }
@@ -227,6 +231,10 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi
return values; 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 { export function getDisplayValueAlignmentFactors(values: FieldDisplay[]): DisplayValueAlignmentFactors {
const info: DisplayValueAlignmentFactors = { const info: DisplayValueAlignmentFactors = {
title: '', title: '',
@@ -287,6 +295,7 @@ function createNoValuesFieldDisplay(options: GetFieldDisplayValuesOptions): Fiel
numeric: 0, numeric: 0,
color: display.color, color: display.color,
}, },
hasLinks: false,
}; };
} }

View File

@@ -5,15 +5,16 @@ import { linkModelToContextMenuItems } from '../../utils/dataLinks';
import { css } from 'emotion'; import { css } from 'emotion';
interface DataLinksContextMenuProps { interface DataLinksContextMenuProps {
children: (props: { openMenu?: React.MouseEventHandler<HTMLElement>; targetClassName?: string }) => JSX.Element; children: (props: DataLinksContextMenuApi) => JSX.Element;
links?: () => LinkModel[]; links: () => LinkModel[];
}
export interface DataLinksContextMenuApi {
openMenu?: React.MouseEventHandler<HTMLElement>;
targetClassName?: string;
} }
export const DataLinksContextMenu: React.FC<DataLinksContextMenuProps> = ({ children, links }) => { export const DataLinksContextMenu: React.FC<DataLinksContextMenuProps> = ({ children, links }) => {
if (!links) {
return children({});
}
const getDataLinksContextMenuItems = () => { const getDataLinksContextMenuItems = () => {
return [{ items: linkModelToContextMenuItems(links), label: 'Data links' }]; return [{ items: linkModelToContextMenuItems(links), label: 'Data links' }];
}; };

View File

@@ -149,6 +149,7 @@ describe('getLinksFromLogsField', () => {
rowIndex, rowIndex,
colIndex, colIndex,
display: field.display!(field.values.get(rowIndex)), display: field.display!(field.values.get(rowIndex)),
hasLinks: true,
}; };
const supplier = getFieldLinksSupplier(fieldDisp); const supplier = getFieldLinksSupplier(fieldDisp);

View File

@@ -1,28 +1,27 @@
// Libraries
import React, { PureComponent } from 'react'; 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 { import {
getFieldDisplayValues,
FieldDisplay,
PanelProps,
getDisplayValueAlignmentFactors,
DisplayValueAlignmentFactors, DisplayValueAlignmentFactors,
FieldDisplay,
getDisplayValueAlignmentFactors,
getFieldDisplayValues,
PanelProps,
} from '@grafana/data'; } 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<PanelProps<BarGaugeOptions>> { export class BarGaugePanel extends PureComponent<PanelProps<BarGaugeOptions>> {
renderValue = (valueProps: VizRepeaterRenderValueProps<FieldDisplay, DisplayValueAlignmentFactors>): JSX.Element => { renderComponent = (
valueProps: VizRepeaterRenderValueProps<FieldDisplay, DisplayValueAlignmentFactors>,
menuProps: DataLinksContextMenuApi
): JSX.Element => {
const { options } = this.props; const { options } = this.props;
const { value, alignmentFactors, orientation, width, height } = valueProps; const { value, alignmentFactors, orientation, width, height } = valueProps;
const { field, display, view, colIndex } = value; const { field, display, view, colIndex } = value;
const { openMenu, targetClassName } = menuProps;
return (
<DataLinksContextMenu links={value.getLinks}>
{({ openMenu, targetClassName }) => {
return ( return (
<BarGauge <BarGauge
value={display} value={display}
@@ -40,6 +39,20 @@ export class BarGaugePanel extends PureComponent<PanelProps<BarGaugeOptions>> {
showUnfilled={options.showUnfilled} showUnfilled={options.showUnfilled}
/> />
); );
};
renderValue = (valueProps: VizRepeaterRenderValueProps<FieldDisplay, DisplayValueAlignmentFactors>): JSX.Element => {
const { value } = valueProps;
const { hasLinks, getLinks } = value;
if (!hasLinks) {
return this.renderComponent(valueProps, {});
}
return (
<DataLinksContextMenu links={getLinks}>
{api => {
return this.renderComponent(valueProps, api);
}} }}
</DataLinksContextMenu> </DataLinksContextMenu>
); );

View File

@@ -1,25 +1,21 @@
// Libraries
import React, { PureComponent } from 'react'; 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'; import { config } from 'app/core/config';
// Components
import { Gauge, DataLinksContextMenu, VizRepeater, VizRepeaterRenderValueProps } from '@grafana/ui';
// Types
import { GaugeOptions } from './types'; import { GaugeOptions } from './types';
import { FieldDisplay, getFieldDisplayValues, VizOrientation, PanelProps } from '@grafana/data';
export class GaugePanel extends PureComponent<PanelProps<GaugeOptions>> { export class GaugePanel extends PureComponent<PanelProps<GaugeOptions>> {
renderValue = (valueProps: VizRepeaterRenderValueProps<FieldDisplay>): JSX.Element => { renderComponent = (
valueProps: VizRepeaterRenderValueProps<FieldDisplay>,
menuProps: DataLinksContextMenuApi
): JSX.Element => {
const { options } = this.props; const { options } = this.props;
const { value, width, height } = valueProps; const { value, width, height } = valueProps;
const { field, display } = value; const { field, display } = value;
const { openMenu, targetClassName } = menuProps;
return (
<DataLinksContextMenu links={value.getLinks}>
{({ openMenu, targetClassName }) => {
return ( return (
<Gauge <Gauge
value={display} value={display}
@@ -33,6 +29,20 @@ export class GaugePanel extends PureComponent<PanelProps<GaugeOptions>> {
className={targetClassName} className={targetClassName}
/> />
); );
};
renderValue = (valueProps: VizRepeaterRenderValueProps<FieldDisplay>): JSX.Element => {
const { value } = valueProps;
const { getLinks, hasLinks } = value;
if (!hasLinks) {
return this.renderComponent(valueProps, {});
}
return (
<DataLinksContextMenu links={getLinks}>
{api => {
return this.renderComponent(valueProps, api);
}} }}
</DataLinksContextMenu> </DataLinksContextMenu>
); );

View File

@@ -24,21 +24,22 @@ import ReactDOM from 'react-dom';
import { GraphLegendProps, Legend } from './Legend/Legend'; import { GraphLegendProps, Legend } from './Legend/Legend';
import { GraphCtrl } from './module'; import { GraphCtrl } from './module';
import { ContextMenuGroup, ContextMenuItem, graphTimeFormatter, graphTimeFormat } from '@grafana/ui'; import { ContextMenuGroup, ContextMenuItem, graphTimeFormat, graphTimeFormatter } from '@grafana/ui';
import { provideTheme, getCurrentTheme } from 'app/core/utils/ConfigProvider'; import { getCurrentTheme, provideTheme } from 'app/core/utils/ConfigProvider';
import { import {
toUtc, DataFrame,
LinkModelSupplier,
DataFrameView, DataFrameView,
getValueFormat,
FieldDisplay, FieldDisplay,
FieldType,
formattedValueToString,
getDisplayProcessor, getDisplayProcessor,
getFlotPairsConstant, getFlotPairsConstant,
PanelEvents,
formattedValueToString,
FieldType,
DataFrame,
getTimeField, getTimeField,
getValueFormat,
hasLinks,
LinkModelSupplier,
PanelEvents,
toUtc,
} from '@grafana/data'; } from '@grafana/data';
import { GraphContextMenuCtrl } from './GraphContextMenuCtrl'; import { GraphContextMenuCtrl } from './GraphContextMenuCtrl';
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
@@ -259,7 +260,8 @@ class GraphElement {
const dataIndex = this.getDataIndexWithNullValuesCorrection(item, dataFrame); const dataIndex = this.getDataIndexWithNullValuesCorrection(item, dataFrame);
let links: any[] = this.panel.options.dataLinks || []; 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 // Append the configured links to the panel datalinks
links = [...links, ...field.config.links]; links = [...links, ...field.config.links];
} }
@@ -280,6 +282,7 @@ class GraphElement {
rowIndex: dataIndex, rowIndex: dataIndex,
colIndex: item.series.fieldIndex, colIndex: item.series.fieldIndex,
field: fieldConfig, field: fieldConfig,
hasLinks: hasLinksValue,
}) })
: undefined; : undefined;
} }

View File

@@ -1,33 +1,33 @@
// Libraries
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
// Utils & Services
import { config } from 'app/core/config';
// Types
import { StatPanelOptions } from './types';
import { import {
BigValue,
BigValueGraphMode,
BigValueSparkline,
DataLinksContextMenu,
VizRepeater, VizRepeater,
VizRepeaterRenderValueProps, VizRepeaterRenderValueProps,
BigValue,
DataLinksContextMenu,
BigValueSparkline,
BigValueGraphMode,
} from '@grafana/ui'; } from '@grafana/ui';
import { import {
PanelProps,
getFieldDisplayValues,
FieldDisplay,
ReducerID,
getDisplayValueAlignmentFactors,
DisplayValueAlignmentFactors, DisplayValueAlignmentFactors,
FieldDisplay,
getDisplayValueAlignmentFactors,
getFieldDisplayValues,
PanelProps,
ReducerID,
} from '@grafana/data'; } 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<PanelProps<StatPanelOptions>> { export class StatPanel extends PureComponent<PanelProps<StatPanelOptions>> {
renderValue = (valueProps: VizRepeaterRenderValueProps<FieldDisplay, DisplayValueAlignmentFactors>): JSX.Element => { renderComponent = (
valueProps: VizRepeaterRenderValueProps<FieldDisplay, DisplayValueAlignmentFactors>,
menuProps: DataLinksContextMenuApi
): JSX.Element => {
const { timeRange, options } = this.props; const { timeRange, options } = this.props;
const { value, alignmentFactors, width, height } = valueProps; const { value, alignmentFactors, width, height } = valueProps;
const { openMenu, targetClassName } = menuProps;
let sparkline: BigValueSparkline | undefined; let sparkline: BigValueSparkline | undefined;
if (value.sparkline) { if (value.sparkline) {
@@ -45,9 +45,6 @@ export class StatPanel extends PureComponent<PanelProps<StatPanelOptions>> {
} }
} }
return (
<DataLinksContextMenu links={value.getLinks}>
{({ openMenu, targetClassName }) => {
return ( return (
<BigValue <BigValue
value={value.display} value={value.display}
@@ -63,6 +60,19 @@ export class StatPanel extends PureComponent<PanelProps<StatPanelOptions>> {
className={targetClassName} className={targetClassName}
/> />
); );
};
renderValue = (valueProps: VizRepeaterRenderValueProps<FieldDisplay, DisplayValueAlignmentFactors>): JSX.Element => {
const { value } = valueProps;
const { getLinks, hasLinks } = value;
if (!hasLinks) {
return this.renderComponent(valueProps, {});
}
return (
<DataLinksContextMenu links={getLinks}>
{api => {
return this.renderComponent(valueProps, api);
}} }}
</DataLinksContextMenu> </DataLinksContextMenu>
); );