Table: Ensure that SparklineCells honor configured mins and maxes (#107991)

* Table: ensure that FieldType.frame fields have mins and maxes applied

* update Sparklines gdev dashboard

* update gdev dashboards for table

* jsonnet spacing

* fix gdev dashboard ids and versions
This commit is contained in:
Paul Marbach
2025-07-14 21:41:32 +00:00
committed by GitHub
parent 58c990b2ca
commit 6c907fd955
6 changed files with 594 additions and 35 deletions
@@ -2662,9 +2662,10 @@
}
],
"schemaVersion": 32,
"tags": [
"gdev",
"panel-tests"
"tags": [
"gdev",
"panel-tests",
"table"
],
"templating": {
"list": []
@@ -2675,8 +2676,8 @@
},
"timepicker": {},
"timezone": "",
"title": "Table panel - Pagination",
"title": "Panel Tests - Table - Pagination",
"uid": "1YGJxPUnz",
"version": 2,
"version": 3,
"weekStart": ""
}
}
@@ -18,22 +18,27 @@
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 1734,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": { "type": "testdata" },
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
"fixedColor": "blue",
"mode": "shades"
},
"custom": {
"align": "auto",
"cellOptions": {
"type": "auto"
"type": "sparkline"
},
"inspect": false
"inspect": false,
"wrapHeaderText": false
},
"mappings": [],
"thresholds": {
@@ -41,7 +46,7 @@
"steps": [
{
"color": "green",
"value": null
"value": 0
},
{
"color": "red",
@@ -53,8 +58,8 @@
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"h": 16,
"w": 8,
"x": 0,
"y": 0
},
@@ -71,14 +76,507 @@
},
"showHeader": true
},
"pluginVersion": "10.1.0-pre",
"pluginVersion": "12.1.0-pre",
"targets": [
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "ca3da78e-6bcc-4a2a-a080-d338a14528ca"
},
"refId": "A",
"scenarioId": "random_walk",
"seriesCount": 31
}
],
"title": "Sparkline (lines)",
"transformations": [
{
"id": "timeSeriesTable",
"options": {
"A": {
"timeField": "time"
}
}
}
],
"type": "table"
},
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"fieldConfig": {
"defaults": {
"color": {
"fixedColor": "blue",
"mode": "shades"
},
"custom": {
"align": "auto",
"cellOptions": {
"drawStyle": "bars",
"type": "sparkline"
},
"inspect": false,
"wrapHeaderText": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 16,
"w": 8,
"x": 8,
"y": 0
},
"id": 2,
"maxDataPoints": 60,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "12.1.0-pre",
"targets": [
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "ca3da78e-6bcc-4a2a-a080-d338a14528ca"
},
"refId": "A",
"scenarioId": "random_walk",
"seriesCount": 31
}
],
"title": "Sparklines (bars)",
"transformations": [
{
"id": "timeSeriesTable",
"options": {
"A": {
"timeField": "time"
}
}
}
],
"type": "table"
},
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"fieldConfig": {
"defaults": {
"color": {
"fixedColor": "blue",
"mode": "shades"
},
"custom": {
"align": "auto",
"cellOptions": {
"drawStyle": "points",
"type": "sparkline"
},
"inspect": false,
"wrapHeaderText": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 16,
"w": 8,
"x": 16,
"y": 0
},
"id": 3,
"maxDataPoints": 100,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "12.1.0-pre",
"targets": [
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "ca3da78e-6bcc-4a2a-a080-d338a14528ca"
},
"refId": "A",
"scenarioId": "random_walk",
"seriesCount": 31
}
],
"title": "Sparklines (points)",
"transformations": [
{
"id": "timeSeriesTable",
"options": {
"A": {
"timeField": "time"
}
}
}
],
"type": "table"
},
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"fieldConfig": {
"defaults": {
"color": {
"fixedColor": "green",
"mode": "shades"
},
"custom": {
"align": "auto",
"cellOptions": {
"drawStyle": "line",
"type": "sparkline"
},
"inspect": false,
"wrapHeaderText": false
},
"mappings": [],
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
},
{
"color": "red",
"value": 80
}
]
},
"unit": "areaF2"
},
"overrides": []
},
"gridPos": {
"h": 16,
"w": 8,
"x": 0,
"y": 16
},
"id": 4,
"maxDataPoints": 100,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "12.1.0-pre",
"targets": [
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "ca3da78e-6bcc-4a2a-a080-d338a14528ca"
},
"refId": "A",
"scenarioId": "random_walk",
"seriesCount": 31
}
],
"title": "Sparklines (min=0)",
"transformations": [
{
"id": "timeSeriesTable",
"options": {
"A": {
"timeField": "time"
}
}
}
],
"type": "table"
},
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"fieldConfig": {
"defaults": {
"color": {
"fixedColor": "yellow",
"mode": "shades"
},
"custom": {
"align": "auto",
"cellOptions": {
"drawStyle": "line",
"type": "sparkline"
},
"inspect": false,
"wrapHeaderText": false
},
"mappings": [],
"max": 100,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
},
{
"color": "red",
"value": 80
}
]
},
"unit": "areaF2"
},
"overrides": []
},
"gridPos": {
"h": 16,
"w": 8,
"x": 8,
"y": 16
},
"id": 5,
"maxDataPoints": 100,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "12.1.0-pre",
"targets": [
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "ca3da78e-6bcc-4a2a-a080-d338a14528ca"
},
"refId": "A",
"scenarioId": "random_walk",
"seriesCount": 31
}
],
"title": "Sparklines (max=100)",
"transformations": [
{
"id": "timeSeriesTable",
"options": {
"A": {
"timeField": "time"
}
}
}
],
"type": "table"
},
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"fieldConfig": {
"defaults": {
"color": {
"fixedColor": "purple",
"mode": "shades"
},
"custom": {
"align": "auto",
"cellOptions": {
"drawStyle": "line",
"lineInterpolation": "linear",
"type": "sparkline"
},
"inspect": false,
"wrapHeaderText": false
},
"mappings": [],
"max": 100,
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
},
{
"color": "red",
"value": 80
}
]
},
"unit": "areaF2"
},
"overrides": []
},
"gridPos": {
"h": 16,
"w": 8,
"x": 16,
"y": 16
},
"id": 6,
"maxDataPoints": 100,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "12.1.0-pre",
"targets": [
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "ca3da78e-6bcc-4a2a-a080-d338a14528ca"
},
"refId": "A",
"scenarioId": "random_walk",
"seriesCount": 31
}
],
"title": "Sparklines (min=0, max=100)",
"transformations": [
{
"id": "timeSeriesTable",
"options": {
"A": {
"timeField": "time"
}
}
}
],
"type": "table"
},
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"cellOptions": {
"type": "auto"
},
"inspect": false,
"wrapHeaderText": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 8,
"x": 0,
"y": 32
},
"id": 7,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "12.1.0-pre",
"targets": [
{
"csvContent": "time,x\n2023-06-12T05:49:00Z,10\n2023-06-12T07:01:00Z,20\n2023-06-12T08:13:00Z,NaN\n2023-06-12T09:25:00Z,NaN\n2023-06-12T10:37:00Z,NaN\n2023-06-12T11:49:00Z,30\n2023-06-12T13:01:00Z,20\n2023-06-12T14:13:00Z,10\n2023-06-12T15:25:00Z,Inf\n2023-06-12T16:37:00Z,null\n2023-06-12T17:49:00Z,",
"datasource": {
"type": "testdata",
"uid": "PD8C576611E62080A"
},
"refId": "A",
"scenarioId": "csv_content"
}
@@ -87,26 +585,31 @@
"transformations": [
{
"id": "timeSeriesTable",
"options": {}
"options": {
"A": {
"stat": "last",
"timeField": ""
}
}
}
],
"type": "table"
}
],
"preload": false,
"refresh": "",
"schemaVersion": 38,
"schemaVersion": 41,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "2023-06-12T00:00:00.000Z",
"to": "2023-06-12T18:00:00.000Z"
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "utc",
"title": "Table with SparklineCell",
"timezone": "",
"title": "Panel Tests - Table - Sparklines",
"uid": "d6373b49-1957-4f00-9218-ee2120d3ecd9",
"version": 4,
"weekStart": ""
}
"version": 5
}
@@ -431,7 +431,11 @@
],
"refresh": false,
"schemaVersion": 16,
"tags": ["gdev", "panel-tests"],
"tags": [
"gdev",
"panel-tests",
"table"
],
"templating": {
"list": []
},
@@ -445,5 +449,5 @@
"timezone": "browser",
"title": "Panel Tests - Table",
"uid": "pttable",
"version": 2
"version": 3
}
@@ -995,7 +995,8 @@
"schemaVersion": 41,
"tags": [
"gdev",
"panel-tests"
"panel-tests",
"table"
],
"templating": {
"list": []
@@ -1,4 +1,3 @@
import { ArrayDataFrame } from '../dataframe/ArrayDataFrame';
import { createDataFrame, toDataFrame } from '../dataframe/processDataFrame';
import { relativeToTimeRange } from '../datetime/rangeutil';
import { createTheme } from '../themes/createTheme';
@@ -83,7 +82,7 @@ locationUtil.initialize({
describe('Global MinMax', () => {
it('find global min max', () => {
const f0 = new ArrayDataFrame<{ title: string; value: number; value2: number | null }>([
const f0 = toDataFrame([
{ title: 'AAA', value: 100, value2: 1234 },
{ title: 'BBB', value: -20, value2: null },
{ title: 'CCC', value: 200, value2: 1000 },
@@ -95,7 +94,7 @@ describe('Global MinMax', () => {
});
it('find global min max when all values are zero', () => {
const f0 = new ArrayDataFrame<{ title: string; value: number; value2: number | null }>([
const f0 = toDataFrame([
{ title: 'AAA', value: 0, value2: 0 },
{ title: 'CCC', value: 0, value2: 0 },
]);
@@ -144,7 +143,7 @@ describe('Global MinMax', () => {
});
describe('applyFieldOverrides', () => {
const f0 = new ArrayDataFrame<{ title: string; value: number; value2: number | null }>([
const f0 = toDataFrame([
{ title: 'AAA', value: 100, value2: 1234 },
{ title: 'BBB', value: -20, value2: null },
{ title: 'CCC', value: 200, value2: 1000 },
@@ -245,6 +244,48 @@ describe('applyFieldOverrides', () => {
});
});
describe('given a frame field', () => {
const f0Internal = createDataFrame({
name: 'frame',
fields: [
{ name: 'time', type: FieldType.time, values: [1752170223000, 1752170224000] },
{ name: 'value', type: FieldType.number, values: [10, 20] },
],
});
const f0 = createDataFrame({
name: 'A',
fields: [
{
name: 'message',
type: FieldType.string,
values: ['foo'],
},
{
name: 'frame',
type: FieldType.frame,
values: [f0Internal],
},
],
});
it('will apply field overrides to the fields within the frame', () => {
const withOverrides = applyFieldOverrides({
data: [f0],
fieldConfig: {
defaults: {
max: 30,
},
overrides: [],
},
replaceVariables: (value) => value,
theme: createTheme(),
fieldConfigRegistry: customFieldRegistry,
});
expect(withOverrides[0].fields[1].values[0].fields[1].state.range.max).toBe(30);
});
});
it('will merge FieldConfig with default values', () => {
const field: FieldConfig = {
min: 0,
@@ -199,6 +199,8 @@ export function applyFieldOverrides(options: ApplyFieldOverrideOptions): DataFra
if (field.type === FieldType.nestedFrames) {
for (const nestedFrames of field.values) {
for (let nfIndex = 0; nfIndex < nestedFrames.length; nfIndex++) {
// TODO: should we apply fieldOverrides to nested frames?
for (const valueField of nestedFrames[nfIndex].fields) {
// Get display processor for nested fields
valueField.display = getDisplayProcessor({
@@ -232,6 +234,13 @@ export function applyFieldOverrides(options: ApplyFieldOverrideOptions): DataFra
}
}
}
if (field.type === FieldType.frame) {
field.values = applyFieldOverrides({
...options,
data: field.values,
});
}
}
return newFrame;