mirror of https://github.com/grafana/grafana.git
NewGauge: Adds new feature toggle named newGauge (#112593)
* NewGauge: Feature toggle and fixes * Gauge: New migration test dashboard * Update * Updates * Tweaked default barWidth * Fix multi data links * remove sizing options * merge fix * Update * Restore * Update * Tweaked name font size logic * use file snapshots instead of inline --------- Co-authored-by: Paul Marbach <paul.marbach@grafana.com>
This commit is contained in:
parent
17771e0e1d
commit
7df537c9fc
1105
apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_old_to_new.v42.json
vendored
Normal file
1105
apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_old_to_new.v42.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -38,6 +38,7 @@
|
|||
"gauge-multi-series": (import '../dev-dashboards/panel-gauge/gauge-multi-series.json'),
|
||||
"gauge_tests": (import '../dev-dashboards/panel-gauge/gauge_tests.json'),
|
||||
"gauge_tests_new": (import '../dev-dashboards/panel-gauge/gauge_tests_new.json'),
|
||||
"gauge_tests_old_to_new": (import '../dev-dashboards/panel-gauge/gauge_tests_old_to_new.json'),
|
||||
"geomap-color-field": (import '../dev-dashboards/panel-geomap/geomap-color-field.json'),
|
||||
"geomap-photo-layer": (import '../dev-dashboards/panel-geomap/geomap-photo-layer.json'),
|
||||
"geomap-route-layer": (import '../dev-dashboards/panel-geomap/geomap-route-layer.json'),
|
||||
|
|
|
|||
|
|
@ -1218,6 +1218,11 @@ export interface FeatureToggles {
|
|||
*/
|
||||
cdnPluginsUrls?: boolean;
|
||||
/**
|
||||
* Enable new gauge visualization
|
||||
* @default false
|
||||
*/
|
||||
newGauge?: boolean;
|
||||
/**
|
||||
* Restrict PanelChrome contents with overflow: hidden;
|
||||
* @default true
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export interface GaugePanelEffects {
|
|||
|
||||
export const defaultGaugePanelEffects: Partial<GaugePanelEffects> = {
|
||||
barGlow: false,
|
||||
centerGlow: true,
|
||||
centerGlow: false,
|
||||
rounded: false,
|
||||
spotlight: false,
|
||||
};
|
||||
|
|
@ -39,13 +39,13 @@ export interface Options extends common.SingleStatBaseOptions {
|
|||
}
|
||||
|
||||
export const defaultOptions: Partial<Options> = {
|
||||
barWidthFactor: 0.4,
|
||||
barWidthFactor: 0.5,
|
||||
effects: {},
|
||||
gradient: 'none',
|
||||
gradient: 'auto',
|
||||
segmentCount: 1,
|
||||
segmentSpacing: 0.3,
|
||||
shape: 'gauge',
|
||||
showThresholdLabels: false,
|
||||
showThresholdMarkers: true,
|
||||
sparkline: false,
|
||||
sparkline: true,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -73,11 +73,12 @@ export class RadialColorDefs {
|
|||
|
||||
this.defs.push(
|
||||
<radialGradient
|
||||
key={id}
|
||||
id={id}
|
||||
cx={dimensions.centerX}
|
||||
cy={dimensions.centerY}
|
||||
r={dimensions.radius + dimensions.barWidth / 2}
|
||||
fr={dimensions.radius - dimensions.barWidth / 2}
|
||||
id={id}
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0%" stopColor={tinycolor(baseColor).spin(20).lighten(10).toString()} stopOpacity={1} />
|
||||
|
|
@ -85,6 +86,8 @@ export class RadialColorDefs {
|
|||
<stop offset="100%" stopColor={color1.toString()} stopOpacity={1} />
|
||||
</radialGradient>
|
||||
);
|
||||
|
||||
return returnColor;
|
||||
}
|
||||
|
||||
// For fixed / palette based color scales we can create a more fun
|
||||
|
|
@ -103,11 +106,12 @@ export class RadialColorDefs {
|
|||
|
||||
this.defs.push(
|
||||
<linearGradient
|
||||
key={id}
|
||||
id={id}
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2={x2}
|
||||
y2={y2}
|
||||
id={id}
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform={transform}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { css } from '@emotion/css';
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { isNumber } from 'lodash';
|
||||
import { useId } from 'react';
|
||||
|
||||
|
|
@ -64,6 +64,8 @@ export interface RadialGaugeProps {
|
|||
/** Specify which text should be visible */
|
||||
textMode?: RadialTextMode;
|
||||
showScaleLabels?: boolean;
|
||||
/** For data links */
|
||||
onClick?: React.MouseEventHandler<HTMLElement>;
|
||||
}
|
||||
|
||||
export type RadialGradientMode = 'none' | 'auto';
|
||||
|
|
@ -87,6 +89,7 @@ export function RadialGauge(props: RadialGaugeProps) {
|
|||
roundedBars = true,
|
||||
thresholdsBar = false,
|
||||
showScaleLabels = false,
|
||||
onClick,
|
||||
values,
|
||||
} = props;
|
||||
const theme = useTheme2();
|
||||
|
|
@ -247,13 +250,27 @@ export function RadialGauge(props: RadialGaugeProps) {
|
|||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.vizWrapper} style={{ width, height }}>
|
||||
const body = (
|
||||
<>
|
||||
<svg width={width} height={height} role="img" aria-label={t('gauge.category-gauge', 'Gauge')}>
|
||||
<defs>{defs}</defs>
|
||||
{graphics}
|
||||
</svg>
|
||||
{sparklineElement}
|
||||
</>
|
||||
);
|
||||
|
||||
if (onClick) {
|
||||
return (
|
||||
<button onClick={onClick} className={cx(styles.clearButton, styles.vizWrapper)} style={{ width, height }}>
|
||||
{body}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.vizWrapper} style={{ width, height }}>
|
||||
{body}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -281,5 +298,12 @@ function getStyles(theme: GrafanaTheme2) {
|
|||
filter: theme.isLight ? `drop-shadow(0px 0px 1px #888);` : '',
|
||||
},
|
||||
}),
|
||||
clearButton: css({
|
||||
background: 'transparent',
|
||||
color: theme.colors.text.primary,
|
||||
border: 'none',
|
||||
padding: 0,
|
||||
cursor: 'context-menu',
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export function RadialSparkline({ sparkline, dimensions, theme, color, shape }:
|
|||
const height = radius / 4;
|
||||
const widthFactor = shape === 'gauge' ? 1.6 : 1.4;
|
||||
const width = radius * widthFactor - barWidth;
|
||||
const topPos = shape === 'gauge' ? `calc(50% + ${radius / 1.75}px)` : `calc(50% + ${radius / 2.8}px)`;
|
||||
const topPos = shape === 'gauge' ? `${dimensions.gaugeBottomY - height}px` : `calc(50% + ${radius / 2.8}px)`;
|
||||
|
||||
const styles = css({
|
||||
position: 'absolute',
|
||||
|
|
|
|||
|
|
@ -59,9 +59,9 @@ export function RadialText({
|
|||
|
||||
// Not sure where this comes from but svg text is not using body line-height
|
||||
const lineHeight = 1.21;
|
||||
const valueWidthToRadiusFactor = 0.6;
|
||||
const nameToHeightFactor = 0.3;
|
||||
const largeRadiusScalingDecay = 0.92;
|
||||
const valueWidthToRadiusFactor = 0.85;
|
||||
const nameToHeightFactor = 0.45;
|
||||
const largeRadiusScalingDecay = 0.86;
|
||||
|
||||
// This pow 0.92 factor is to create a decay so the font size does not become rediculously large for very large panels
|
||||
let maxValueHeight = valueWidthToRadiusFactor * Math.pow(radius, largeRadiusScalingDecay);
|
||||
|
|
@ -99,7 +99,8 @@ export function RadialText({
|
|||
const nameHeight = nameFontSize * lineHeight;
|
||||
|
||||
const valueY = showName ? centerY - nameHeight / 2 : centerY;
|
||||
const nameY = showValue ? valueY + valueHeight * 0.7 : centerY;
|
||||
const valueNameSpacing = valueHeight / 3.5;
|
||||
const nameY = showValue ? valueY + valueHeight / 2 + valueNameSpacing : centerY;
|
||||
const nameColor = showValue ? theme.colors.text.secondary : theme.colors.text.primary;
|
||||
const suffixShift = (valueFontSize - unitFontSize * 1.2) / 2;
|
||||
|
||||
|
|
|
|||
|
|
@ -40,8 +40,15 @@ export function ThresholdsBar({
|
|||
|
||||
for (let i = 1; i < thresholds.length; i++) {
|
||||
const threshold = thresholds[i];
|
||||
const valueDeg = ((threshold.value - min) / (max - min)) * angleRange;
|
||||
const lengthDeg = valueDeg - currentStart + startAngle;
|
||||
let valueDeg = ((threshold.value - min) / (max - min)) * angleRange;
|
||||
|
||||
if (valueDeg > angleRange) {
|
||||
valueDeg = angleRange;
|
||||
} else if (valueDeg < 0) {
|
||||
valueDeg = 0;
|
||||
}
|
||||
|
||||
let lengthDeg = valueDeg - currentStart + startAngle;
|
||||
|
||||
paths.push(
|
||||
<RadialArcPath
|
||||
|
|
|
|||
|
|
@ -178,5 +178,20 @@ describe('RadialGauge utils', () => {
|
|||
|
||||
expect(result.angle).toBe(360);
|
||||
});
|
||||
|
||||
it('should handle values lower than min', () => {
|
||||
const fieldDisplay = createFieldDisplay(-50, 0, 100);
|
||||
const result = getValueAngleForValue(fieldDisplay, 240, 120);
|
||||
|
||||
expect(result.angle).toBe(0);
|
||||
});
|
||||
|
||||
it('should handle values higher than max', () => {
|
||||
const fieldDisplay = createFieldDisplay(200, 0, 100);
|
||||
const result = getValueAngleForValue(fieldDisplay, 240, 120);
|
||||
|
||||
// Expect the angle to be clamped to the maximum range
|
||||
expect(result.angle).toBe(240);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ export function getValueAngleForValue(fieldDisplay: FieldDisplay, startAngle: nu
|
|||
|
||||
if (angle > angleRange) {
|
||||
angle = angleRange;
|
||||
} else if (angle < 0) {
|
||||
angle = 0;
|
||||
}
|
||||
|
||||
return { angleRange, angle };
|
||||
|
|
@ -39,6 +41,7 @@ export interface GaugeDimensions {
|
|||
scaleLabelsFontSize: number;
|
||||
scaleLabelsSpacing: number;
|
||||
scaleLabelsRadius: number;
|
||||
gaugeBottomY: number;
|
||||
}
|
||||
|
||||
export function calculateDimensions(
|
||||
|
|
@ -119,8 +122,8 @@ export function calculateDimensions(
|
|||
|
||||
let innerRadius = outerRadius - barWidth / 2;
|
||||
|
||||
const maxY = maxRadius * Math.sin(toRad(yMaxAngle)) + maxRadius;
|
||||
const rest = height - maxY - margin * 2;
|
||||
const belowCenterY = maxRadius * Math.sin(toRad(yMaxAngle));
|
||||
const rest = height - belowCenterY - margin * 2 - maxRadius;
|
||||
const centerX = width / 2;
|
||||
const centerY = maxRadius + margin + rest / 2;
|
||||
|
||||
|
|
@ -130,6 +133,7 @@ export function calculateDimensions(
|
|||
|
||||
return {
|
||||
margin,
|
||||
gaugeBottomY: centerY + belowCenterY,
|
||||
radius: innerRadius,
|
||||
centerX,
|
||||
centerY,
|
||||
|
|
|
|||
|
|
@ -2110,6 +2110,14 @@ var (
|
|||
Owner: grafanaPluginsPlatformSquad,
|
||||
Expression: "false",
|
||||
},
|
||||
{
|
||||
Name: "newGauge",
|
||||
Description: "Enable new gauge visualization",
|
||||
Stage: FeatureStageExperimental,
|
||||
FrontendOnly: true,
|
||||
Owner: grafanaDatavizSquad,
|
||||
Expression: "false",
|
||||
},
|
||||
{
|
||||
Name: "preventPanelChromeOverflow",
|
||||
Description: "Restrict PanelChrome contents with overflow: hidden;",
|
||||
|
|
|
|||
|
|
@ -271,6 +271,7 @@ pluginContainers,privatePreview,@grafana/plugins-platform-backend,false,true,fal
|
|||
tempoSearchBackendMigration,GA,@grafana/oss-big-tent,false,true,false
|
||||
cdnPluginsLoadFirst,experimental,@grafana/plugins-platform-backend,false,false,false
|
||||
cdnPluginsUrls,experimental,@grafana/plugins-platform-backend,false,false,false
|
||||
newGauge,experimental,@grafana/dataviz-squad,false,false,true
|
||||
preventPanelChromeOverflow,preview,@grafana/grafana-frontend-platform,false,false,true
|
||||
pluginStoreServiceLoading,experimental,@grafana/plugins-platform-backend,false,false,false
|
||||
onlyStoreActionSets,GA,@grafana/identity-access-team,false,false,false
|
||||
|
|
|
|||
|
|
|
@ -1094,6 +1094,10 @@ const (
|
|||
// Enable loading plugins via declarative URLs
|
||||
FlagCdnPluginsUrls = "cdnPluginsUrls"
|
||||
|
||||
// FlagNewGauge
|
||||
// Enable new gauge visualization
|
||||
FlagNewGauge = "newGauge"
|
||||
|
||||
// FlagPreventPanelChromeOverflow
|
||||
// Restrict PanelChrome contents with overflow: hidden;
|
||||
FlagPreventPanelChromeOverflow = "preventPanelChromeOverflow"
|
||||
|
|
|
|||
|
|
@ -2694,6 +2694,20 @@
|
|||
"expression": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "newGauge",
|
||||
"resourceVersion": "1760700645318",
|
||||
"creationTimestamp": "2025-10-17T11:30:45Z"
|
||||
},
|
||||
"spec": {
|
||||
"description": "Enable new gauge visualization",
|
||||
"stage": "experimental",
|
||||
"codeowner": "@grafana/dataviz-squad",
|
||||
"frontend": true,
|
||||
"expression": "false"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "newInfluxDSConfigPageDesign",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { config } from '@grafana/runtime';
|
||||
|
||||
const cloudwatchPlugin = async () =>
|
||||
await import(/* webpackChunkName: "cloudwatchPlugin" */ 'app/plugins/datasource/cloudwatch/module');
|
||||
const dashboardDSPlugin = async () =>
|
||||
|
|
@ -104,7 +106,7 @@ const builtInPlugins: Record<string, System.Module | (() => Promise<System.Modul
|
|||
'core:plugin/debug': debugPanel,
|
||||
'core:plugin/flamegraph': flamegraphPanel,
|
||||
'core:plugin/gettingstarted': gettingStartedPanel,
|
||||
'core:plugin/gauge': gaugePanel,
|
||||
'core:plugin/gauge': config.featureToggles.newGauge ? radialBar : gaugePanel,
|
||||
'core:plugin/piechart': pieChartPanel,
|
||||
'core:plugin/bargauge': barGaugePanel,
|
||||
'core:plugin/barchart': barChartPanel,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -88,56 +88,7 @@ describe('Gauge Panel Migrations', () => {
|
|||
//@ts-ignore
|
||||
expect(result.reduceOptions.overrides).toBeUndefined();
|
||||
|
||||
expect((panel as PanelModel).fieldConfig).toMatchInlineSnapshot(`
|
||||
{
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds",
|
||||
},
|
||||
"decimals": 3,
|
||||
"mappings": [
|
||||
{
|
||||
"from": "50",
|
||||
"id": 1,
|
||||
"operator": "",
|
||||
"text": "BIG",
|
||||
"to": "1000",
|
||||
"type": 2,
|
||||
"value": "",
|
||||
},
|
||||
],
|
||||
"max": "50",
|
||||
"min": "-50",
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"index": 0,
|
||||
"value": -Infinity,
|
||||
},
|
||||
{
|
||||
"color": "#EAB839",
|
||||
"index": 1,
|
||||
"value": -25,
|
||||
},
|
||||
{
|
||||
"color": "#6ED0E0",
|
||||
"index": 2,
|
||||
"value": 0,
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"index": 3,
|
||||
"value": 25,
|
||||
},
|
||||
],
|
||||
},
|
||||
"unit": "accMS2",
|
||||
},
|
||||
"overrides": [],
|
||||
}
|
||||
`);
|
||||
expect((panel as PanelModel).fieldConfig).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('change from angular singlestat to gauge', () => {
|
||||
|
|
|
|||
|
|
@ -14,3 +14,54 @@ exports[`Gauge Panel Migrations from 6.1.1 1`] = `
|
|||
"showThresholdMarkers": true,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Gauge Panel Migrations from 6.1.1 2`] = `
|
||||
{
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds",
|
||||
},
|
||||
"decimals": 3,
|
||||
"mappings": [
|
||||
{
|
||||
"from": "50",
|
||||
"id": 1,
|
||||
"operator": "",
|
||||
"text": "BIG",
|
||||
"to": "1000",
|
||||
"type": 2,
|
||||
"value": "",
|
||||
},
|
||||
],
|
||||
"max": "50",
|
||||
"min": "-50",
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"index": 0,
|
||||
"value": -Infinity,
|
||||
},
|
||||
{
|
||||
"color": "#EAB839",
|
||||
"index": 1,
|
||||
"value": -25,
|
||||
},
|
||||
{
|
||||
"color": "#6ED0E0",
|
||||
"index": 2,
|
||||
"value": 0,
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"index": 3,
|
||||
"value": 25,
|
||||
},
|
||||
],
|
||||
},
|
||||
"unit": "accMS2",
|
||||
},
|
||||
"overrides": [],
|
||||
}
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,146 @@
|
|||
import { PanelModel } from '@grafana/data';
|
||||
import { FieldColorModeId } from '@grafana/schema/dist/esm/index.gen';
|
||||
|
||||
import { gaugePanelMigrationHandler, gaugePanelChangedHandler } from './GaugeMigrations';
|
||||
|
||||
describe('Gauge Panel Migrations', () => {
|
||||
it('from old gauge', () => {
|
||||
const panel = {
|
||||
id: 2,
|
||||
options: {
|
||||
reduceOptions: {
|
||||
calcs: ['lastNotNull'],
|
||||
},
|
||||
showThresholdLabels: false,
|
||||
showThresholdMarkers: true,
|
||||
},
|
||||
fieldConfig: {
|
||||
defaults: {
|
||||
color: {
|
||||
mode: FieldColorModeId.Fixed,
|
||||
fixedColor: 'blue',
|
||||
},
|
||||
},
|
||||
overrides: [],
|
||||
},
|
||||
pluginVersion: '12.3.0',
|
||||
type: 'gauge',
|
||||
} as Omit<PanelModel, 'fieldConfig'>;
|
||||
|
||||
const result = gaugePanelMigrationHandler(panel as PanelModel);
|
||||
expect(result.showThresholdMarkers).toBe(false);
|
||||
expect(result.sparkline).toBe(false);
|
||||
});
|
||||
|
||||
it('from 6.1.1', () => {
|
||||
const panel = {
|
||||
datasource: '-- Grafana --',
|
||||
gridPos: {
|
||||
h: 9,
|
||||
w: 12,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
id: 2,
|
||||
options: {
|
||||
maxValue: '50',
|
||||
minValue: '-50',
|
||||
orientation: 'auto',
|
||||
showThresholdLabels: true,
|
||||
showThresholdMarkers: true,
|
||||
thresholds: [
|
||||
{
|
||||
color: 'green',
|
||||
index: 0,
|
||||
value: -Infinity,
|
||||
},
|
||||
{
|
||||
color: '#EAB839',
|
||||
index: 1,
|
||||
value: -25,
|
||||
},
|
||||
{
|
||||
color: '#6ED0E0',
|
||||
index: 2,
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
color: 'red',
|
||||
index: 3,
|
||||
value: 25,
|
||||
},
|
||||
],
|
||||
valueMappings: [
|
||||
{
|
||||
id: 1,
|
||||
operator: '',
|
||||
value: '',
|
||||
text: 'BIG',
|
||||
type: 2,
|
||||
from: '50',
|
||||
to: '1000',
|
||||
},
|
||||
],
|
||||
valueOptions: {
|
||||
decimals: 3,
|
||||
prefix: 'XX',
|
||||
stat: 'last',
|
||||
suffix: 'YY',
|
||||
unit: 'accMS2',
|
||||
},
|
||||
},
|
||||
pluginVersion: '6.1.6',
|
||||
targets: [
|
||||
{
|
||||
refId: 'A',
|
||||
},
|
||||
{
|
||||
refId: 'B',
|
||||
},
|
||||
{
|
||||
refId: 'C',
|
||||
},
|
||||
],
|
||||
timeFrom: null,
|
||||
timeShift: null,
|
||||
title: 'Panel Title',
|
||||
type: 'gauge',
|
||||
} as Omit<PanelModel, 'fieldConfig'>;
|
||||
|
||||
const result = gaugePanelMigrationHandler(panel as PanelModel);
|
||||
|
||||
// Ignored due to the API change
|
||||
//@ts-ignore
|
||||
expect(result.reduceOptions.defaults).toBeUndefined();
|
||||
// Ignored due to the API change
|
||||
//@ts-ignore
|
||||
expect(result.reduceOptions.overrides).toBeUndefined();
|
||||
|
||||
expect((panel as PanelModel).fieldConfig).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('change from angular singlestat to gauge', () => {
|
||||
const old = {
|
||||
angular: {
|
||||
format: 'ms',
|
||||
decimals: 7,
|
||||
gauge: {
|
||||
maxValue: 150,
|
||||
minValue: -10,
|
||||
show: true,
|
||||
thresholdLabels: true,
|
||||
thresholdMarkers: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const panel = {} as PanelModel;
|
||||
const newOptions = gaugePanelChangedHandler(panel, 'singlestat', old, { defaults: {}, overrides: [] });
|
||||
expect(panel.fieldConfig.defaults.unit).toBe('ms');
|
||||
expect(panel.fieldConfig.defaults.min).toBe(-10);
|
||||
expect(panel.fieldConfig.defaults.max).toBe(150);
|
||||
expect(panel.fieldConfig.defaults.decimals).toBe(7);
|
||||
expect(newOptions.showThresholdMarkers).toBe(true);
|
||||
expect(newOptions.showThresholdLabels).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
import { PanelModel, PanelTypeChangedHandler } from '@grafana/data';
|
||||
import { FieldColorModeId } from '@grafana/schema/dist/esm/index.gen';
|
||||
import { sharedSingleStatPanelChangedHandler, sharedSingleStatMigrationHandler } from '@grafana/ui';
|
||||
|
||||
import { Options } from './panelcfg.gen';
|
||||
|
||||
// This is called when the panel first loads
|
||||
export function gaugePanelMigrationHandler(panel: PanelModel<Options>): Partial<Options> {
|
||||
const sharedOptions = sharedSingleStatMigrationHandler(panel);
|
||||
const newOptions: Partial<Options> = { ...sharedOptions };
|
||||
|
||||
const previousVersion = parseFloat(panel.pluginVersion || '8');
|
||||
const fieldConfig = panel.fieldConfig;
|
||||
|
||||
if (previousVersion <= 12.3) {
|
||||
// This option had no effect in old gauge unless color mode was 'From thresholds'
|
||||
if (newOptions.showThresholdMarkers && fieldConfig?.defaults?.color?.mode !== FieldColorModeId.Thresholds) {
|
||||
newOptions.showThresholdMarkers = false;
|
||||
}
|
||||
|
||||
// This option is enabled by default in new gauge but does not exist in old gauge
|
||||
newOptions.sparkline = false;
|
||||
|
||||
// Remove deprecated sizing options
|
||||
if ('sizing' in newOptions) {
|
||||
delete newOptions.sizing;
|
||||
}
|
||||
|
||||
if ('minVizHeight' in newOptions) {
|
||||
delete newOptions.minVizHeight;
|
||||
}
|
||||
|
||||
if ('minVizWidth' in newOptions) {
|
||||
delete newOptions.minVizWidth;
|
||||
}
|
||||
}
|
||||
|
||||
return newOptions;
|
||||
}
|
||||
|
||||
export function shouldMigrateGauge(panel: PanelModel): boolean {
|
||||
const previousVersion = parseFloat(panel.pluginVersion ?? '8');
|
||||
return previousVersion <= 12.3;
|
||||
}
|
||||
|
||||
// This is called when the panel changes from another panel
|
||||
export const gaugePanelChangedHandler: PanelTypeChangedHandler<Options> = (
|
||||
panel,
|
||||
prevPluginId: string,
|
||||
prevOptions
|
||||
) => {
|
||||
// This handles most config changes
|
||||
const opts: Options = sharedSingleStatPanelChangedHandler(panel, prevPluginId, prevOptions);
|
||||
|
||||
// Changing from angular singlestat
|
||||
if (prevPluginId === 'singlestat' && prevOptions.angular) {
|
||||
const gauge = prevOptions.angular.gauge;
|
||||
if (gauge) {
|
||||
opts.showThresholdMarkers = gauge.thresholdMarkers;
|
||||
opts.showThresholdLabels = gauge.thresholdLabels;
|
||||
}
|
||||
}
|
||||
return opts;
|
||||
};
|
||||
|
|
@ -47,6 +47,7 @@ export function RadialBarPanel({
|
|||
alignmentFactors={valueProps.alignmentFactors}
|
||||
valueManualFontSize={options.text?.valueSize}
|
||||
nameManualFontSize={options.text?.titleSize}
|
||||
onClick={menuProps.openMenu}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Gauge Panel Migrations from 6.1.1 1`] = `
|
||||
{
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds",
|
||||
},
|
||||
"decimals": 3,
|
||||
"mappings": [
|
||||
{
|
||||
"from": "50",
|
||||
"id": 1,
|
||||
"operator": "",
|
||||
"text": "BIG",
|
||||
"to": "1000",
|
||||
"type": 2,
|
||||
"value": "",
|
||||
},
|
||||
],
|
||||
"max": "50",
|
||||
"min": "-50",
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"index": 0,
|
||||
"value": -Infinity,
|
||||
},
|
||||
{
|
||||
"color": "#EAB839",
|
||||
"index": 1,
|
||||
"value": -25,
|
||||
},
|
||||
{
|
||||
"color": "#6ED0E0",
|
||||
"index": 2,
|
||||
"value": 0,
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"index": 3,
|
||||
"value": 25,
|
||||
},
|
||||
],
|
||||
},
|
||||
"unit": "accMS2",
|
||||
},
|
||||
"overrides": [],
|
||||
}
|
||||
`;
|
||||
|
|
@ -5,6 +5,7 @@ import { commonOptionsBuilder } from '@grafana/ui';
|
|||
import { addOrientationOption, addStandardDataReduceOptions } from '../stat/common';
|
||||
|
||||
import { EffectsEditor } from './EffectsEditor';
|
||||
import { gaugePanelChangedHandler, gaugePanelMigrationHandler, shouldMigrateGauge } from './GaugeMigrations';
|
||||
import { RadialBarPanel } from './RadialBarPanel';
|
||||
import { defaultGaugePanelEffects, defaultOptions, Options } from './panelcfg.gen';
|
||||
import { GaugeSuggestionsSupplier } from './suggestions';
|
||||
|
|
@ -111,4 +112,6 @@ export const plugin = new PanelPlugin<Options>(RadialBarPanel)
|
|||
defaultValue: defaultGaugePanelEffects,
|
||||
});
|
||||
})
|
||||
.setSuggestionsSupplier(new GaugeSuggestionsSupplier());
|
||||
.setSuggestionsSupplier(new GaugeSuggestionsSupplier())
|
||||
.setMigrationHandler(gaugePanelMigrationHandler, shouldMigrateGauge)
|
||||
.setPanelChangeHandler(gaugePanelChangedHandler);
|
||||
|
|
|
|||
|
|
@ -10,3 +10,9 @@
|
|||
Gauge => new gauge migration notes
|
||||
|
||||
Old gauge "Show threshold markers" does nothing when color scheme != From thresholds
|
||||
|
||||
Decide what to do with
|
||||
|
||||
sizing: manual & minVizHeight & minVizWidth (do not think this panel should scroll, and minWidth is broken/does not scroll)
|
||||
neutral value
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ composableKinds: PanelCfg: {
|
|||
barGlow?: bool | *false
|
||||
spotlight?: bool | *false
|
||||
rounded?: bool | *false
|
||||
centerGlow?: bool | *true
|
||||
centerGlow?: bool | *false
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
Options: {
|
||||
|
|
@ -38,10 +38,10 @@ composableKinds: PanelCfg: {
|
|||
showThresholdLabels: bool | *false
|
||||
segmentCount: number | *1
|
||||
segmentSpacing: number | *0.3
|
||||
sparkline?: bool | *false
|
||||
sparkline?: bool | *true
|
||||
shape: "circle" | *"gauge"
|
||||
barWidthFactor: number | *0.4
|
||||
gradient: *"none" | "auto"
|
||||
barWidthFactor: number | *0.5
|
||||
gradient: "none" | *"auto"
|
||||
effects: GaugePanelEffects | *{}
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export interface GaugePanelEffects {
|
|||
|
||||
export const defaultGaugePanelEffects: Partial<GaugePanelEffects> = {
|
||||
barGlow: false,
|
||||
centerGlow: true,
|
||||
centerGlow: false,
|
||||
rounded: false,
|
||||
spotlight: false,
|
||||
};
|
||||
|
|
@ -37,13 +37,13 @@ export interface Options extends common.SingleStatBaseOptions {
|
|||
}
|
||||
|
||||
export const defaultOptions: Partial<Options> = {
|
||||
barWidthFactor: 0.4,
|
||||
barWidthFactor: 0.5,
|
||||
effects: {},
|
||||
gradient: 'none',
|
||||
gradient: 'auto',
|
||||
segmentCount: 1,
|
||||
segmentSpacing: 0.3,
|
||||
shape: 'gauge',
|
||||
showThresholdLabels: false,
|
||||
showThresholdMarkers: true,
|
||||
sparkline: false,
|
||||
sparkline: true,
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue