Commit 428b4ae5 by Hugo Häggmark Committed by GitHub

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
parent 9b1c0455
...@@ -7,6 +7,7 @@ import { ...@@ -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 { ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -287,6 +295,7 @@ function createNoValuesFieldDisplay(options: GetFieldDisplayValuesOptions): Fiel
numeric: 0, numeric: 0,
color: display.color, color: display.color,
}, },
hasLinks: false,
}; };
} }
......
...@@ -5,15 +5,16 @@ import { linkModelToContextMenuItems } from '../../utils/dataLinks'; ...@@ -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 const DataLinksContextMenu: React.FC<DataLinksContextMenuProps> = ({ children, links }) => { export interface DataLinksContextMenuApi {
if (!links) { openMenu?: React.MouseEventHandler<HTMLElement>;
return children({}); targetClassName?: string;
} }
export const DataLinksContextMenu: React.FC<DataLinksContextMenuProps> = ({ children, links }) => {
const getDataLinksContextMenuItems = () => { const getDataLinksContextMenuItems = () => {
return [{ items: linkModelToContextMenuItems(links), label: 'Data links' }]; return [{ items: linkModelToContextMenuItems(links), label: 'Data links' }];
}; };
......
...@@ -149,6 +149,7 @@ describe('getLinksFromLogsField', () => { ...@@ -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);
......
// 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, DisplayValueAlignmentFactors,
FieldDisplay, FieldDisplay,
PanelProps,
getDisplayValueAlignmentFactors, getDisplayValueAlignmentFactors,
DisplayValueAlignmentFactors, 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 (
<BarGauge
value={display}
width={width}
height={height}
orientation={orientation}
field={field}
display={view?.getFieldDisplayProcessor(colIndex)}
theme={config.theme}
itemSpacing={this.getItemSpacing()}
displayMode={options.displayMode}
onClick={openMenu}
className={targetClassName}
alignmentFactors={alignmentFactors}
showUnfilled={options.showUnfilled}
/>
);
};
renderValue = (valueProps: VizRepeaterRenderValueProps<FieldDisplay, DisplayValueAlignmentFactors>): JSX.Element => {
const { value } = valueProps;
const { hasLinks, getLinks } = value;
if (!hasLinks) {
return this.renderComponent(valueProps, {});
}
return ( return (
<DataLinksContextMenu links={value.getLinks}> <DataLinksContextMenu links={getLinks}>
{({ openMenu, targetClassName }) => { {api => {
return ( return this.renderComponent(valueProps, api);
<BarGauge
value={display}
width={width}
height={height}
orientation={orientation}
field={field}
display={view?.getFieldDisplayProcessor(colIndex)}
theme={config.theme}
itemSpacing={this.getItemSpacing()}
displayMode={options.displayMode}
onClick={openMenu}
className={targetClassName}
alignmentFactors={alignmentFactors}
showUnfilled={options.showUnfilled}
/>
);
}} }}
</DataLinksContextMenu> </DataLinksContextMenu>
); );
......
// 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 (
<Gauge
value={display}
width={width}
height={height}
field={field}
showThresholdLabels={options.showThresholdLabels}
showThresholdMarkers={options.showThresholdMarkers}
theme={config.theme}
onClick={openMenu}
className={targetClassName}
/>
);
};
renderValue = (valueProps: VizRepeaterRenderValueProps<FieldDisplay>): JSX.Element => {
const { value } = valueProps;
const { getLinks, hasLinks } = value;
if (!hasLinks) {
return this.renderComponent(valueProps, {});
}
return ( return (
<DataLinksContextMenu links={value.getLinks}> <DataLinksContextMenu links={getLinks}>
{({ openMenu, targetClassName }) => { {api => {
return ( return this.renderComponent(valueProps, api);
<Gauge
value={display}
width={width}
height={height}
field={field}
showThresholdLabels={options.showThresholdLabels}
showThresholdMarkers={options.showThresholdMarkers}
theme={config.theme}
onClick={openMenu}
className={targetClassName}
/>
);
}} }}
</DataLinksContextMenu> </DataLinksContextMenu>
); );
......
...@@ -24,21 +24,22 @@ import ReactDOM from 'react-dom'; ...@@ -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 { ...@@ -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 { ...@@ -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;
} }
......
// 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 {
VizRepeater,
VizRepeaterRenderValueProps,
BigValue, BigValue,
DataLinksContextMenu,
BigValueSparkline,
BigValueGraphMode, BigValueGraphMode,
BigValueSparkline,
DataLinksContextMenu,
VizRepeater,
VizRepeaterRenderValueProps,
} from '@grafana/ui'; } from '@grafana/ui';
import { import {
PanelProps, DisplayValueAlignmentFactors,
getFieldDisplayValues,
FieldDisplay, FieldDisplay,
ReducerID,
getDisplayValueAlignmentFactors, getDisplayValueAlignmentFactors,
DisplayValueAlignmentFactors, 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) {
...@@ -46,23 +46,33 @@ export class StatPanel extends PureComponent<PanelProps<StatPanelOptions>> { ...@@ -46,23 +46,33 @@ export class StatPanel extends PureComponent<PanelProps<StatPanelOptions>> {
} }
return ( return (
<DataLinksContextMenu links={value.getLinks}> <BigValue
{({ openMenu, targetClassName }) => { value={value.display}
return ( sparkline={sparkline}
<BigValue colorMode={options.colorMode}
value={value.display} graphMode={options.graphMode}
sparkline={sparkline} justifyMode={options.justifyMode}
colorMode={options.colorMode} alignmentFactors={alignmentFactors}
graphMode={options.graphMode} width={width}
justifyMode={options.justifyMode} height={height}
alignmentFactors={alignmentFactors} theme={config.theme}
width={width} onClick={openMenu}
height={height} className={targetClassName}
theme={config.theme} />
onClick={openMenu} );
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>
); );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment