diff --git a/public/app/plugins/panel/heatmap/heatmap_data_converter.ts b/public/app/plugins/panel/heatmap/heatmap_data_converter.ts index d9e6bbb7d43..ef405ea5508 100644 --- a/public/app/plugins/panel/heatmap/heatmap_data_converter.ts +++ b/public/app/plugins/panel/heatmap/heatmap_data_converter.ts @@ -214,12 +214,14 @@ function pushToYBuckets(buckets, bucketNum, value, point, bounds) { } if (buckets[bucketNum]) { buckets[bucketNum].values.push(value); + buckets[bucketNum].points.push(point); buckets[bucketNum].count += count; } else { buckets[bucketNum] = { y: bucketNum, bounds: bounds, values: [value], + points: [point], count: count, }; } diff --git a/public/app/plugins/panel/heatmap/heatmap_tooltip.ts b/public/app/plugins/panel/heatmap/heatmap_tooltip.ts index 5ec0a8fa308..0d3f65cbf4d 100644 --- a/public/app/plugins/panel/heatmap/heatmap_tooltip.ts +++ b/public/app/plugins/panel/heatmap/heatmap_tooltip.ts @@ -83,7 +83,10 @@ export class HeatmapTooltip { let boundBottom, boundTop, valuesNumber; let xData = data.buckets[xBucketIndex]; - let yData = xData.buckets[yBucketIndex]; + // Search in special 'zero' bucket also + let yData = _.find(xData.buckets, (bucket, bucketIndex) => { + return bucket.bounds.bottom === yBucketIndex || bucketIndex === yBucketIndex; + }); let tooltipTimeFormat = 'YYYY-MM-DD HH:mm:ss'; let time = this.dashboard.formatDate(xData.x, tooltipTimeFormat); @@ -105,7 +108,9 @@ export class HeatmapTooltip { if (yData) { if (yData.bounds) { - boundBottom = valueFormatter(yData.bounds.bottom); + // Display 0 if bucket is a special 'zero' bucket + let bottom = yData.y ? yData.bounds.bottom : 0; + boundBottom = valueFormatter(bottom); boundTop = valueFormatter(yData.bounds.top); valuesNumber = yData.count; tooltipHtml += `
@@ -165,7 +170,7 @@ export class HeatmapTooltip { let yBucketSize = this.scope.ctrl.data.yBucketSize; let {min, max, ticks} = this.scope.ctrl.data.yAxis; let histogramData = _.map(xBucket.buckets, bucket => { - return [bucket.y, bucket.values.length]; + return [bucket.bounds.bottom, bucket.values.length]; }); histogramData = _.filter(histogramData, d => { return d[0] >= min && d[0] <= max; @@ -180,7 +185,8 @@ export class HeatmapTooltip { if (this.panel.yAxis.logBase === 1) { barWidth = Math.floor(HISTOGRAM_WIDTH / (max - min) * yBucketSize * 0.9); } else { - barWidth = Math.floor(HISTOGRAM_WIDTH / ticks / yBucketSize * 0.9); + let barNumberFactor = yBucketSize ? yBucketSize : 1; + barWidth = Math.floor(HISTOGRAM_WIDTH / ticks / barNumberFactor * 0.9); } barWidth = Math.max(barWidth, 1); diff --git a/public/app/plugins/panel/heatmap/partials/axes_editor.html b/public/app/plugins/panel/heatmap/partials/axes_editor.html index 10af1977af7..5d25bedd667 100644 --- a/public/app/plugins/panel/heatmap/partials/axes_editor.html +++ b/public/app/plugins/panel/heatmap/partials/axes_editor.html @@ -33,43 +33,26 @@
Buckets
-
-
-
- - - -
-
- - -
-
-
-
- - - -
-
- - -
-
-
-
+
- + +
+
+ + +
+
+ + +
+
+
+
+
+ + + +
+
+ + +
+
diff --git a/public/app/plugins/panel/heatmap/rendering.ts b/public/app/plugins/panel/heatmap/rendering.ts index b928173eafe..de2c2fde719 100644 --- a/public/app/plugins/panel/heatmap/rendering.ts +++ b/public/app/plugins/panel/heatmap/rendering.ts @@ -210,7 +210,7 @@ export default function link(scope, elem, attrs, ctrl) { let log_base = panel.yAxis.logBase; let {y_min, y_max} = adjustLogRange(data.heatmapStats.minLog, data.heatmapStats.max, log_base); - y_min = panel.yAxis.min !== null ? adjustLogMin(panel.yAxis.min, log_base) : y_min; + y_min = panel.yAxis.min && panel.yAxis.min !== '0' ? adjustLogMin(panel.yAxis.min, log_base) : y_min; y_max = panel.yAxis.max !== null ? adjustLogMax(panel.yAxis.max, log_base) : y_max; // Set default Y min and max if no data diff --git a/public/app/plugins/panel/singlestat/specs/singlestat_panel_spec.ts b/public/app/plugins/panel/singlestat/specs/singlestat_panel_spec.ts index 979972c6272..87922a3523b 100644 --- a/public/app/plugins/panel/singlestat/specs/singlestat_panel_spec.ts +++ b/public/app/plugins/panel/singlestat/specs/singlestat_panel_spec.ts @@ -14,11 +14,27 @@ describe('grafanaSingleStat', function() { expect(getColorForValue(data, 5)).to.be('green'); }); - it('25 should return green', () => { + it('19.9 should return green', () => { + expect(getColorForValue(data, 19.9)).to.be('green'); + }); + + it('20 should return yellow', () => { + expect(getColorForValue(data, 20)).to.be('yellow'); + }); + + it('20.1 should return yellow', () => { + expect(getColorForValue(data, 20.1)).to.be('yellow'); + }); + + it('25 should return yellow', () => { expect(getColorForValue(data, 25)).to.be('yellow'); }); - it('55 should return green', () => { + it('50 should return red', () => { + expect(getColorForValue(data, 50)).to.be('red'); + }); + + it('55 should return red', () => { expect(getColorForValue(data, 55)).to.be('red'); }); }); diff --git a/public/app/plugins/panel/singlestat/specs/singlestat_specs.ts b/public/app/plugins/panel/singlestat/specs/singlestat_specs.ts index 210196a24cc..48b13d66c84 100644 --- a/public/app/plugins/panel/singlestat/specs/singlestat_specs.ts +++ b/public/app/plugins/panel/singlestat/specs/singlestat_specs.ts @@ -108,7 +108,7 @@ describe('SingleStatCtrl', function() { }); }); - singleStatScenario('When range to text mapping is specifiedfor first range', function(ctx) { + singleStatScenario('When range to text mapping is specified for first range', function(ctx) { ctx.setup(function() { ctx.data = [ {target: 'test.cpu1', datapoints: [[41,50]]} diff --git a/public/sass/components/_panel_heatmap.scss b/public/sass/components/_panel_heatmap.scss index cf55c91ab7c..9d07e6a363c 100644 --- a/public/sass/components/_panel_heatmap.scss +++ b/public/sass/components/_panel_heatmap.scss @@ -18,6 +18,15 @@ stroke: $text-color-weak; } } + + // This hack prevents mouseenter/mouseleave events get fired too often + svg { + pointer-events: none; + + rect { + pointer-events: visiblePainted; + } + } } .heatmap-tooltip { diff --git a/public/vendor/flot/jquery.flot.gauge.js b/public/vendor/flot/jquery.flot.gauge.js index d8ed958e990..14866dbef20 100644 --- a/public/vendor/flot/jquery.flot.gauge.js +++ b/public/vendor/flot/jquery.flot.gauge.js @@ -85,17 +85,17 @@ * @return the calculated layout properties */ Gauge.prototype.calculateLayout = function() { - + var canvasWidth = placeholder.width(); var canvasHeight = placeholder.height(); - - + + // calculate cell size var columns = Math.min(series.length, gaugeOptions.layout.columns); var rows = Math.ceil(series.length / columns); - - + + var margin = gaugeOptions.layout.margin; var hMargin = gaugeOptions.layout.hMargin; @@ -107,8 +107,8 @@ cellWidth = cell; cellHeight = cell; } - - + + // calculate 'auto' values calculateAutoValues(gaugeOptions, cellWidth); @@ -155,13 +155,13 @@ var maxRadiusV = outerRadiusV - (thresholdLabelMargin * 2) - thresholdLabelFontSize - thresholdWidth; var radius = Math.min(maxRadiusH, maxRadiusV); - + var width = gaugeOptions.gauge.width; if (width >= radius) { width = Math.max(3, radius / 3); } - + var outerRadius = (thresholdLabelMargin * 2) + thresholdLabelFontSize + thresholdWidth + radius; var gaugeOuterHeight = Math.max(outerRadius * (1 + heightRatioV), outerRadius + valueMargin + (valueFontSize / 2)); @@ -198,7 +198,7 @@ * @param {Number} cellWidth the width of cell */ function calculateAutoValues(gaugeOptionsi, cellWidth) { - + if (gaugeOptionsi.gauge.width === "auto") { gaugeOptionsi.gauge.width = Math.max(5, cellWidth / 8); } @@ -223,7 +223,7 @@ if (gaugeOptionsi.threshold.label.font.size === "auto") { gaugeOptionsi.threshold.label.font.size = Math.max(5, cellWidth / 15); } - + } Gauge.prototype.calculateAutoValues = calculateAutoValues; @@ -237,7 +237,7 @@ * @return the calculated cell layout properties */ Gauge.prototype.calculateCellLayout = function(gaugeOptionsi, layout, i) { - + // calculate top, left and center var c = col(layout.columns, i); var r = row(layout.columns, i); @@ -276,7 +276,7 @@ * @param {Object} layout the layout properties */ Gauge.prototype.drawBackground = function(layout) { - + if (!gaugeOptions.frame.show) { return; } @@ -299,7 +299,7 @@ * @param {Object} cellLayout the cell layout properties */ Gauge.prototype.drawCellBackground = function(gaugeOptionsi, cellLayout) { - + context.save(); if (gaugeOptionsi.cell.border && gaugeOptionsi.cell.border.show && gaugeOptionsi.cell.border.color && gaugeOptionsi.cell.border.width) { context.strokeStyle = gaugeOptionsi.cell.border.color; @@ -324,10 +324,10 @@ * @param {Number} data the value of the gauge */ Gauge.prototype.drawGauge = function(gaugeOptionsi, layout, cellLayout, label, data) { - + var blur = gaugeOptionsi.gauge.shadow.show ? gaugeOptionsi.gauge.shadow.blur : 0; - + // draw gauge frame drawArcWithShadow( @@ -371,7 +371,7 @@ for (var i = 0; i < gaugeOptionsi.threshold.values.length; i++) { var threshold = gaugeOptionsi.threshold.values[i]; color = threshold.color; - if (data <= threshold.value) { + if (data < threshold.value) { break; } } @@ -410,7 +410,7 @@ * @param {Object} cellLayout the cell layout properties */ Gauge.prototype.drawThreshold = function(gaugeOptionsi, layout, cellLayout) { - + var a1 = gaugeOptionsi.gauge.startAngle; for (var i = 0; i < gaugeOptionsi.threshold.values.length; i++) { var threshold = gaugeOptionsi.threshold.values[i]; @@ -478,7 +478,7 @@ * @param {Object} item the item of the series */ Gauge.prototype.drawLable = function(gaugeOptionsi, layout, cellLayout, i, item) { - + drawText( cellLayout.cx, cellLayout.y + cellLayout.cellMargin + layout.labelMargin + cellLayout.offsetY, @@ -498,7 +498,7 @@ * @param {Object} item the item of the series */ Gauge.prototype.drawValue = function(gaugeOptionsi, layout, cellLayout, i, item) { - + drawText( cellLayout.cx, cellLayout.cy - (gaugeOptionsi.value.font.size / 2), @@ -517,7 +517,7 @@ * @param {Number} i the index of the series */ Gauge.prototype.drawThresholdValues = function(gaugeOptionsi, layout, cellLayout, i) { - + // min, max drawThresholdValue(gaugeOptionsi, layout, cellLayout, "Min" + i, gaugeOptionsi.gauge.min, gaugeOptionsi.gauge.startAngle); drawThresholdValue(gaugeOptionsi, layout, cellLayout, "Max" + i, gaugeOptionsi.gauge.max, gaugeOptionsi.gauge.endAngle); @@ -729,8 +729,8 @@ plot.hooks.processOptions.push(function(plot, options) { var logger = getLogger(options.series.gauges.debug); - - + + // turn 'grid' and 'legend' off if (options.series.gauges.show) { @@ -740,7 +740,7 @@ // sort threshold var thresholds = options.series.gauges.threshold.values; - + thresholds.sort(function(a, b) { if (a.value < b.value) { return -1; @@ -750,9 +750,9 @@ return 0; } }); - - + + }); // add draw hook @@ -761,14 +761,14 @@ var gaugeOptions = options.series.gauges; var logger = getLogger(gaugeOptions.debug); - + if (!gaugeOptions.show) { return; } var series = plot.getData(); - + if (!series || !series.length) { return; // if no series were passed } @@ -777,10 +777,10 @@ // calculate layout var layout = gauge.calculateLayout(); - + // debug layout if (gaugeOptions.debug.layout) { - + } // draw background @@ -789,21 +789,21 @@ // draw cells (label, gauge, value, threshold) for (var i = 0; i < series.length; i++) { var item = series[i]; - + var gaugeOptionsi = $.extend({}, gaugeOptions, item.gauges); if (item.gauges) { // re-calculate 'auto' values gauge.calculateAutoValues(gaugeOptionsi, layout.cellWidth); } - + // calculate cell layout var cellLayout = gauge.calculateCellLayout(gaugeOptionsi, layout, i); - + // draw cell background gauge.drawCellBackground(gaugeOptionsi, cellLayout) // debug layout if (gaugeOptionsi.debug.layout) { - + } // draw label if (gaugeOptionsi.label.show) {