mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
@@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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' }];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -1,45 +1,58 @@
|
|||||||
// 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 (
|
return (
|
||||||
<DataLinksContextMenu links={value.getLinks}>
|
<BarGauge
|
||||||
{({ openMenu, targetClassName }) => {
|
value={display}
|
||||||
return (
|
width={width}
|
||||||
<BarGauge
|
height={height}
|
||||||
value={display}
|
orientation={orientation}
|
||||||
width={width}
|
field={field}
|
||||||
height={height}
|
display={view?.getFieldDisplayProcessor(colIndex)}
|
||||||
orientation={orientation}
|
theme={config.theme}
|
||||||
field={field}
|
itemSpacing={this.getItemSpacing()}
|
||||||
display={view?.getFieldDisplayProcessor(colIndex)}
|
displayMode={options.displayMode}
|
||||||
theme={config.theme}
|
onClick={openMenu}
|
||||||
itemSpacing={this.getItemSpacing()}
|
className={targetClassName}
|
||||||
displayMode={options.displayMode}
|
alignmentFactors={alignmentFactors}
|
||||||
onClick={openMenu}
|
showUnfilled={options.showUnfilled}
|
||||||
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 (
|
||||||
|
<DataLinksContextMenu links={getLinks}>
|
||||||
|
{api => {
|
||||||
|
return this.renderComponent(valueProps, api);
|
||||||
}}
|
}}
|
||||||
</DataLinksContextMenu>
|
</DataLinksContextMenu>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,38 +1,48 @@
|
|||||||
// 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 (
|
return (
|
||||||
<DataLinksContextMenu links={value.getLinks}>
|
<Gauge
|
||||||
{({ openMenu, targetClassName }) => {
|
value={display}
|
||||||
return (
|
width={width}
|
||||||
<Gauge
|
height={height}
|
||||||
value={display}
|
field={field}
|
||||||
width={width}
|
showThresholdLabels={options.showThresholdLabels}
|
||||||
height={height}
|
showThresholdMarkers={options.showThresholdMarkers}
|
||||||
field={field}
|
theme={config.theme}
|
||||||
showThresholdLabels={options.showThresholdLabels}
|
onClick={openMenu}
|
||||||
showThresholdMarkers={options.showThresholdMarkers}
|
className={targetClassName}
|
||||||
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 (
|
||||||
|
<DataLinksContextMenu links={getLinks}>
|
||||||
|
{api => {
|
||||||
|
return this.renderComponent(valueProps, api);
|
||||||
}}
|
}}
|
||||||
</DataLinksContextMenu>
|
</DataLinksContextMenu>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user