mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Heatmap fixes (adapted for v4.4.x branch) (#8920)
* heatmap: fix converting error when series contains 0 and log scale is selected, issue #8884 * heatmap: fix app crash when Y min set to 0 with log scale * heatmap: fix tooltip for 'zero' buckets in log scale * heatmap: fix tooltip histogram for log scales * heatmap: fix flicker of the highlighted element this was caused by too often fired mouseenter/mouseleave events * heatmap: fix missing X axis option for log scales * heatmap: fix missing zero bucket in tooltip histogram
This commit is contained in:
committed by
Torkel Ödegaard
parent
8ca08d65e7
commit
cb8ecb2d5f
@@ -214,12 +214,14 @@ function pushToYBuckets(buckets, bucketNum, value, point, bounds) {
|
|||||||
}
|
}
|
||||||
if (buckets[bucketNum]) {
|
if (buckets[bucketNum]) {
|
||||||
buckets[bucketNum].values.push(value);
|
buckets[bucketNum].values.push(value);
|
||||||
|
buckets[bucketNum].points.push(point);
|
||||||
buckets[bucketNum].count += count;
|
buckets[bucketNum].count += count;
|
||||||
} else {
|
} else {
|
||||||
buckets[bucketNum] = {
|
buckets[bucketNum] = {
|
||||||
y: bucketNum,
|
y: bucketNum,
|
||||||
bounds: bounds,
|
bounds: bounds,
|
||||||
values: [value],
|
values: [value],
|
||||||
|
points: [point],
|
||||||
count: count,
|
count: count,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,10 @@ export class HeatmapTooltip {
|
|||||||
|
|
||||||
let boundBottom, boundTop, valuesNumber;
|
let boundBottom, boundTop, valuesNumber;
|
||||||
let xData = data.buckets[xBucketIndex];
|
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 tooltipTimeFormat = 'YYYY-MM-DD HH:mm:ss';
|
||||||
let time = this.dashboard.formatDate(xData.x, tooltipTimeFormat);
|
let time = this.dashboard.formatDate(xData.x, tooltipTimeFormat);
|
||||||
@@ -105,7 +108,9 @@ export class HeatmapTooltip {
|
|||||||
|
|
||||||
if (yData) {
|
if (yData) {
|
||||||
if (yData.bounds) {
|
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);
|
boundTop = valueFormatter(yData.bounds.top);
|
||||||
valuesNumber = yData.count;
|
valuesNumber = yData.count;
|
||||||
tooltipHtml += `<div>
|
tooltipHtml += `<div>
|
||||||
@@ -165,7 +170,7 @@ export class HeatmapTooltip {
|
|||||||
let yBucketSize = this.scope.ctrl.data.yBucketSize;
|
let yBucketSize = this.scope.ctrl.data.yBucketSize;
|
||||||
let {min, max, ticks} = this.scope.ctrl.data.yAxis;
|
let {min, max, ticks} = this.scope.ctrl.data.yAxis;
|
||||||
let histogramData = _.map(xBucket.buckets, bucket => {
|
let histogramData = _.map(xBucket.buckets, bucket => {
|
||||||
return [bucket.y, bucket.values.length];
|
return [bucket.bounds.bottom, bucket.values.length];
|
||||||
});
|
});
|
||||||
histogramData = _.filter(histogramData, d => {
|
histogramData = _.filter(histogramData, d => {
|
||||||
return d[0] >= min && d[0] <= max;
|
return d[0] >= min && d[0] <= max;
|
||||||
@@ -180,7 +185,8 @@ export class HeatmapTooltip {
|
|||||||
if (this.panel.yAxis.logBase === 1) {
|
if (this.panel.yAxis.logBase === 1) {
|
||||||
barWidth = Math.floor(HISTOGRAM_WIDTH / (max - min) * yBucketSize * 0.9);
|
barWidth = Math.floor(HISTOGRAM_WIDTH / (max - min) * yBucketSize * 0.9);
|
||||||
} else {
|
} 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);
|
barWidth = Math.max(barWidth, 1);
|
||||||
|
|
||||||
|
|||||||
@@ -33,43 +33,26 @@
|
|||||||
|
|
||||||
<div class="section gf-form-group" ng-if="ctrl.panel.dataFormat == 'timeseries'">
|
<div class="section gf-form-group" ng-if="ctrl.panel.dataFormat == 'timeseries'">
|
||||||
<h5 class="section-heading">Buckets</h5>
|
<h5 class="section-heading">Buckets</h5>
|
||||||
<div ng-show="ctrl.panel.yAxis.logBase === 1">
|
<div class="gf-form-inline">
|
||||||
<div class="gf-form-inline">
|
|
||||||
<div class="gf-form">
|
|
||||||
<label class="gf-form-label">Y Axis</label>
|
|
||||||
<label class="gf-form-label">Buckets</label>
|
|
||||||
<input type="number" class="gf-form-input width-5" placeholder="auto" data-placement="right"
|
|
||||||
bs-tooltip="'Number of buckets for Y axis.'"
|
|
||||||
ng-model="ctrl.panel.yBucketNumber" ng-change="ctrl.refresh()" ng-model-onblur>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form">
|
|
||||||
<label class="gf-form-label">Size</label>
|
|
||||||
<input type="number" class="gf-form-input width-5" placeholder="auto" data-placement="right"
|
|
||||||
bs-tooltip="'Size of bucket. Has priority over Buckets option.'"
|
|
||||||
ng-model="ctrl.panel.yBucketSize" ng-change="ctrl.refresh()" ng-model-onblur>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form-inline">
|
|
||||||
<div class="gf-form">
|
|
||||||
<label class="gf-form-label">X Axis</label>
|
|
||||||
<label class="gf-form-label">Buckets</label>
|
|
||||||
<input type="number" class="gf-form-input width-5" placeholder="auto" data-placement="right"
|
|
||||||
bs-tooltip="'Number of buckets for Y axis.'"
|
|
||||||
ng-model="ctrl.panel.xBucketNumber" ng-change="ctrl.refresh()" ng-model-onblur>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form">
|
|
||||||
<label class="gf-form-label">Size</label>
|
|
||||||
<input type="text" class="gf-form-input width-5" placeholder="auto" data-placement="right"
|
|
||||||
bs-tooltip="'Size of bucket. Number or interval (10s, 5m, 1h, etc). Supported intervals: ms, s, m, h, d, w, M, y. Has priority over Buckets option.'"
|
|
||||||
ng-model="ctrl.panel.xBucketSize" ng-change="ctrl.refresh()" ng-model-onblur>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div ng-show="ctrl.panel.yAxis.logBase !== 1">
|
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<label class="gf-form-label width-7">Split Factor</label>
|
<label class="gf-form-label width-5">Y Axis</label>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form" ng-show="ctrl.panel.yAxis.logBase === 1">
|
||||||
|
<label class="gf-form-label width-5">Buckets</label>
|
||||||
|
<input type="number" class="gf-form-input width-5" placeholder="auto" data-placement="right"
|
||||||
|
bs-tooltip="'Number of buckets for Y axis.'"
|
||||||
|
ng-model="ctrl.panel.yBucketNumber" ng-change="ctrl.refresh()" ng-model-onblur>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form" ng-show="ctrl.panel.yAxis.logBase === 1">
|
||||||
|
<label class="gf-form-label width-4">Size</label>
|
||||||
|
<input type="number" class="gf-form-input width-5" placeholder="auto" data-placement="right"
|
||||||
|
bs-tooltip="'Size of bucket. Has priority over Buckets option.'"
|
||||||
|
ng-model="ctrl.panel.yBucketSize" ng-change="ctrl.refresh()" ng-model-onblur>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form" ng-show="ctrl.panel.yAxis.logBase !== 1">
|
||||||
|
<label class="gf-form-label width-10">Split Factor</label>
|
||||||
<input type="number"
|
<input type="number"
|
||||||
class="gf-form-input width-3"
|
class="gf-form-input width-9"
|
||||||
placeholder="1"
|
placeholder="1"
|
||||||
data-placement="right"
|
data-placement="right"
|
||||||
bs-tooltip="'For log scales only. By default Y values is splitted by integer powers of log base (1, 2, 4, 8, 16, ... for log2). This option allows to split each default bucket into specified number of buckets.'"
|
bs-tooltip="'For log scales only. By default Y values is splitted by integer powers of log base (1, 2, 4, 8, 16, ... for log2). This option allows to split each default bucket into specified number of buckets.'"
|
||||||
@@ -77,6 +60,21 @@
|
|||||||
</input>
|
</input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="gf-form-inline">
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label width-5">X Axis</label>
|
||||||
|
<label class="gf-form-label width-5">Buckets</label>
|
||||||
|
<input type="number" class="gf-form-input width-5" placeholder="auto" data-placement="right"
|
||||||
|
bs-tooltip="'Number of buckets for X axis.'"
|
||||||
|
ng-model="ctrl.panel.xBucketNumber" ng-change="ctrl.refresh()" ng-model-onblur>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label width-4">Size</label>
|
||||||
|
<input type="text" class="gf-form-input width-5" placeholder="auto" data-placement="right"
|
||||||
|
bs-tooltip="'Size of bucket. Number or interval (10s, 5m, 1h, etc). Supported intervals: ms, s, m, h, d, w, M, y. Has priority over Buckets option.'"
|
||||||
|
ng-model="ctrl.panel.xBucketSize" ng-change="ctrl.refresh()" ng-model-onblur>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section gf-form-group">
|
<div class="section gf-form-group">
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ export default function link(scope, elem, attrs, ctrl) {
|
|||||||
let log_base = panel.yAxis.logBase;
|
let log_base = panel.yAxis.logBase;
|
||||||
let {y_min, y_max} = adjustLogRange(data.heatmapStats.minLog, data.heatmapStats.max, log_base);
|
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;
|
y_max = panel.yAxis.max !== null ? adjustLogMax(panel.yAxis.max, log_base) : y_max;
|
||||||
|
|
||||||
// Set default Y min and max if no data
|
// Set default Y min and max if no data
|
||||||
|
|||||||
@@ -18,6 +18,15 @@
|
|||||||
stroke: $text-color-weak;
|
stroke: $text-color-weak;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This hack prevents mouseenter/mouseleave events get fired too often
|
||||||
|
svg {
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
rect {
|
||||||
|
pointer-events: visiblePainted;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.heatmap-tooltip {
|
.heatmap-tooltip {
|
||||||
|
|||||||
Reference in New Issue
Block a user