From ce63df425df9cace7dcd32a030667553936f10b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 19 Feb 2021 12:10:48 +0100 Subject: [PATCH] TimeSeriesPanel: Fixes overlapping time axis ticks (#31332) * GraphNG: Fixes overlapping time axis ticks * Review tweaks --- .../panel-graph/graph-ng-gradient-area.json | 4 +- .../panel-graph/graph-ng-hue-gradients.json | 10 +- .../panel-graph/graph-ng-nulls.json | 4 +- .../panel-graph/graph-ng-soft-limits.json | 4 +- .../panel-graph/graph-ng-time.json | 470 ++++++++++++++++++ .../panel-graph/graph-ng-yaxis-ticks.json | 4 +- .../dev-dashboards/panel-graph/graph-ng.json | 4 +- .../uPlot/config/UPlotAxisBuilder.ts | 50 +- 8 files changed, 529 insertions(+), 21 deletions(-) create mode 100644 devenv/dev-dashboards/panel-graph/graph-ng-time.json diff --git a/devenv/dev-dashboards/panel-graph/graph-ng-gradient-area.json b/devenv/dev-dashboards/panel-graph/graph-ng-gradient-area.json index e5701b48e9c..76629a0505a 100644 --- a/devenv/dev-dashboards/panel-graph/graph-ng-gradient-area.json +++ b/devenv/dev-dashboards/panel-graph/graph-ng-gradient-area.json @@ -19,7 +19,9 @@ { "icon": "external link", "tags": ["gdev", "graph-ng"], - "type": "dashboards" + "type": "dashboards", + "asDropdown": true, + "title": "Graph Tests" } ], "panels": [ diff --git a/devenv/dev-dashboards/panel-graph/graph-ng-hue-gradients.json b/devenv/dev-dashboards/panel-graph/graph-ng-hue-gradients.json index 7cf0f4a6f8a..23fe602a5f9 100644 --- a/devenv/dev-dashboards/panel-graph/graph-ng-hue-gradients.json +++ b/devenv/dev-dashboards/panel-graph/graph-ng-hue-gradients.json @@ -16,7 +16,15 @@ "gnetId": null, "graphTooltip": 0, "id": 391, - "links": [], + "links": [ + { + "icon": "external link", + "tags": ["gdev", "graph-ng"], + "type": "dashboards", + "asDropdown": true, + "title": "Graph Tests" + } + ], "panels": [ { "datasource": null, diff --git a/devenv/dev-dashboards/panel-graph/graph-ng-nulls.json b/devenv/dev-dashboards/panel-graph/graph-ng-nulls.json index c5dbb646638..26766b74b05 100644 --- a/devenv/dev-dashboards/panel-graph/graph-ng-nulls.json +++ b/devenv/dev-dashboards/panel-graph/graph-ng-nulls.json @@ -19,7 +19,9 @@ { "icon": "external link", "tags": ["gdev", "graph-ng"], - "type": "dashboards" + "type": "dashboards", + "asDropdown": true, + "title": "Graph Tests" } ], "panels": [ diff --git a/devenv/dev-dashboards/panel-graph/graph-ng-soft-limits.json b/devenv/dev-dashboards/panel-graph/graph-ng-soft-limits.json index d79ede0cf49..8507ebadb61 100644 --- a/devenv/dev-dashboards/panel-graph/graph-ng-soft-limits.json +++ b/devenv/dev-dashboards/panel-graph/graph-ng-soft-limits.json @@ -19,7 +19,9 @@ { "icon": "external link", "tags": ["gdev", "graph-ng"], - "type": "dashboards" + "type": "dashboards", + "asDropdown": true, + "title": "Graph Tests" } ], "panels": [ diff --git a/devenv/dev-dashboards/panel-graph/graph-ng-time.json b/devenv/dev-dashboards/panel-graph/graph-ng-time.json new file mode 100644 index 00000000000..ed06868f01a --- /dev/null +++ b/devenv/dev-dashboards/panel-graph/graph-ng-time.json @@ -0,0 +1,470 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "links": [ + { + "icon": "external link", + "tags": ["gdev", "graph-ng"], + "type": "dashboards", + "asDropdown": true, + "title": "Graph Tests" + } + ], + "panels": [ + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 41, + "gradientMode": "opacity", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "smooth", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 0, + "y": 0 + }, + "id": 2, + "maxDataPoints": 50, + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.5.0-pre", + "timeFrom": "1s", + "timeShift": null, + "title": "Sub second range ", + "type": "timeseries" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 17, + "gradientMode": "opacity", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "smooth", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 8, + "y": 0 + }, + "id": 3, + "maxDataPoints": 50, + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.5.0-pre", + "timeFrom": "1m", + "timeShift": null, + "title": "Sub minute range", + "type": "timeseries" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 30, + "gradientMode": "opacity", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "smooth", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 16, + "y": 0 + }, + "id": 4, + "maxDataPoints": 50, + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.5.0-pre", + "timeFrom": "1h", + "timeShift": null, + "title": "Sub hour range", + "type": "timeseries" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 37, + "gradientMode": "opacity", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "smooth", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 9 + }, + "id": 5, + "maxDataPoints": 50, + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.5.0-pre", + "timeFrom": "1d", + "timeShift": null, + "title": "Sub day range", + "type": "timeseries" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 37, + "gradientMode": "opacity", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "smooth", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 8, + "y": 9 + }, + "id": 6, + "maxDataPoints": 50, + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.5.0-pre", + "timeFrom": "2d", + "timeShift": null, + "title": "Last days (shows date)", + "type": "timeseries" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 37, + "gradientMode": "opacity", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "smooth", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 16, + "y": 9 + }, + "id": 7, + "maxDataPoints": 50, + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.5.0-pre", + "timeFrom": "5y", + "timeShift": null, + "title": "Last 5 days (year only)", + "type": "timeseries" + } + ], + "schemaVersion": 27, + "style": "dark", + "tags": ["gdev", "panel-tests", "graph-ng"], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Panel Tests - GraphNG - Time Axis", + "uid": "sd27_CPGz", + "version": 3 +} diff --git a/devenv/dev-dashboards/panel-graph/graph-ng-yaxis-ticks.json b/devenv/dev-dashboards/panel-graph/graph-ng-yaxis-ticks.json index f12c2e65b9a..1a9177bfdfa 100644 --- a/devenv/dev-dashboards/panel-graph/graph-ng-yaxis-ticks.json +++ b/devenv/dev-dashboards/panel-graph/graph-ng-yaxis-ticks.json @@ -19,7 +19,9 @@ { "icon": "external link", "tags": ["gdev", "graph-ng"], - "type": "dashboards" + "type": "dashboards", + "asDropdown": true, + "title": "Graph Tests" } ], "panels": [ diff --git a/devenv/dev-dashboards/panel-graph/graph-ng.json b/devenv/dev-dashboards/panel-graph/graph-ng.json index 43176b3d224..abf88a47626 100644 --- a/devenv/dev-dashboards/panel-graph/graph-ng.json +++ b/devenv/dev-dashboards/panel-graph/graph-ng.json @@ -19,7 +19,9 @@ { "icon": "external link", "tags": ["gdev", "graph-ng"], - "type": "dashboards" + "type": "dashboards", + "asDropdown": true, + "title": "Graph Tests" } ], "panels": [ diff --git a/packages/grafana-ui/src/components/uPlot/config/UPlotAxisBuilder.ts b/packages/grafana-ui/src/components/uPlot/config/UPlotAxisBuilder.ts index 7e27027595e..49b1b2eac64 100644 --- a/packages/grafana-ui/src/components/uPlot/config/UPlotAxisBuilder.ts +++ b/packages/grafana-ui/src/components/uPlot/config/UPlotAxisBuilder.ts @@ -99,13 +99,24 @@ export class UPlotAxisBuilder extends PlotConfigBuilder { /* Minimum grid & tick spacing in CSS pixels */ function calculateSpace(self: uPlot, axisIdx: number, scaleMin: number, scaleMax: number, plotDim: number): number { const axis = self.axes[axisIdx]; + const scale = self.scales[axis.scale!]; - // For x-axis (bottom) we need bigger spacing between labels - if (axis.side === 2) { - return 55; + // for axis left & right + if (axis.side !== 2 || !scale) { + return 30; } - return 30; + const defaultSpacing = 40; + + if (scale.time) { + const maxTicks = plotDim / defaultSpacing; + const increment = (scaleMax - scaleMin) / maxTicks; + const sample = formatTime(self, [scaleMin], axisIdx, defaultSpacing, increment); + const width = measureText(sample[0], 12).width + 18; + return width; + } + + return defaultSpacing; } /** height of x axis or width of y axis in CSS pixels alloted for values, gap & ticks, but excluding axis label */ @@ -129,29 +140,38 @@ function calculateAxisSize(self: uPlot, values: string[], axisIdx: number) { return measureText(maxLength, 12).width + 18; } +const timeUnitSize = { + second: 1000, + minute: 60 * 1000, + hour: 60 * 60 * 1000, + day: 24 * 60 * 60 * 1000, + month: 28 * 24 * 60 * 60 * 1000, + year: 365 * 24 * 60 * 60 * 1000, +}; + /** Format time axis ticks */ function formatTime(self: uPlot, splits: number[], axisIdx: number, foundSpace: number, foundIncr: number): string[] { const timeZone = (self.axes[axisIdx] as any).timeZone; const scale = self.scales.x; - const range = ((scale?.max ?? 0) - (scale?.min ?? 0)) / 1e3; - const oneDay = 86400; - const oneYear = 31536000; - - foundIncr /= 1e3; + const range = (scale?.max ?? 0) - (scale?.min ?? 0); + const yearRoundedToDay = Math.round(timeUnitSize.year / timeUnitSize.day) * timeUnitSize.day; + const incrementRoundedToDay = Math.round(foundIncr / timeUnitSize.day) * timeUnitSize.day; let format = systemDateFormats.interval.minute; - if (foundIncr < 1) { + if (foundIncr < timeUnitSize.second) { format = systemDateFormats.interval.second.replace('ss', 'ss.SS'); - } else if (foundIncr <= 45) { + } else if (foundIncr <= timeUnitSize.minute) { format = systemDateFormats.interval.second; - } else if (foundIncr <= 7200 || range <= oneDay) { + } else if (foundIncr <= timeUnitSize.hour || range <= timeUnitSize.day) { format = systemDateFormats.interval.minute; - } else if (foundIncr <= 80000) { + } else if (foundIncr <= timeUnitSize.day) { format = systemDateFormats.interval.hour; - } else if (foundIncr <= 2419200 || range <= oneYear) { + } else if (foundIncr <= timeUnitSize.month || range < timeUnitSize.year) { format = systemDateFormats.interval.day; - } else if (foundIncr <= 31536000) { + } else if (incrementRoundedToDay === yearRoundedToDay) { + format = systemDateFormats.interval.year; + } else if (foundIncr <= timeUnitSize.year) { format = systemDateFormats.interval.month; }