mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Gauge: Add overflow scrolling support for vertical and horizontal orientations (#71690)
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
parent
69267cd28b
commit
65df5a0d7e
@ -28,6 +28,8 @@ It extends [SingleStatBaseOptions](#singlestatbaseoptions).
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|------------------------|-------------------------------------------------|----------|---------|---------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `minVizHeight` | uint32 | **Yes** | `75` | |
|
||||
| `minVizWidth` | uint32 | **Yes** | `75` | |
|
||||
| `showThresholdLabels` | boolean | **Yes** | `false` | |
|
||||
| `showThresholdMarkers` | boolean | **Yes** | `true` | |
|
||||
| `orientation` | string | No | | *(Inherited from [SingleStatBaseOptions](#singlestatbaseoptions))*<br/>TODO docs<br/>Possible values are: `auto`, `vertical`, `horizontal`. |
|
||||
|
@ -14,11 +14,15 @@ import * as common from '@grafana/schema';
|
||||
export const pluginVersion = "10.2.0-pre";
|
||||
|
||||
export interface Options extends common.SingleStatBaseOptions {
|
||||
minVizHeight: number;
|
||||
minVizWidth: number;
|
||||
showThresholdLabels: boolean;
|
||||
showThresholdMarkers: boolean;
|
||||
}
|
||||
|
||||
export const defaultOptions: Partial<Options> = {
|
||||
minVizHeight: 75,
|
||||
minVizWidth: 75,
|
||||
showThresholdLabels: false,
|
||||
showThresholdMarkers: true,
|
||||
};
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
GAUGE_DEFAULT_MINIMUM,
|
||||
GrafanaTheme2,
|
||||
} from '@grafana/data';
|
||||
import { VizTextDisplayOptions } from '@grafana/schema';
|
||||
import { VizTextDisplayOptions, VizOrientation } from '@grafana/schema';
|
||||
|
||||
import { calculateFontSize } from '../../utils/measureText';
|
||||
import { clearButtonStyles } from '../Button';
|
||||
@ -28,6 +28,7 @@ export interface Props {
|
||||
onClick?: React.MouseEventHandler<HTMLElement>;
|
||||
className?: string;
|
||||
theme: GrafanaTheme2;
|
||||
orientation?: VizOrientation;
|
||||
}
|
||||
|
||||
export class Gauge extends PureComponent<Props> {
|
||||
@ -52,17 +53,19 @@ export class Gauge extends PureComponent<Props> {
|
||||
}
|
||||
|
||||
draw() {
|
||||
const { field, showThresholdLabels, showThresholdMarkers, width, height, theme, value } = this.props;
|
||||
const { field, showThresholdLabels, showThresholdMarkers, width, height, theme, value, orientation } = this.props;
|
||||
|
||||
const autoProps = calculateGaugeAutoProps(width, height, value.title);
|
||||
const dimension = Math.min(width, autoProps.gaugeHeight);
|
||||
// If the gauge is in vertical layout, we need to set the width of the gauge to the height of the gauge
|
||||
const calculatedGaugeWidth = orientation === VizOrientation.Vertical ? autoProps.gaugeHeight : width;
|
||||
const dimension = Math.min(calculatedGaugeWidth, autoProps.gaugeHeight);
|
||||
const backgroundColor = theme.colors.background.secondary;
|
||||
const gaugeWidthReduceRatio = showThresholdLabels ? 1.5 : 1;
|
||||
const gaugeWidth = Math.min(dimension / 5.5, 40) / gaugeWidthReduceRatio;
|
||||
const thresholdMarkersWidth = gaugeWidth / 5;
|
||||
const text = formattedValueToString(value);
|
||||
// This not 100% accurate as I am unsure of flot's calculations here
|
||||
const valueWidthBase = Math.min(width, dimension * 1.3) * 0.9;
|
||||
const valueWidthBase = Math.min(calculatedGaugeWidth, dimension * 1.3) * 0.9;
|
||||
// remove gauge & marker width (on left and right side)
|
||||
// and 10px is some padding that flot adds to the outer canvas
|
||||
const valueWidth =
|
||||
@ -145,11 +148,15 @@ export class Gauge extends PureComponent<Props> {
|
||||
}
|
||||
|
||||
renderVisualization = () => {
|
||||
const { width, value, height, onClick, text, theme } = this.props;
|
||||
const autoProps = calculateGaugeAutoProps(width, height, value.title);
|
||||
const { width, value, height, onClick, text, theme, orientation } = this.props;
|
||||
const autoProps = calculateGaugeAutoProps(width, height, value.title, orientation);
|
||||
|
||||
// If the gauge is in vertical layout, we need to set the width of the gauge to the height of the gauge
|
||||
const gaugeWidth = orientation === VizOrientation.Vertical ? `${autoProps.gaugeHeight}px` : '100%';
|
||||
|
||||
const gaugeElement = (
|
||||
<div
|
||||
style={{ height: `${autoProps.gaugeHeight}px`, width: '100%' }}
|
||||
style={{ height: `${autoProps.gaugeHeight}px`, width: gaugeWidth }}
|
||||
ref={(element) => (this.canvasElement = element)}
|
||||
/>
|
||||
);
|
||||
@ -172,7 +179,7 @@ export class Gauge extends PureComponent<Props> {
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
width: gaugeWidth,
|
||||
top: '-4px',
|
||||
cursor: 'default',
|
||||
}}
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { FieldColorModeId, FieldConfig, ThresholdsMode } from '@grafana/data';
|
||||
import { VizOrientation } from '@grafana/schema';
|
||||
|
||||
import { getTheme } from '../../themes';
|
||||
|
||||
import { getFormattedThresholds } from './utils';
|
||||
import { calculateGaugeAutoProps, getFormattedThresholds } from './utils';
|
||||
|
||||
describe('getFormattedThresholds', () => {
|
||||
const value = {
|
||||
@ -53,3 +54,49 @@ describe('getFormattedThresholds', () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('calculateGaugeAutoProps', () => {
|
||||
it('should calculate gauge properties correctly when title is undefined', () => {
|
||||
const width = 200;
|
||||
const height = 300;
|
||||
const orientation = VizOrientation.Horizontal;
|
||||
const title = undefined;
|
||||
|
||||
const result = calculateGaugeAutoProps(width, height, title, orientation);
|
||||
|
||||
expect(result.showLabel).toBe(false);
|
||||
expect(result.gaugeHeight).toBe(200);
|
||||
expect(result.titleFontSize).toBe(20);
|
||||
});
|
||||
|
||||
it('should calculate gauge properties correctly when title is defined', () => {
|
||||
const width = 200;
|
||||
const height = 300;
|
||||
const orientation = VizOrientation.Vertical;
|
||||
const title = 'My Gauge';
|
||||
|
||||
const result = calculateGaugeAutoProps(width, height, title, orientation);
|
||||
|
||||
expect(result.showLabel).toBe(true);
|
||||
expect(result.gaugeHeight).toBe(200);
|
||||
expect(result.titleFontSize).toBe(20);
|
||||
});
|
||||
|
||||
it('should calculate gauge properties correctly for vertical and horizontal orientations', () => {
|
||||
const width = 100;
|
||||
const height = 150;
|
||||
const title = 'My Gauge';
|
||||
|
||||
// Test for vertical orientation
|
||||
const verticalResult = calculateGaugeAutoProps(width, height, title, VizOrientation.Vertical);
|
||||
expect(verticalResult.showLabel).toBe(true);
|
||||
expect(verticalResult.gaugeHeight).toBe(100);
|
||||
expect(verticalResult.titleFontSize).toBe(15);
|
||||
|
||||
// Test for horizontal orientation
|
||||
const horizontalResult = calculateGaugeAutoProps(width, height, title, VizOrientation.Horizontal);
|
||||
expect(horizontalResult.showLabel).toBe(true);
|
||||
expect(horizontalResult.gaugeHeight).toBe(100);
|
||||
expect(horizontalResult.titleFontSize).toBe(10);
|
||||
});
|
||||
});
|
||||
|
@ -12,6 +12,7 @@ import {
|
||||
ThresholdsConfig,
|
||||
ThresholdsMode,
|
||||
} from '@grafana/data';
|
||||
import { VizOrientation } from '@grafana/schema';
|
||||
|
||||
interface GaugeAutoProps {
|
||||
titleFontSize: number;
|
||||
@ -27,9 +28,15 @@ export const DEFAULT_THRESHOLDS: ThresholdsConfig = {
|
||||
],
|
||||
};
|
||||
|
||||
export function calculateGaugeAutoProps(width: number, height: number, title: string | undefined): GaugeAutoProps {
|
||||
export function calculateGaugeAutoProps(
|
||||
width: number,
|
||||
height: number,
|
||||
title: string | undefined,
|
||||
orientation?: VizOrientation
|
||||
): GaugeAutoProps {
|
||||
const showLabel = title !== null && title !== undefined;
|
||||
const titleFontSize = Math.min((width * 0.15) / 1.5, 20); // 20% of height * line-height, max 40px
|
||||
const titleFontSizeDimension = orientation === VizOrientation.Vertical ? height : width;
|
||||
const titleFontSize = Math.min((titleFontSizeDimension * 0.15) / 1.5, 20); // 20% of height * line-height, max 40px
|
||||
const titleHeight = titleFontSize * 1.5;
|
||||
const availableHeight = showLabel ? height - titleHeight : height;
|
||||
const gaugeHeight = Math.min(availableHeight, width);
|
||||
|
@ -31,6 +31,7 @@ export class GaugePanel extends PureComponent<PanelProps<Options>> {
|
||||
theme={config.theme2}
|
||||
onClick={openMenu}
|
||||
className={targetClassName}
|
||||
orientation={options.orientation}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -90,6 +91,8 @@ export class GaugePanel extends PureComponent<PanelProps<Options>> {
|
||||
autoGrid={true}
|
||||
renderCounter={renderCounter}
|
||||
orientation={options.orientation}
|
||||
minVizHeight={options.minVizHeight}
|
||||
minVizWidth={options.minVizWidth}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { PanelPlugin } from '@grafana/data';
|
||||
import { VizOrientation } from '@grafana/schema';
|
||||
import { commonOptionsBuilder } from '@grafana/ui';
|
||||
|
||||
import { addOrientationOption, addStandardDataReduceOptions } from '../stat/common';
|
||||
@ -37,6 +38,20 @@ export const plugin = new PanelPlugin<Options>(GaugePanel)
|
||||
name: 'Show threshold markers',
|
||||
description: 'Renders the thresholds as an outer bar',
|
||||
defaultValue: defaultOptions.showThresholdMarkers,
|
||||
})
|
||||
.addNumberInput({
|
||||
path: 'minVizWidth',
|
||||
name: 'Min width',
|
||||
description: 'Minimum column width',
|
||||
defaultValue: defaultOptions.minVizWidth,
|
||||
showIf: (options: Options) => options.orientation === VizOrientation.Vertical,
|
||||
})
|
||||
.addNumberInput({
|
||||
path: 'minVizHeight',
|
||||
name: 'Min height',
|
||||
description: 'Minimum row height',
|
||||
defaultValue: defaultOptions.minVizHeight,
|
||||
showIf: (options: Options) => options.orientation === VizOrientation.Horizontal,
|
||||
});
|
||||
|
||||
commonOptionsBuilder.addTextSizeOptions(builder);
|
||||
|
@ -29,6 +29,8 @@ composableKinds: PanelCfg: {
|
||||
common.SingleStatBaseOptions
|
||||
showThresholdLabels: bool | *false
|
||||
showThresholdMarkers: bool | *true
|
||||
minVizWidth: uint32 | *75
|
||||
minVizHeight: uint32 | *75
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
}]
|
||||
|
@ -11,11 +11,15 @@
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export interface Options extends common.SingleStatBaseOptions {
|
||||
minVizHeight: number;
|
||||
minVizWidth: number;
|
||||
showThresholdLabels: boolean;
|
||||
showThresholdMarkers: boolean;
|
||||
}
|
||||
|
||||
export const defaultOptions: Partial<Options> = {
|
||||
minVizHeight: 75,
|
||||
minVizWidth: 75,
|
||||
showThresholdLabels: false,
|
||||
showThresholdMarkers: true,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user