Time series: always show single points if surrounded by nulls (#35845)

This commit is contained in:
Leon Sorokin 2021-06-17 15:44:26 -05:00 committed by GitHub
parent 22203fd4df
commit 503c3b8f66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 270 additions and 88 deletions

View File

@ -17,11 +17,14 @@
"graphTooltip": 0,
"links": [
{
"icon": "external link",
"tags": ["gdev", "graph-ng"],
"type": "dashboards",
"asDropdown": true,
"title": "Graph Tests"
"icon": "external link",
"tags": [
"gdev",
"graph-ng"
],
"title": "Graph Tests",
"type": "dashboards"
}
],
"panels": [
@ -35,13 +38,15 @@
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillGradient": "none",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"graph": false,
"legend": false,
"tooltip": false
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 2,
@ -50,10 +55,16 @@
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"nullValueMode": "null",
"thresholds": {
"mode": "absolute",
"steps": [
@ -82,6 +93,7 @@
"options": {
"graph": {},
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
@ -136,13 +148,15 @@
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillGradient": "none",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"graph": false,
"legend": false,
"tooltip": false
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 2,
@ -151,11 +165,17 @@
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"max": 120,
"nullValueMode": "null",
"thresholds": {
"mode": "absolute",
"steps": [
@ -184,6 +204,7 @@
"options": {
"graph": {},
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
@ -246,13 +267,15 @@
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillGradient": "none",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"graph": false,
"legend": false,
"tooltip": false
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 2,
@ -261,11 +284,17 @@
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"max": 120,
"nullValueMode": "null",
"thresholds": {
"mode": "absolute",
"steps": [
@ -294,6 +323,7 @@
"options": {
"graph": {},
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
@ -375,13 +405,15 @@
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillGradient": "none",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"graph": false,
"legend": false,
"tooltip": false
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 2,
@ -390,10 +422,16 @@
"type": "linear"
},
"showPoints": "auto",
"spanNulls": true
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"nullValueMode": "null",
"thresholds": {
"mode": "absolute",
"steps": [
@ -422,6 +460,7 @@
"options": {
"graph": {},
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
@ -476,13 +515,15 @@
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillGradient": "none",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"graph": false,
"legend": false,
"tooltip": false
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 2,
@ -491,11 +532,17 @@
"type": "linear"
},
"showPoints": "auto",
"spanNulls": true
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"max": 120,
"nullValueMode": "null",
"thresholds": {
"mode": "absolute",
"steps": [
@ -524,6 +571,7 @@
"options": {
"graph": {},
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
@ -586,13 +634,15 @@
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillGradient": "none",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"graph": false,
"legend": false,
"tooltip": false
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 2,
@ -601,11 +651,17 @@
"type": "linear"
},
"showPoints": "auto",
"spanNulls": true
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"max": 120,
"nullValueMode": "null",
"thresholds": {
"mode": "absolute",
"steps": [
@ -634,6 +690,7 @@
"options": {
"graph": {},
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
@ -713,48 +770,33 @@
"mode": "palette-classic"
},
"custom": {
"axis": {
"grid": true,
"label": "Temperature",
"side": 3,
"width": 60
},
"axisLabel": "",
"axisPlacement": "auto",
"bars": {
"show": false
},
"barAlignment": 0,
"drawStyle": "line",
"fill": {
"alpha": 0
},
"fillGradient": "none",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"graph": false,
"legend": false,
"tooltip": false
},
"line": {
"color": {
"mode": "palette-classic"
},
"show": true,
"width": 1
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 2,
"nullValues": "null",
"pointSize": 5,
"points": {
"radius": 8,
"show": true
},
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
@ -818,6 +860,7 @@
},
"legend": {
"asTable": false,
"calcs": [],
"displayMode": "list",
"isVisible": true,
"placement": "bottom"
@ -874,48 +917,33 @@
"mode": "palette-classic"
},
"custom": {
"axis": {
"grid": true,
"label": "Temperature",
"side": 3,
"width": 60
},
"axisLabel": "",
"axisPlacement": "auto",
"bars": {
"show": false
},
"barAlignment": 0,
"drawStyle": "line",
"fill": {
"alpha": 0
},
"fillGradient": "none",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"graph": false,
"legend": false,
"tooltip": false
},
"line": {
"color": {
"mode": "palette-classic"
},
"show": true,
"width": 1
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 2,
"nullValues": "null",
"pointSize": 5,
"points": {
"radius": 8,
"show": true
},
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
@ -979,6 +1007,7 @@
},
"legend": {
"asTable": false,
"calcs": [],
"displayMode": "list",
"isVisible": true,
"placement": "bottom"
@ -1044,7 +1073,8 @@
"hideFrom": {
"graph": false,
"legend": false,
"tooltip": false
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
@ -1053,7 +1083,14 @@
"type": "linear"
},
"showPoints": "never",
"spanNulls": 3600000
"spanNulls": 3600000,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
@ -1124,11 +1161,97 @@
"timeShift": null,
"title": "Span nulls below 1hr",
"type": "timeseries"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineStyle": {
"fill": "solid"
},
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 7,
"x": 0,
"y": 22
},
"id": 15,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"refId": "A",
"scenarioId": "csv_metric_values",
"stringInput": "30,null,1,20,90,null,30,null,5,0,null,30"
}
],
"title": "Always show points between gaps",
"type": "timeseries"
}
],
"schemaVersion": 27,
"schemaVersion": 30,
"style": "dark",
"tags": ["gdev", "panel-tests", "graph-ng"],
"tags": [
"gdev",
"panel-tests",
"graph-ng"
],
"templating": {
"list": []
},
@ -1140,5 +1263,5 @@
"timezone": "",
"title": "Panel Tests - Graph NG - Gaps and Connected",
"uid": "8mmCAF1Mz",
"version": 1
}
"version": 12
}

View File

@ -127,6 +127,7 @@ Object {
"paths": [Function],
"points": Object {
"fill": "#808080",
"filter": [Function],
"show": true,
"size": undefined,
"stroke": "#808080",
@ -147,6 +148,7 @@ Object {
"paths": [Function],
"points": Object {
"fill": "#808080",
"filter": [Function],
"show": true,
"size": undefined,
"stroke": "#808080",
@ -167,6 +169,7 @@ Object {
"paths": [Function],
"points": Object {
"fill": "#808080",
"filter": [Function],
"show": true,
"size": undefined,
"stroke": "#808080",
@ -187,6 +190,7 @@ Object {
"paths": [Function],
"points": Object {
"fill": "#808080",
"filter": [Function],
"show": true,
"size": undefined,
"stroke": "#808080",
@ -207,6 +211,7 @@ Object {
"paths": [Function],
"points": Object {
"fill": "#808080",
"filter": [Function],
"show": true,
"size": undefined,
"stroke": "#808080",

View File

@ -25,6 +25,7 @@ import {
ScaleOrientation,
} from '../uPlot/config';
import { collectStackingGroups } from '../uPlot/utils';
import uPlot from 'uplot';
const defaultFormatter = (v: any) => (v == null ? '-' : v.toFixed(1));
@ -149,6 +150,43 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{ sync: DashboardCursor
const showPoints = customConfig.drawStyle === DrawStyle.Points ? PointVisibility.Always : customConfig.showPoints;
let pointsFilter: uPlot.Series.Points.Filter = () => null;
if (customConfig.spanNulls !== true) {
pointsFilter = (u, seriesIdx, show, gaps) => {
let filtered = [];
let series = u.series[seriesIdx];
if (!show && gaps && gaps.length) {
const [firstIdx, lastIdx] = series.idxs!;
const xData = u.data[0];
const firstPos = Math.round(u.valToPos(xData[firstIdx], 'x', true));
const lastPos = Math.round(u.valToPos(xData[lastIdx], 'x', true));
if (gaps[0][0] === firstPos) {
filtered.push(firstIdx);
}
// show single points between consecutive gaps that share end/start
for (let i = 0; i < gaps.length; i++) {
let thisGap = gaps[i];
let nextGap = gaps[i + 1];
if (nextGap && thisGap[1] === nextGap[0]) {
filtered.push(u.posToIdx(thisGap[1], true));
}
}
if (gaps[gaps.length - 1][1] === lastPos) {
filtered.push(lastIdx);
}
}
return filtered.length ? filtered : null;
};
}
let { fillOpacity } = customConfig;
if (customConfig.fillBelowTo && field.state?.origin) {
@ -175,6 +213,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{ sync: DashboardCursor
builder.addSeries({
scaleKey,
showPoints,
pointsFilter,
colorMode,
fillOpacity,
theme,

View File

@ -497,6 +497,7 @@ describe('UPlotConfigBuilder', () => {
"paths": [Function],
"points": Object {
"fill": "#00ff00",
"filter": undefined,
"size": 5,
"stroke": "#00ff00",
},
@ -606,6 +607,7 @@ describe('UPlotConfigBuilder', () => {
"paths": [Function],
"points": Object {
"fill": undefined,
"filter": undefined,
"size": undefined,
"stroke": undefined,
},
@ -621,6 +623,7 @@ describe('UPlotConfigBuilder', () => {
"paths": [Function],
"points": Object {
"fill": undefined,
"filter": undefined,
"size": 5,
"stroke": undefined,
},
@ -636,6 +639,7 @@ describe('UPlotConfigBuilder', () => {
"paths": [Function],
"points": Object {
"fill": undefined,
"filter": undefined,
"size": 5,
"stroke": undefined,
},

View File

@ -25,6 +25,7 @@ export interface SeriesProps extends LineConfig, BarConfig, FillConfig, PointsCo
colorMode?: FieldColorMode;
drawStyle?: DrawStyle;
pathBuilder?: Series.PathBuilder;
pointsFilter?: Series.Points.Filter;
pointsBuilder?: Series.Points.Show;
show?: boolean;
dataFrameFieldIndex?: DataFrameFieldIndex;
@ -37,6 +38,7 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
drawStyle,
pathBuilder,
pointsBuilder,
pointsFilter,
lineInterpolation,
lineWidth,
lineStyle,
@ -86,6 +88,7 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
stroke: pointColor,
fill: pointColor,
size: pointSize,
filter: pointsFilter,
},
};

View File

@ -109,6 +109,7 @@ Object {
"paths": [Function],
"points": Object {
"fill": undefined,
"filter": undefined,
"size": undefined,
"stroke": undefined,
},
@ -233,6 +234,7 @@ Object {
"paths": [Function],
"points": Object {
"fill": undefined,
"filter": undefined,
"size": undefined,
"stroke": undefined,
},
@ -357,6 +359,7 @@ Object {
"paths": [Function],
"points": Object {
"fill": undefined,
"filter": undefined,
"size": undefined,
"stroke": undefined,
},
@ -481,6 +484,7 @@ Object {
"paths": [Function],
"points": Object {
"fill": undefined,
"filter": undefined,
"size": undefined,
"stroke": undefined,
},
@ -605,6 +609,7 @@ Object {
"paths": [Function],
"points": Object {
"fill": undefined,
"filter": undefined,
"size": undefined,
"stroke": undefined,
},
@ -729,6 +734,7 @@ Object {
"paths": [Function],
"points": Object {
"fill": undefined,
"filter": undefined,
"size": undefined,
"stroke": undefined,
},
@ -853,6 +859,7 @@ Object {
"paths": [Function],
"points": Object {
"fill": undefined,
"filter": undefined,
"size": undefined,
"stroke": undefined,
},
@ -977,6 +984,7 @@ Object {
"paths": [Function],
"points": Object {
"fill": undefined,
"filter": undefined,
"size": undefined,
"stroke": undefined,
},