Y-Axes
diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts
index 4033e4b3778..e3eed6fc382 100755
--- a/public/app/plugins/panel/graph/graph.ts
+++ b/public/app/plugins/panel/graph/graph.ts
@@ -337,9 +337,17 @@ class GraphElement {
let bucketSize: number;
if (this.data.length) {
- const histMin = _.min(_.map(this.data, s => s.stats.min));
- const histMax = _.max(_.map(this.data, s => s.stats.max));
+ let histMin = _.min(_.map(this.data, s => s.stats.min));
+ let histMax = _.max(_.map(this.data, s => s.stats.max));
const ticks = panel.xaxis.buckets || this.panelWidth / 50;
+ if (panel.xaxis.min != null) {
+ const isInvalidXaxisMin = tickStep(panel.xaxis.min, histMax, ticks) <= 0;
+ histMin = isInvalidXaxisMin ? histMin : panel.xaxis.min;
+ }
+ if (panel.xaxis.max != null) {
+ const isInvalidXaxisMax = tickStep(histMin, panel.xaxis.max, ticks) <= 0;
+ histMax = isInvalidXaxisMax ? histMax : panel.xaxis.max;
+ }
bucketSize = tickStep(histMin, histMax, ticks);
options.series.bars.barWidth = bucketSize * 0.8;
this.data = convertToHistogramData(this.data, bucketSize, this.ctrl.hiddenSeries, histMin, histMax);
diff --git a/public/app/plugins/panel/graph/histogram.ts b/public/app/plugins/panel/graph/histogram.ts
index b09226d15e7..b5c478198c0 100644
--- a/public/app/plugins/panel/graph/histogram.ts
+++ b/public/app/plugins/panel/graph/histogram.ts
@@ -43,6 +43,10 @@ export function convertValuesToHistogram(values: number[], bucketSize: number, m
}
for (let i = 0; i < values.length; i++) {
+ // filter out values outside the min and max boundaries
+ if (values[i] < min || values[i] > max) {
+ continue;
+ }
const bound = getBucketBound(values[i], bucketSize);
histogram[bound] = histogram[bound] + 1;
}
diff --git a/public/app/plugins/panel/graph/specs/graph.test.ts b/public/app/plugins/panel/graph/specs/graph.test.ts
index 58a35ea2a5f..ff81a8ae6eb 100644
--- a/public/app/plugins/panel/graph/specs/graph.test.ts
+++ b/public/app/plugins/panel/graph/specs/graph.test.ts
@@ -516,4 +516,408 @@ describe('grafanaGraph', () => {
expect(ctx.plotData[0].data[0][1]).toBe(2);
});
});
+
+ describe('when graph is histogram, and xaxis min is set', () => {
+ beforeEach(() => {
+ setupCtx(() => {
+ ctrl.panel.xaxis.mode = 'histogram';
+ ctrl.panel.xaxis.min = 150;
+ ctrl.panel.stack = false;
+ ctrl.hiddenSeries = {};
+ ctx.data[0] = new TimeSeries({
+ datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
+ alias: 'series1',
+ });
+ });
+ });
+
+ it('should not contain values lower than min', () => {
+ const nonZero = ctx.plotData[0].data.filter(t => t[1] > 0);
+ expect(Math.min.apply(Math, nonZero.map(t => t[0]))).toBe(200);
+ expect(Math.max.apply(Math, nonZero.map(t => t[0]))).toBe(300);
+ });
+ });
+
+ describe('when graph is histogram, and xaxis min is zero', () => {
+ beforeEach(() => {
+ setupCtx(() => {
+ ctrl.panel.xaxis.mode = 'histogram';
+ ctrl.panel.xaxis.min = 0;
+ ctrl.panel.stack = false;
+ ctrl.hiddenSeries = {};
+ ctx.data[0] = new TimeSeries({
+ datapoints: [[-100, 1], [100, 2], [200, 3], [300, 4]],
+ alias: 'series1',
+ });
+ });
+ });
+
+ it('should not contain values lower than zero', () => {
+ const nonZero = ctx.plotData[0].data.filter(t => t[1] > 0);
+ expect(Math.min.apply(Math, nonZero.map(t => t[0]))).toBe(100);
+ expect(Math.max.apply(Math, nonZero.map(t => t[0]))).toBe(300);
+ });
+ });
+
+ describe('when graph is histogram, and xaxis min is null', () => {
+ beforeEach(() => {
+ setupCtx(() => {
+ ctrl.panel.xaxis.mode = 'histogram';
+ ctrl.panel.xaxis.min = null;
+ ctrl.panel.stack = false;
+ ctrl.hiddenSeries = {};
+ ctx.data[0] = new TimeSeries({
+ datapoints: [[-100, 1], [100, 2], [200, 3], [300, 4]],
+ alias: 'series1',
+ });
+ });
+ });
+
+ it('xaxis min should not affect the histogram', () => {
+ const nonZero = ctx.plotData[0].data.filter(t => t[1] > 0);
+ expect(Math.min.apply(Math, nonZero.map(t => t[0]))).toBe(-100);
+ expect(Math.max.apply(Math, nonZero.map(t => t[0]))).toBe(300);
+ });
+ });
+
+ describe('when graph is histogram, and xaxis min is undefined', () => {
+ beforeEach(() => {
+ setupCtx(() => {
+ ctrl.panel.xaxis.mode = 'histogram';
+ ctrl.panel.xaxis.min = undefined;
+ ctrl.panel.stack = false;
+ ctrl.hiddenSeries = {};
+ ctx.data[0] = new TimeSeries({
+ datapoints: [[-100, 1], [100, 2], [200, 3], [300, 4]],
+ alias: 'series1',
+ });
+ });
+ });
+
+ it('xaxis min should not affect the histogram', () => {
+ const nonZero = ctx.plotData[0].data.filter(t => t[1] > 0);
+ expect(Math.min.apply(Math, nonZero.map(t => t[0]))).toBe(-100);
+ expect(Math.max.apply(Math, nonZero.map(t => t[0]))).toBe(300);
+ });
+ });
+
+ describe('when graph is histogram, and xaxis max is set', () => {
+ beforeEach(() => {
+ setupCtx(() => {
+ ctrl.panel.xaxis.mode = 'histogram';
+ ctrl.panel.xaxis.max = 250;
+ ctrl.panel.stack = false;
+ ctrl.hiddenSeries = {};
+ ctx.data[0] = new TimeSeries({
+ datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
+ alias: 'series1',
+ });
+ });
+ });
+
+ it('should not contain values greater than max', () => {
+ const nonZero = ctx.plotData[0].data.filter(t => t[1] > 0);
+ expect(Math.min.apply(Math, nonZero.map(t => t[0]))).toBe(100);
+ expect(Math.max.apply(Math, nonZero.map(t => t[0]))).toBe(200);
+ });
+ });
+
+ describe('when graph is histogram, and xaxis max is zero', () => {
+ beforeEach(() => {
+ setupCtx(() => {
+ ctrl.panel.xaxis.mode = 'histogram';
+ ctrl.panel.xaxis.max = 0;
+ ctrl.panel.stack = false;
+ ctrl.hiddenSeries = {};
+ ctx.data[0] = new TimeSeries({
+ datapoints: [[-100, 1], [100, 1], [100, 2], [200, 3], [300, 4]],
+ alias: 'series1',
+ });
+ });
+ });
+
+ it('should not contain values greater than zero', () => {
+ const nonZero = ctx.plotData[0].data.filter(t => t[1] > 0);
+ expect(Math.min.apply(Math, nonZero.map(t => t[0]))).toBe(-100);
+ expect(Math.max.apply(Math, nonZero.map(t => t[0]))).toBe(-100);
+ });
+ });
+
+ describe('when graph is histogram, and xaxis max is null', () => {
+ beforeEach(() => {
+ setupCtx(() => {
+ ctrl.panel.xaxis.mode = 'histogram';
+ ctrl.panel.xaxis.max = null;
+ ctrl.panel.stack = false;
+ ctrl.hiddenSeries = {};
+ ctx.data[0] = new TimeSeries({
+ datapoints: [[-100, 1], [100, 1], [100, 2], [200, 3], [300, 4]],
+ alias: 'series1',
+ });
+ });
+ });
+
+ it('xaxis max should not affect the histogram', () => {
+ const nonZero = ctx.plotData[0].data.filter(t => t[1] > 0);
+ expect(Math.min.apply(Math, nonZero.map(t => t[0]))).toBe(-100);
+ expect(Math.max.apply(Math, nonZero.map(t => t[0]))).toBe(300);
+ });
+ });
+
+ describe('when graph is histogram, and xaxis max is undefined', () => {
+ beforeEach(() => {
+ setupCtx(() => {
+ ctrl.panel.xaxis.mode = 'histogram';
+ ctrl.panel.xaxis.max = undefined;
+ ctrl.panel.stack = false;
+ ctrl.hiddenSeries = {};
+ ctx.data[0] = new TimeSeries({
+ datapoints: [[-100, 1], [100, 1], [100, 2], [200, 3], [300, 4]],
+ alias: 'series1',
+ });
+ });
+ });
+
+ it('xaxis max should not should node affect the histogram', () => {
+ const nonZero = ctx.plotData[0].data.filter(t => t[1] > 0);
+ expect(Math.min.apply(Math, nonZero.map(t => t[0]))).toBe(-100);
+ expect(Math.max.apply(Math, nonZero.map(t => t[0]))).toBe(300);
+ });
+ });
+
+ describe('when graph is histogram, and xaxis min and max are set', () => {
+ beforeEach(() => {
+ setupCtx(() => {
+ ctrl.panel.xaxis.mode = 'histogram';
+ ctrl.panel.xaxis.min = 150;
+ ctrl.panel.xaxis.max = 250;
+ ctrl.panel.stack = false;
+ ctrl.hiddenSeries = {};
+ ctx.data[0] = new TimeSeries({
+ datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
+ alias: 'series1',
+ });
+ });
+ });
+
+ it('should not contain values lower than min and greater than max', () => {
+ const nonZero = ctx.plotData[0].data.filter(t => t[1] > 0);
+ expect(Math.min.apply(Math, nonZero.map(t => t[0]))).toBe(200);
+ expect(Math.max.apply(Math, nonZero.map(t => t[0]))).toBe(200);
+ });
+ });
+
+ describe('when graph is histogram, and xaxis min and max are zero', () => {
+ beforeEach(() => {
+ setupCtx(() => {
+ ctrl.panel.xaxis.mode = 'histogram';
+ ctrl.panel.xaxis.min = 0;
+ ctrl.panel.xaxis.max = 0;
+ ctrl.panel.stack = false;
+ ctrl.hiddenSeries = {};
+ ctx.data[0] = new TimeSeries({
+ datapoints: [[-100, 1], [100, 1], [100, 2], [200, 3], [300, 4]],
+ alias: 'series1',
+ });
+ });
+ });
+
+ it('xaxis max should be ignored otherwise the bucketSize is zero', () => {
+ const nonZero = ctx.plotData[0].data.filter(t => t[1] > 0);
+ expect(Math.min.apply(Math, nonZero.map(t => t[0]))).toBe(100);
+ expect(Math.max.apply(Math, nonZero.map(t => t[0]))).toBe(300);
+ });
+ });
+
+ describe('when graph is histogram, and xaxis min and max are null', () => {
+ beforeEach(() => {
+ setupCtx(() => {
+ ctrl.panel.xaxis.mode = 'histogram';
+ ctrl.panel.xaxis.min = null;
+ ctrl.panel.xaxis.max = null;
+ ctrl.panel.stack = false;
+ ctrl.hiddenSeries = {};
+ ctx.data[0] = new TimeSeries({
+ datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
+ alias: 'series1',
+ });
+ });
+ });
+
+ it('xaxis min and max should not affect the histogram', () => {
+ const nonZero = ctx.plotData[0].data.filter(t => t[1] > 0);
+ expect(Math.min.apply(Math, nonZero.map(t => t[0]))).toBe(100);
+ expect(Math.max.apply(Math, nonZero.map(t => t[0]))).toBe(300);
+ });
+ });
+
+ describe('when graph is histogram, and xaxis min and max are undefined', () => {
+ beforeEach(() => {
+ setupCtx(() => {
+ ctrl.panel.xaxis.mode = 'histogram';
+ ctrl.panel.xaxis.min = undefined;
+ ctrl.panel.xaxis.max = undefined;
+ ctrl.panel.stack = false;
+ ctrl.hiddenSeries = {};
+ ctx.data[0] = new TimeSeries({
+ datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
+ alias: 'series1',
+ });
+ });
+ });
+
+ it('xaxis min and max should not affect the histogram', () => {
+ const nonZero = ctx.plotData[0].data.filter(t => t[1] > 0);
+ expect(Math.min.apply(Math, nonZero.map(t => t[0]))).toBe(100);
+ expect(Math.max.apply(Math, nonZero.map(t => t[0]))).toBe(300);
+ });
+ });
+
+ describe('when graph is histogram, and xaxis min is greater than xaxis max', () => {
+ beforeEach(() => {
+ setupCtx(() => {
+ ctrl.panel.xaxis.mode = 'histogram';
+ ctrl.panel.xaxis.min = 150;
+ ctrl.panel.xaxis.max = 100;
+ ctrl.panel.stack = false;
+ ctrl.hiddenSeries = {};
+ ctx.data[0] = new TimeSeries({
+ datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
+ alias: 'series1',
+ });
+ });
+ });
+
+ it('xaxis max should be ignored otherwise the bucketSize is negative', () => {
+ const nonZero = ctx.plotData[0].data.filter(t => t[1] > 0);
+ expect(Math.min.apply(Math, nonZero.map(t => t[0]))).toBe(200);
+ expect(Math.max.apply(Math, nonZero.map(t => t[0]))).toBe(300);
+ });
+ });
+
+ // aaa
+ describe('when graph is histogram, and xaxis min is greater than the maximum value', () => {
+ beforeEach(() => {
+ setupCtx(() => {
+ ctrl.panel.xaxis.mode = 'histogram';
+ ctrl.panel.xaxis.min = 301;
+ ctrl.panel.stack = false;
+ ctrl.hiddenSeries = {};
+ ctx.data[0] = new TimeSeries({
+ datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
+ alias: 'series1',
+ });
+ });
+ });
+
+ it('xaxis min should be ignored otherwise the bucketSize is negative', () => {
+ const nonZero = ctx.plotData[0].data.filter(t => t[1] > 0);
+ expect(Math.min.apply(Math, nonZero.map(t => t[0]))).toBe(100);
+ expect(Math.max.apply(Math, nonZero.map(t => t[0]))).toBe(300);
+ });
+ });
+
+ describe('when graph is histogram, and xaxis min is equal to the maximum value', () => {
+ beforeEach(() => {
+ setupCtx(() => {
+ ctrl.panel.xaxis.mode = 'histogram';
+ ctrl.panel.xaxis.min = 300;
+ ctrl.panel.stack = false;
+ ctrl.hiddenSeries = {};
+ ctx.data[0] = new TimeSeries({
+ datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
+ alias: 'series1',
+ });
+ });
+ });
+
+ it('xaxis min should be ignored otherwise the bucketSize is zero', () => {
+ const nonZero = ctx.plotData[0].data.filter(t => t[1] > 0);
+ expect(Math.min.apply(Math, nonZero.map(t => t[0]))).toBe(100);
+ expect(Math.max.apply(Math, nonZero.map(t => t[0]))).toBe(300);
+ });
+ });
+
+ describe('when graph is histogram, and xaxis min is lower than the minimum value', () => {
+ beforeEach(() => {
+ setupCtx(() => {
+ ctrl.panel.xaxis.mode = 'histogram';
+ ctrl.panel.xaxis.min = 99;
+ ctrl.panel.stack = false;
+ ctrl.hiddenSeries = {};
+ ctx.data[0] = new TimeSeries({
+ datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
+ alias: 'series1',
+ });
+ });
+ });
+
+ it('xaxis min should not affect the histogram', () => {
+ const nonZero = ctx.plotData[0].data.filter(t => t[1] > 0);
+ expect(Math.min.apply(Math, nonZero.map(t => t[0]))).toBe(100);
+ expect(Math.max.apply(Math, nonZero.map(t => t[0]))).toBe(300);
+ });
+ });
+
+ describe('when graph is histogram, and xaxis max is equal to the minimum value', () => {
+ beforeEach(() => {
+ setupCtx(() => {
+ ctrl.panel.xaxis.mode = 'histogram';
+ ctrl.panel.xaxis.max = 100;
+ ctrl.panel.stack = false;
+ ctrl.hiddenSeries = {};
+ ctx.data[0] = new TimeSeries({
+ datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
+ alias: 'series1',
+ });
+ });
+ });
+
+ it('should calculate correct histogram', () => {
+ const nonZero = ctx.plotData[0].data.filter(t => t[1] > 0);
+ expect(Math.min.apply(Math, nonZero.map(t => t[0]))).toBe(100);
+ expect(Math.max.apply(Math, nonZero.map(t => t[0]))).toBe(100);
+ });
+ });
+
+ describe('when graph is histogram, and xaxis max is a lower than the minimum value', () => {
+ beforeEach(() => {
+ setupCtx(() => {
+ ctrl.panel.xaxis.mode = 'histogram';
+ ctrl.panel.xaxis.max = 99;
+ ctrl.panel.stack = false;
+ ctrl.hiddenSeries = {};
+ ctx.data[0] = new TimeSeries({
+ datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
+ alias: 'series1',
+ });
+ });
+ });
+
+ it('should calculate empty histogram', () => {
+ const nonZero = ctx.plotData[0].data.filter(t => t[1] > 0);
+ expect(nonZero.length).toBe(0);
+ });
+ });
+
+ describe('when graph is histogram, and xaxis max is greater than the maximum value', () => {
+ beforeEach(() => {
+ setupCtx(() => {
+ ctrl.panel.xaxis.mode = 'histogram';
+ ctrl.panel.xaxis.max = 301;
+ ctrl.panel.stack = false;
+ ctrl.hiddenSeries = {};
+ ctx.data[0] = new TimeSeries({
+ datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
+ alias: 'series1',
+ });
+ });
+ });
+
+ it('should calculate correct histogram', () => {
+ const nonZero = ctx.plotData[0].data.filter(t => t[1] > 0);
+ expect(Math.min.apply(Math, nonZero.map(t => t[0]))).toBe(100);
+ expect(Math.max.apply(Math, nonZero.map(t => t[0]))).toBe(300);
+ });
+ });
});
diff --git a/public/app/plugins/panel/heatmap/color_legend.ts b/public/app/plugins/panel/heatmap/color_legend.ts
index a86a498b723..a1c80629c7e 100644
--- a/public/app/plugins/panel/heatmap/color_legend.ts
+++ b/public/app/plugins/panel/heatmap/color_legend.ts
@@ -69,10 +69,11 @@ coreModule.directive('heatmapLegend', () => {
function render() {
clearLegend(elem);
if (!_.isEmpty(ctrl.data) && !_.isEmpty(ctrl.data.cards)) {
- const rangeFrom = 0;
- const rangeTo = ctrl.data.cardStats.max;
- const maxValue = panel.color.max || rangeTo;
- const minValue = panel.color.min || 0;
+ const cardStats = ctrl.data.cardStats;
+ const rangeFrom = _.isNil(panel.color.min) ? Math.min(cardStats.min, 0) : panel.color.min;
+ const rangeTo = _.isNil(panel.color.max) ? cardStats.max : panel.color.max;
+ const maxValue = cardStats.max;
+ const minValue = cardStats.min;
if (panel.color.mode === 'spectrum') {
const colorScheme = _.find(ctrl.colorSchemes, {
@@ -110,7 +111,7 @@ function drawColorLegend(elem, colorScheme, rangeFrom, rangeTo, maxValue, minVal
.data(valuesRange)
.enter()
.append('rect')
- .attr('x', d => Math.round(d * widthFactor))
+ .attr('x', d => Math.round((d - rangeFrom) * widthFactor))
.attr('y', 0)
.attr('width', Math.round(rangeStep * widthFactor + 1)) // Overlap rectangles to prevent gaps
.attr('height', legendHeight)
@@ -141,7 +142,7 @@ function drawOpacityLegend(elem, options, rangeFrom, rangeTo, maxValue, minValue
.data(valuesRange)
.enter()
.append('rect')
- .attr('x', d => Math.round(d * widthFactor))
+ .attr('x', d => Math.round((d - rangeFrom) * widthFactor))
.attr('y', 0)
.attr('width', Math.round(rangeStep * widthFactor))
.attr('height', legendHeight)
@@ -162,10 +163,10 @@ function drawLegendValues(elem, rangeFrom, rangeTo, maxValue, minValue, legendWi
const legendValueScale = d3
.scaleLinear()
- .domain([0, rangeTo])
+ .domain([rangeFrom, rangeTo])
.range([0, legendWidth]);
- const ticks = buildLegendTicks(0, rangeTo, maxValue, minValue);
+ const ticks = buildLegendTicks(rangeFrom, rangeTo, maxValue, minValue);
const xAxis = d3
.axisBottom(legendValueScale)
.tickValues(ticks)
@@ -286,11 +287,12 @@ function getSvgElemHeight(elem) {
function buildLegendTicks(rangeFrom, rangeTo, maxValue, minValue) {
const range = rangeTo - rangeFrom;
const tickStepSize = tickStep(rangeFrom, rangeTo, 3);
- const ticksNum = Math.round(range / tickStepSize);
+ const ticksNum = Math.ceil(range / tickStepSize);
+ const firstTick = getFirstCloseTick(rangeFrom, tickStepSize);
let ticks = [];
for (let i = 0; i < ticksNum; i++) {
- const current = tickStepSize * i;
+ const current = firstTick + tickStepSize * i;
// Add user-defined min and max if it had been set
if (isValueCloseTo(minValue, current, tickStepSize)) {
ticks.push(minValue);
@@ -304,7 +306,7 @@ function buildLegendTicks(rangeFrom, rangeTo, maxValue, minValue) {
} else if (maxValue < current) {
ticks.push(maxValue);
}
- ticks.push(tickStepSize * i);
+ ticks.push(current);
}
if (!isValueCloseTo(maxValue, rangeTo, tickStepSize)) {
ticks.push(maxValue);
@@ -318,3 +320,10 @@ function isValueCloseTo(val, valueTo, step) {
const diff = Math.abs(val - valueTo);
return diff < step * 0.3;
}
+
+function getFirstCloseTick(minValue, step) {
+ if (minValue < 0) {
+ return Math.floor(minValue / step) * step;
+ }
+ return 0;
+}
diff --git a/public/app/plugins/panel/heatmap/rendering.ts b/public/app/plugins/panel/heatmap/rendering.ts
index 59d704f0d55..702704b1ada 100644
--- a/public/app/plugins/panel/heatmap/rendering.ts
+++ b/public/app/plugins/panel/heatmap/rendering.ts
@@ -524,14 +524,16 @@ export class HeatmapRenderer {
}
const cardsData = this.data.cards;
- const maxValueAuto = this.data.cardStats.max;
- const maxValue = this.panel.color.max || maxValueAuto;
- const minValue = this.panel.color.min || 0;
+ const cardStats = this.data.cardStats;
+ const maxValueAuto = cardStats.max;
+ const minValueAuto = Math.min(cardStats.min, 0);
+ const maxValue = _.isNil(this.panel.color.max) ? maxValueAuto : this.panel.color.max;
+ const minValue = _.isNil(this.panel.color.min) ? minValueAuto : this.panel.color.min;
const colorScheme = _.find(this.ctrl.colorSchemes, {
value: this.panel.color.colorScheme,
});
this.colorScale = getColorScale(colorScheme, contextSrv.user.lightTheme, maxValue, minValue);
- this.opacityScale = getOpacityScale(this.panel.color, maxValue);
+ this.opacityScale = getOpacityScale(this.panel.color, maxValue, minValue);
this.setCardSize();
let cards = this.heatmap.selectAll('.heatmap-card').data(cardsData);
diff --git a/public/app/plugins/panel/table/renderer.ts b/public/app/plugins/panel/table/renderer.ts
index 6b6189f7482..bbc0f3f9ea6 100644
--- a/public/app/plugins/panel/table/renderer.ts
+++ b/public/app/plugins/panel/table/renderer.ts
@@ -1,6 +1,7 @@
import _ from 'lodash';
import moment from 'moment';
import { getValueFormat, getColorFromHexRgbOrName, GrafanaThemeType, stringToJsRegex } from '@grafana/ui';
+import { ColumnStyle } from '@grafana/ui/src/components/Table/TableCellBuilder';
export class TableRenderer {
formatters: any[];
@@ -50,7 +51,7 @@ export class TableRenderer {
}
}
- getColorForValue(value, style) {
+ getColorForValue(value, style: ColumnStyle) {
if (!style.thresholds) {
return null;
}
@@ -62,7 +63,7 @@ export class TableRenderer {
return getColorFromHexRgbOrName(_.first(style.colors), this.theme);
}
- defaultCellFormatter(v, style) {
+ defaultCellFormatter(v, style: ColumnStyle) {
if (v === null || v === void 0 || v === undefined) {
return '';
}
@@ -189,7 +190,7 @@ export class TableRenderer {
};
}
- setColorState(value, style) {
+ setColorState(value, style: ColumnStyle) {
if (!style.colorMode) {
return;
}
diff --git a/public/app/plugins/panel/table2/README.md b/public/app/plugins/panel/table2/README.md
new file mode 100644
index 00000000000..98f2c13f75c
--- /dev/null
+++ b/public/app/plugins/panel/table2/README.md
@@ -0,0 +1,9 @@
+# Table Panel - Native Plugin
+
+The Table Panel is **included** with Grafana.
+
+The table panel is very flexible, supporting both multiple modes for time series as well as for table, annotation and raw JSON data. It also provides date formatting and value formatting and coloring options.
+
+Check out the [Table Panel Showcase in the Grafana Playground](http://play.grafana.org/dashboard/db/table-panel-showcase) or read more about it here:
+
+[http://docs.grafana.org/reference/table_panel/](http://docs.grafana.org/reference/table_panel/)
diff --git a/public/app/plugins/panel/table2/TablePanel.tsx b/public/app/plugins/panel/table2/TablePanel.tsx
new file mode 100644
index 00000000000..a7cd84f8ecb
--- /dev/null
+++ b/public/app/plugins/panel/table2/TablePanel.tsx
@@ -0,0 +1,29 @@
+// Libraries
+import React, { Component } from 'react';
+
+// Types
+import { PanelProps, ThemeContext } from '@grafana/ui';
+import { Options } from './types';
+import Table from '@grafana/ui/src/components/Table/Table';
+
+interface Props extends PanelProps
{}
+
+export class TablePanel extends Component {
+ constructor(props: Props) {
+ super(props);
+ }
+
+ render() {
+ const { panelData, options } = this.props;
+
+ if (!panelData || !panelData.tableData) {
+ return No Table Data...
;
+ }
+
+ return (
+
+ {theme => }
+
+ );
+ }
+}
diff --git a/public/app/plugins/panel/table2/TablePanelEditor.tsx b/public/app/plugins/panel/table2/TablePanelEditor.tsx
new file mode 100644
index 00000000000..ca64cc5e9d0
--- /dev/null
+++ b/public/app/plugins/panel/table2/TablePanelEditor.tsx
@@ -0,0 +1,55 @@
+//// Libraries
+import _ from 'lodash';
+import React, { PureComponent } from 'react';
+
+// Types
+import { PanelEditorProps, Switch, FormField } from '@grafana/ui';
+import { Options } from './types';
+
+export class TablePanelEditor extends PureComponent> {
+ onToggleShowHeader = () => {
+ this.props.onOptionsChange({ ...this.props.options, showHeader: !this.props.options.showHeader });
+ };
+
+ onToggleFixedHeader = () => {
+ this.props.onOptionsChange({ ...this.props.options, fixedHeader: !this.props.options.fixedHeader });
+ };
+
+ onToggleRotate = () => {
+ this.props.onOptionsChange({ ...this.props.options, rotate: !this.props.options.rotate });
+ };
+
+ onFixedColumnsChange = ({ target }) => {
+ this.props.onOptionsChange({ ...this.props.options, fixedColumns: target.value });
+ };
+
+ render() {
+ const { showHeader, fixedHeader, rotate, fixedColumns } = this.props.options;
+
+ return (
+
+
+
Header
+
+
+
+
+
+
Display
+
+
+
+
+ );
+ }
+}
diff --git a/public/app/plugins/panel/table2/img/icn-table-panel.svg b/public/app/plugins/panel/table2/img/icn-table-panel.svg
new file mode 100644
index 00000000000..83097e259dc
--- /dev/null
+++ b/public/app/plugins/panel/table2/img/icn-table-panel.svg
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/app/plugins/panel/table2/module.tsx b/public/app/plugins/panel/table2/module.tsx
new file mode 100644
index 00000000000..d93e7911074
--- /dev/null
+++ b/public/app/plugins/panel/table2/module.tsx
@@ -0,0 +1,9 @@
+import { ReactPanelPlugin } from '@grafana/ui';
+
+import { TablePanelEditor } from './TablePanelEditor';
+import { TablePanel } from './TablePanel';
+import { Options, defaults } from './types';
+
+export const reactPanel = new ReactPanelPlugin(TablePanel);
+reactPanel.setEditor(TablePanelEditor);
+reactPanel.setDefaults(defaults);
diff --git a/public/app/plugins/panel/table2/plugin.json b/public/app/plugins/panel/table2/plugin.json
new file mode 100644
index 00000000000..4fa7728bd55
--- /dev/null
+++ b/public/app/plugins/panel/table2/plugin.json
@@ -0,0 +1,19 @@
+{
+ "type": "panel",
+ "name": "React Table",
+ "id": "table2",
+ "state": "alpha",
+
+ "dataFormats": ["table"],
+
+ "info": {
+ "author": {
+ "name": "Grafana Project",
+ "url": "https://grafana.com"
+ },
+ "logos": {
+ "small": "img/icn-table-panel.svg",
+ "large": "img/icn-table-panel.svg"
+ }
+ }
+}
diff --git a/public/app/plugins/panel/table2/types.ts b/public/app/plugins/panel/table2/types.ts
new file mode 100644
index 00000000000..d58c58810ef
--- /dev/null
+++ b/public/app/plugins/panel/table2/types.ts
@@ -0,0 +1,35 @@
+import { ColumnStyle } from '@grafana/ui/src/components/Table/TableCellBuilder';
+
+export interface Options {
+ showHeader: boolean;
+ fixedHeader: boolean;
+ fixedColumns: number;
+ rotate: boolean;
+
+ styles: ColumnStyle[];
+}
+
+export const defaults: Options = {
+ showHeader: true,
+ fixedHeader: true,
+ fixedColumns: 0,
+ rotate: false,
+ styles: [
+ {
+ type: 'date',
+ pattern: 'Time',
+ alias: 'Time',
+ dateFormat: 'YYYY-MM-DD HH:mm:ss',
+ },
+ {
+ unit: 'short',
+ type: 'number',
+ alias: '',
+ decimals: 2,
+ colors: ['rgba(245, 54, 54, 0.9)', 'rgba(237, 129, 40, 0.89)', 'rgba(50, 172, 45, 0.97)'],
+ colorMode: null,
+ pattern: '/.*/',
+ thresholds: [],
+ },
+ ],
+};
diff --git a/public/app/types/explore.ts b/public/app/types/explore.ts
index 7a6af04b2ee..27894200e51 100644
--- a/public/app/types/explore.ts
+++ b/public/app/types/explore.ts
@@ -4,13 +4,14 @@ import {
RawTimeRange,
TimeRange,
DataQuery,
+ DataQueryResponseData,
DataSourceSelectItem,
DataSourceApi,
QueryHint,
ExploreStartPageProps,
} from '@grafana/ui';
-import { Emitter } from 'app/core/core';
+import { Emitter, TimeSeries } from 'app/core/core';
import { LogsModel, LogsDedupStrategy, LogLevel } from 'app/core/logs_model';
import TableModel from 'app/core/table_model';
@@ -322,6 +323,12 @@ export interface QueryTransaction {
export type RangeScanner = () => RawTimeRange;
+export type ResultGetter = (
+ result: DataQueryResponseData,
+ transaction: QueryTransaction,
+ allTransactions: QueryTransaction[]
+) => TimeSeries;
+
export interface TextMatch {
text: string;
start: number;
diff --git a/public/sass/_variables.generated.scss b/public/sass/_variables.generated.scss
index ddb94f804d4..21fc602b8ba 100644
--- a/public/sass/_variables.generated.scss
+++ b/public/sass/_variables.generated.scss
@@ -55,7 +55,6 @@ $spacers: (
),
),
) !default;
-$border-width: 1px !default;
// Grid breakpoints
//
@@ -88,14 +87,11 @@ $container-max-widths: (
$grid-columns: 12 !default;
$grid-gutter-width: 30px !default;
-$enable-flex: true;
-
// Typography
// -------------------------
$font-family-sans-serif: 'Roboto', Helvetica, Arial, sans-serif;
$font-family-monospace: Menlo, Monaco, Consolas, 'Courier New', monospace;
-$font-family-base: $font-family-sans-serif !default;
$font-size-root: 14px !default;
$font-size-base: 13px !default;
@@ -106,7 +102,9 @@ $font-size-sm: 12px !default;
$font-size-xs: 10px !default;
$line-height-base: 1.5 !default;
-$font-weight-semi-bold: 500;
+
+$font-weight-regular: 400 !default;
+$font-weight-semi-bold: 500 !default;
$font-size-h1: 28px !default;
$font-size-h2: 24px !default;
@@ -116,18 +114,13 @@ $font-size-h5: 16px !default;
$font-size-h6: 14px !default;
$headings-font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif;
-$headings-font-weight: 400 !default;
$headings-line-height: 1.1 !default;
-$hr-border-width: $border-width !default;
-$dt-font-weight: bold !default;
-
// Components
//
// Define common padding and border radius sizes and more.
-$line-height-lg: (4 / 3) !default;
-$line-height-sm: 1.5 !default;
+$border-width: 1px !default;
$border-radius: 3px !default;
$border-radius-lg: 5px !default;
@@ -154,22 +147,17 @@ $input-padding-x: 10px !default;
$input-padding-y: 8px !default;
$input-line-height: 18px !default;
-$input-btn-border-width: 1px;
$input-border-radius: 0 $border-radius $border-radius 0 !default;
$input-border-radius-sm: 0 $border-radius-sm $border-radius-sm 0 !default;
$label-border-radius: $border-radius 0 0 $border-radius !default;
$label-border-radius-sm: $border-radius-sm 0 0 $border-radius-sm !default;
-$input-padding-y-sm: 4px !default;
-
$input-padding-x-lg: 20px !default;
$input-padding-y-lg: 10px !default;
$input-height: 35px !default;
-$gf-form-input-height: 35px;
-
$cursor-disabled: not-allowed !default;
// Form validation icons
@@ -206,8 +194,6 @@ $btn-padding-y-lg: 11px !default;
$btn-padding-x-xl: 21px !default;
$btn-padding-y-xl: 11px !default;
-$btn-border-radius: 2px;
-
$btn-semi-transparent: rgba(0, 0, 0, 0.2) !default;
// sidemenu
diff --git a/public/sass/base/_forms.scss b/public/sass/base/_forms.scss
index 76d936c18ec..f58e75f41b2 100644
--- a/public/sass/base/_forms.scss
+++ b/public/sass/base/_forms.scss
@@ -37,7 +37,7 @@ input,
button,
select,
textarea {
- font-family: $font-family-base; // And only set font-family here for those that need it (note the missing label element)
+ font-family: $font-family-sans-serif; // And only set font-family here for those that need it (note the missing label element)
}
// Identify controls by their labels
diff --git a/public/sass/base/_reboot.scss b/public/sass/base/_reboot.scss
index 65cfafc107d..3b05bff61bf 100644
--- a/public/sass/base/_reboot.scss
+++ b/public/sass/base/_reboot.scss
@@ -70,7 +70,7 @@ html {
body {
// Make the `body` use the `font-size-root`
- font-family: $font-family-base;
+ font-family: $font-family-sans-serif;
font-size: $font-size-base;
line-height: $line-height-base;
// Go easy on the eyes and use something other than `#000` for text
@@ -145,7 +145,7 @@ ul ol {
}
dt {
- font-weight: $dt-font-weight;
+ font-weight: $font-weight-semi-bold;
}
dd {
diff --git a/public/sass/base/_type.scss b/public/sass/base/_type.scss
index 6cf32687188..9efae56d5e4 100644
--- a/public/sass/base/_type.scss
+++ b/public/sass/base/_type.scss
@@ -111,7 +111,7 @@ h6,
.h6 {
margin-bottom: $space-sm;
font-family: $headings-font-family;
- font-weight: $headings-font-weight;
+ font-weight: $font-weight-regular;
line-height: $headings-line-height;
color: $headings-color;
}
@@ -149,7 +149,7 @@ hr {
margin-top: $spacer-y;
margin-bottom: $spacer-y;
border: 0;
- border-top: $hr-border-width solid $hr-border-color;
+ border-top: $border-width solid $hr-border-color;
}
//
diff --git a/public/sass/components/_buttons.scss b/public/sass/components/_buttons.scss
index 0c1ac726690..f85fecec50c 100644
--- a/public/sass/components/_buttons.scss
+++ b/public/sass/components/_buttons.scss
@@ -16,7 +16,7 @@
cursor: pointer;
border: none;
- @include button-size($btn-padding-y, $btn-padding-x, $font-size-base, $btn-border-radius);
+ @include button-size($btn-padding-y, $btn-padding-x, $font-size-base, $border-radius-sm);
&,
&:active,
@@ -53,7 +53,7 @@
// --------------------------------------------------
// XLarge
.btn-xlarge {
- @include button-size($btn-padding-y-xl, $btn-padding-x-xl, $font-size-lg, $btn-border-radius);
+ @include button-size($btn-padding-y-xl, $btn-padding-x-xl, $font-size-lg, $border-radius-sm);
font-weight: normal;
padding-bottom: $btn-padding-y-xl - 3;
.gicon {
@@ -64,16 +64,16 @@
// Large
.btn-large {
- @include button-size($btn-padding-y-lg, $btn-padding-x-lg, $font-size-lg, $btn-border-radius);
+ @include button-size($btn-padding-y-lg, $btn-padding-x-lg, $font-size-lg, $border-radius-sm);
font-weight: normal;
}
.btn-small {
- @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $font-size-sm, $btn-border-radius);
+ @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $font-size-sm, $border-radius-sm);
}
.btn-mini {
- @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $font-size-xs, $btn-border-radius);
+ @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $font-size-xs, $border-radius-sm);
}
.btn-link {
diff --git a/public/sass/components/_code_editor.scss b/public/sass/components/_code_editor.scss
index a9c7ebf2e75..832449e31f1 100644
--- a/public/sass/components/_code_editor.scss
+++ b/public/sass/components/_code_editor.scss
@@ -10,7 +10,7 @@
min-height: 3.6rem; // Include space for horizontal scrollbar
@include border-radius($input-border-radius-sm);
- border: $input-btn-border-width solid $input-border-color;
+ border: $border-width solid $input-border-color;
}
.ace_content {
diff --git a/public/sass/components/_gf-form.scss b/public/sass/components/_gf-form.scss
index 2eccdd52e8b..f62c2fcb7a0 100644
--- a/public/sass/components/_gf-form.scss
+++ b/public/sass/components/_gf-form.scss
@@ -105,9 +105,9 @@ $input-border: 1px solid $input-border-color;
background-color: $input-label-bg;
display: block;
- height: $gf-form-input-height;
+ height: $input-height;
- border: $input-btn-border-width solid $input-label-border-color;
+ border: $border-width solid $input-label-border-color;
border-right: none;
border-radius: $label-border-radius;
@@ -127,7 +127,7 @@ $input-border: 1px solid $input-border-color;
}
&--btn {
- border-right: $input-btn-border-width solid $input-label-border-color;
+ border-right: $border-width solid $input-label-border-color;
border-radius: $border-radius;
&:hover {
@@ -154,7 +154,7 @@ $input-border: 1px solid $input-border-color;
flex-grow: 1;
margin: 0;
margin-right: $space-xs;
- border: $input-btn-border-width solid transparent;
+ border: $border-width solid transparent;
border-left: none;
@include border-radius($label-border-radius-sm);
}
@@ -166,7 +166,7 @@ $input-border: 1px solid $input-border-color;
.gf-form-input {
display: block;
width: 100%;
- height: $gf-form-input-height;
+ height: $input-height;
padding: $input-padding-y $input-padding-x;
font-size: $font-size-md;
line-height: $input-line-height;
diff --git a/public/sass/components/_panel_gettingstarted.scss b/public/sass/components/_panel_gettingstarted.scss
index fe2f3704b0b..7e23550dda6 100644
--- a/public/sass/components/_panel_gettingstarted.scss
+++ b/public/sass/components/_panel_gettingstarted.scss
@@ -117,7 +117,7 @@ $path-position: $marker-size-half - ($path-height / 2);
}
.progress-step-cta {
- @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $font-size-sm, $btn-border-radius);
+ @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $font-size-sm, $border-radius-sm);
@include buttonBackground($btn-primary-bg, $btn-primary-bg-hl);
display: none;
}
diff --git a/public/sass/components/_slate_editor.scss b/public/sass/components/_slate_editor.scss
index 9dfdb685466..db09b8fffd7 100644
--- a/public/sass/components/_slate_editor.scss
+++ b/public/sass/components/_slate_editor.scss
@@ -9,7 +9,7 @@
position: relative;
display: inline-block;
padding: $input-padding-y $input-padding-x;
- min-height: $gf-form-input-height;
+ min-height: $input-height;
width: 100%;
cursor: text;
line-height: $line-height-base;
diff --git a/public/sass/components/_submenu.scss b/public/sass/components/_submenu.scss
index 514aa44525e..2f2a9294799 100644
--- a/public/sass/components/_submenu.scss
+++ b/public/sass/components/_submenu.scss
@@ -42,7 +42,7 @@
border-radius: $input-border-radius;
display: inline-block;
color: $text-color;
- height: $gf-form-input-height;
+ height: $input-height;
.label-tag {
margin: 0 5px;
diff --git a/public/sass/components/_switch.scss b/public/sass/components/_switch.scss
index f7980215659..09b32463370 100644
--- a/public/sass/components/_switch.scss
+++ b/public/sass/components/_switch.scss
@@ -26,7 +26,7 @@ gf-form-switch[disabled] {
display: flex;
position: relative;
width: 60px;
- height: $gf-form-input-height;
+ height: $input-height;
background: $switch-bg;
border: 1px solid $input-border-color;
border-left: none;
@@ -82,7 +82,7 @@ input:checked + .gf-form-switch__slider::before {
position: relative;
display: flex;
width: 50px;
- height: $gf-form-input-height;
+ height: $input-height;
background: $switch-bg;
border: 1px solid $input-border-color;
border-left: none;
diff --git a/public/sass/components/_toolbar.scss b/public/sass/components/_toolbar.scss
index 36be8a18739..905a8ef6829 100644
--- a/public/sass/components/_toolbar.scss
+++ b/public/sass/components/_toolbar.scss
@@ -27,7 +27,7 @@
line-height: $input-line-height;
color: $input-color;
background-color: $input-bg;
- height: $gf-form-input-height;
+ height: $input-height;
border: $input-border;
border-radius: $input-border-radius;
display: flex;
diff --git a/public/sass/utils/_utils.scss b/public/sass/utils/_utils.scss
index 16a84784de8..41a0df5f363 100644
--- a/public/sass/utils/_utils.scss
+++ b/public/sass/utils/_utils.scss
@@ -96,7 +96,9 @@ button.close {
}
.center-vh {
+ height: 100%;
display: flex;
+ flex-direction: column;
align-items: center;
justify-content: center;
justify-items: center;
diff --git a/scripts/ci-frontend-metrics.sh b/scripts/ci-frontend-metrics.sh
new file mode 100755
index 00000000000..49786b752af
--- /dev/null
+++ b/scripts/ci-frontend-metrics.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+echo -e "Collecting code stats (typescript errors & more)"
+
+ERROR_COUNT="$(./node_modules/.bin/tsc --project tsconfig.json --noEmit --noImplicitAny true | grep -oP 'Found \K(\d+)')"
+DIRECTIVES="$(grep -r -o directive public/app/**/* | wc -l)"
+CONTROLLERS="$(grep -r -oP 'class .*Ctrl' public/app/**/* | wc -l)"
+
+echo -e "Typescript errors: $ERROR_COUNT"
+echo -e "Directives: $DIRECTIVES"
+echo -e "Controllers: $CONTROLLERS"
+
+./scripts/ci-metrics-publisher.sh \
+ grafana.ci-code.noImplicitAny=$ERROR_COUNT \
+ grafana.ci-code.directives=$DIRECTIVES \
+ grafana.ci-code.controllers=$CONTROLLERS \
+
+
+
diff --git a/scripts/ci-metrics-publisher.sh b/scripts/ci-metrics-publisher.sh
new file mode 100755
index 00000000000..bb7d042caf1
--- /dev/null
+++ b/scripts/ci-metrics-publisher.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+echo "Publishing CI Metrics"
+
+data=""
+
+for ((i = 1; i <= $#; i++ )); do
+ remainder="${!i}"
+ first="${remainder%%=*}"; remainder="${remainder#*=}"
+ if [ -n "$data" ]; then
+ data="$data,"
+ fi
+ data=''$data'{"name": "'${first}'", "value": '${remainder}', "interval": 60, "mtype": "gauge", "time": '$(date +%s)'}'
+done
+
+curl https://6371:$GRAFANA_MISC_STATS_API_KEY@graphite-us-central1.grafana.net/metrics \
+ -H 'Content-type: application/json' \
+ -d "[$data]"
diff --git a/scripts/circle-metrics.sh b/scripts/circle-metrics.sh
deleted file mode 100755
index b52347d0755..00000000000
--- a/scripts/circle-metrics.sh
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-
-echo "Collecting code stats (typescript errors & more)"
-
-ERROR_COUNT="$(./node_modules/.bin/tsc --project tsconfig.json --noEmit --noImplicitAny true | grep -oP 'Found \K(\d+)')"
-DIRECTIVES="$(grep -r -o directive public/app/**/* | wc -l)"
-CONTROLLERS="$(grep -r -oP 'class .*Ctrl' public/app/**/* | wc -l)"
-
-echo "Typescript errors: $ERROR_COUNT"
-echo "Directives: $DIRECTIVES"
-echo "Controllers: $CONTROLLERS"
-
-curl \
- -d "{\"metrics\": {
- \"ci.code.noImplicitAny\": $ERROR_COUNT,
- \"ci.code.directives\": $DIRECTIVES,
- \"ci.code.controllers\": $CONTROLLERS
- }
- }" \
- -H "Content-Type: application/json" \
- -u ci:$CIRCLE_STATS_PWD \
- -X POST https://stats.grafana.org/metric-receiver
-
-curl https://6371:$GRAFANA_MISC_STATS_API_KEY@graphite-us-central1.grafana.net/metrics \
- -H 'Content-type: application/json' \
- -d '[
- {"name":"grafana.ci-code.noImplicitAny", "interval":60, "value": '$ERROR_COUNT', "mtype": "gauge", "time": '$(date +%s)'},
- {"name":"grafana.ci-code.directives", "interval":60, "value": '$DIRECTIVES', "mtype": "gauge", "time": '$(date +%s)'},
- {"name":"grafana.ci-code.controllers", "interval":60, "value": '$CONTROLLERS', "mtype": "gauge", "time": '$(date +%s)'}
- ]'
diff --git a/scripts/circle-test-frontend.sh b/scripts/circle-test-frontend.sh
index 3366bf3d4fb..9d945a03b7f 100755
--- a/scripts/circle-test-frontend.sh
+++ b/scripts/circle-test-frontend.sh
@@ -1,4 +1,5 @@
#!/bin/bash
+
function exit_if_fail {
command=$@
echo "Executing '$command'"
@@ -10,11 +11,16 @@ function exit_if_fail {
fi
}
-exit_if_fail npm run prettier:check
-exit_if_fail npm run test
+start=$(date +%s)
-# On master also collect some and send some metrics
-branch="$(git rev-parse --abbrev-ref HEAD)"
-if [ "${branch}" == "master" ]; then
- exit_if_fail ./scripts/circle-metrics.sh
+exit_if_fail npm run prettier:check
+# exit_if_fail npm run test
+
+end=$(date +%s)
+seconds=$((end - start))
+
+if [ "${CIRCLE_BRANCH}" == "master" ]; then
+ exit_if_fail ./scripts/ci-frontend-metrics.sh
+ exit_if_fail ./scripts/ci-metrics-publisher.sh grafana.ci-performance.frontend-tests=$seconds
fi
+