Merge branch 'master' into dashboard_tag_colors
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
- [Issue #578](https://github.com/grafana/grafana/issues/578). Dashboard: Row option to display row title even when the row is visible
|
- [Issue #578](https://github.com/grafana/grafana/issues/578). Dashboard: Row option to display row title even when the row is visible
|
||||||
- [Issue #672](https://github.com/grafana/grafana/issues/672). Dashboard: panel fullscreen & edit state is present in url, can now link to graph in edit & fullscreen mode.
|
- [Issue #672](https://github.com/grafana/grafana/issues/672). Dashboard: panel fullscreen & edit state is present in url, can now link to graph in edit & fullscreen mode.
|
||||||
- [Issue #709](https://github.com/grafana/grafana/issues/709). Dashboard: Small UI look polish to search results, made dashboard title link are larger
|
- [Issue #709](https://github.com/grafana/grafana/issues/709). Dashboard: Small UI look polish to search results, made dashboard title link are larger
|
||||||
|
- [Issue #425](https://github.com/grafana/grafana/issues/425). Graph: New section in 'Display Styles' tab to override any display setting on per series bases (mix and match lines, bars, points, fill, stack, line width etc)
|
||||||
|
|
||||||
**Fixes**
|
**Fixes**
|
||||||
- [Issue #696](https://github.com/grafana/grafana/issues/696). Graph: Fix for y-axis format 'none' when values are in scientific notation (ex 2.3e-13)
|
- [Issue #696](https://github.com/grafana/grafana/issues/696). Graph: Fix for y-axis format 'none' when values are in scientific notation (ex 2.3e-13)
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@
|
|||||||
"grunt-string-replace": "~0.2.4",
|
"grunt-string-replace": "~0.2.4",
|
||||||
"grunt-usemin": "^2.1.1",
|
"grunt-usemin": "^2.1.1",
|
||||||
"jshint-stylish": "~0.1.5",
|
"jshint-stylish": "~0.1.5",
|
||||||
"karma": "~0.12.16",
|
"karma": "~0.12.21",
|
||||||
"karma-chrome-launcher": "~0.1.4",
|
"karma-chrome-launcher": "~0.1.4",
|
||||||
"karma-coffee-preprocessor": "~0.1.2",
|
"karma-coffee-preprocessor": "~0.1.2",
|
||||||
"karma-coverage": "^0.2.5",
|
"karma-coverage": "^0.2.5",
|
||||||
|
|||||||
+14
-14
@@ -231,36 +231,36 @@ function($, _, moment) {
|
|||||||
if (type === 0) {
|
if (type === 0) {
|
||||||
roundUp ? dateTime.endOf('year') : dateTime.startOf('year');
|
roundUp ? dateTime.endOf('year') : dateTime.startOf('year');
|
||||||
} else if (type === 1) {
|
} else if (type === 1) {
|
||||||
dateTime.add('years',num);
|
dateTime.add(num, 'years');
|
||||||
} else if (type === 2) {
|
} else if (type === 2) {
|
||||||
dateTime.subtract('years',num);
|
dateTime.subtract(num, 'years');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'M':
|
case 'M':
|
||||||
if (type === 0) {
|
if (type === 0) {
|
||||||
roundUp ? dateTime.endOf('month') : dateTime.startOf('month');
|
roundUp ? dateTime.endOf('month') : dateTime.startOf('month');
|
||||||
} else if (type === 1) {
|
} else if (type === 1) {
|
||||||
dateTime.add('months',num);
|
dateTime.add(num, 'months');
|
||||||
} else if (type === 2) {
|
} else if (type === 2) {
|
||||||
dateTime.subtract('months',num);
|
dateTime.subtract(num, 'months');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'w':
|
case 'w':
|
||||||
if (type === 0) {
|
if (type === 0) {
|
||||||
roundUp ? dateTime.endOf('week') : dateTime.startOf('week');
|
roundUp ? dateTime.endOf('week') : dateTime.startOf('week');
|
||||||
} else if (type === 1) {
|
} else if (type === 1) {
|
||||||
dateTime.add('weeks',num);
|
dateTime.add(num, 'weeks');
|
||||||
} else if (type === 2) {
|
} else if (type === 2) {
|
||||||
dateTime.subtract('weeks',num);
|
dateTime.subtract(num, 'weeks');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
if (type === 0) {
|
if (type === 0) {
|
||||||
roundUp ? dateTime.endOf('day') : dateTime.startOf('day');
|
roundUp ? dateTime.endOf('day') : dateTime.startOf('day');
|
||||||
} else if (type === 1) {
|
} else if (type === 1) {
|
||||||
dateTime.add('days',num);
|
dateTime.add(num, 'days');
|
||||||
} else if (type === 2) {
|
} else if (type === 2) {
|
||||||
dateTime.subtract('days',num);
|
dateTime.subtract(num, 'days');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
@@ -268,27 +268,27 @@ function($, _, moment) {
|
|||||||
if (type === 0) {
|
if (type === 0) {
|
||||||
roundUp ? dateTime.endOf('hour') : dateTime.startOf('hour');
|
roundUp ? dateTime.endOf('hour') : dateTime.startOf('hour');
|
||||||
} else if (type === 1) {
|
} else if (type === 1) {
|
||||||
dateTime.add('hours',num);
|
dateTime.add(num, 'hours');
|
||||||
} else if (type === 2) {
|
} else if (type === 2) {
|
||||||
dateTime.subtract('hours',num);
|
dateTime.subtract(num,'hours');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
if (type === 0) {
|
if (type === 0) {
|
||||||
roundUp ? dateTime.endOf('minute') : dateTime.startOf('minute');
|
roundUp ? dateTime.endOf('minute') : dateTime.startOf('minute');
|
||||||
} else if (type === 1) {
|
} else if (type === 1) {
|
||||||
dateTime.add('minutes',num);
|
dateTime.add(num, 'minutes');
|
||||||
} else if (type === 2) {
|
} else if (type === 2) {
|
||||||
dateTime.subtract('minutes',num);
|
dateTime.subtract(num, 'minutes');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
if (type === 0) {
|
if (type === 0) {
|
||||||
roundUp ? dateTime.endOf('second') : dateTime.startOf('second');
|
roundUp ? dateTime.endOf('second') : dateTime.startOf('second');
|
||||||
} else if (type === 1) {
|
} else if (type === 1) {
|
||||||
dateTime.add('seconds',num);
|
dateTime.add(num, 'seconds');
|
||||||
} else if (type === 2) {
|
} else if (type === 2) {
|
||||||
dateTime.subtract('seconds',num);
|
dateTime.subtract(num, 'seconds');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ function (_, crypto) {
|
|||||||
default_route : '/dashboard/file/default.json',
|
default_route : '/dashboard/file/default.json',
|
||||||
playlist_timespan : "1m",
|
playlist_timespan : "1m",
|
||||||
unsaved_changes_warning : true,
|
unsaved_changes_warning : true,
|
||||||
search : { max_results: 20 },
|
search : { max_results: 16 },
|
||||||
admin : {}
|
admin : {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,15 +5,57 @@ define([
|
|||||||
function (_, kbn) {
|
function (_, kbn) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var ts = {};
|
function TimeSeries(opts) {
|
||||||
|
|
||||||
ts.ZeroFilled = function (opts) {
|
|
||||||
this.datapoints = opts.datapoints;
|
this.datapoints = opts.datapoints;
|
||||||
this.info = opts.info;
|
this.info = opts.info;
|
||||||
this.label = opts.info.alias;
|
this.label = opts.info.alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
function matchSeriesOverride(aliasOrRegex, seriesAlias) {
|
||||||
|
if (!aliasOrRegex) { return false; }
|
||||||
|
|
||||||
|
if (aliasOrRegex[0] === '/') {
|
||||||
|
var match = aliasOrRegex.match(new RegExp('^/(.*?)/(g?i?m?y?)$'));
|
||||||
|
var regex = new RegExp(match[1], match[2]);
|
||||||
|
return seriesAlias.match(regex) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return aliasOrRegex === seriesAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
function translateFillOption(fill) {
|
||||||
|
return fill === 0 ? 0.001 : fill/10;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeSeries.prototype.applySeriesOverrides = function(overrides) {
|
||||||
|
this.lines = {};
|
||||||
|
this.points = {};
|
||||||
|
this.bars = {};
|
||||||
|
this.info.yaxis = 1;
|
||||||
|
this.zindex = 0;
|
||||||
|
delete this.stack;
|
||||||
|
|
||||||
|
for (var i = 0; i < overrides.length; i++) {
|
||||||
|
var override = overrides[i];
|
||||||
|
if (!matchSeriesOverride(override.alias, this.info.alias)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (override.lines !== void 0) { this.lines.show = override.lines; }
|
||||||
|
if (override.points !== void 0) { this.points.show = override.points; }
|
||||||
|
if (override.bars !== void 0) { this.bars.show = override.bars; }
|
||||||
|
if (override.fill !== void 0) { this.lines.fill = translateFillOption(override.fill); }
|
||||||
|
if (override.stack !== void 0) { this.stack = override.stack; }
|
||||||
|
if (override.linewidth !== void 0) { this.lines.lineWidth = override.linewidth; }
|
||||||
|
if (override.pointradius !== void 0) { this.points.radius = override.pointradius; }
|
||||||
|
if (override.steppedLine !== void 0) { this.lines.steps = override.steppedLine; }
|
||||||
|
if (override.zindex !== void 0) { this.zindex = override.zindex; }
|
||||||
|
if (override.yaxis !== void 0) {
|
||||||
|
this.info.yaxis = override.yaxis;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ts.ZeroFilled.prototype.getFlotPairs = function (fillStyle, yFormats) {
|
TimeSeries.prototype.getFlotPairs = function (fillStyle, yFormats) {
|
||||||
var result = [];
|
var result = [];
|
||||||
|
|
||||||
this.color = this.info.color;
|
this.color = this.info.color;
|
||||||
@@ -74,5 +116,6 @@ function (_, kbn) {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
return ts;
|
return TimeSeries;
|
||||||
});
|
|
||||||
|
});
|
||||||
@@ -13,7 +13,7 @@ function (angular, $, config, _) {
|
|||||||
module.controller('DashboardCtrl', function(
|
module.controller('DashboardCtrl', function(
|
||||||
$scope, $rootScope, dashboardKeybindings,
|
$scope, $rootScope, dashboardKeybindings,
|
||||||
filterSrv, dashboardSrv, dashboardViewStateSrv,
|
filterSrv, dashboardSrv, dashboardViewStateSrv,
|
||||||
panelMoveSrv, timer) {
|
panelMoveSrv, timer, $timeout) {
|
||||||
|
|
||||||
$scope.editor = { index: 0 };
|
$scope.editor = { index: 0 };
|
||||||
$scope.panelNames = config.panels;
|
$scope.panelNames = config.panels;
|
||||||
@@ -21,6 +21,13 @@ function (angular, $, config, _) {
|
|||||||
$scope.init = function() {
|
$scope.init = function() {
|
||||||
$scope.availablePanels = config.panels;
|
$scope.availablePanels = config.panels;
|
||||||
$scope.onAppEvent('setup-dashboard', $scope.setupDashboard);
|
$scope.onAppEvent('setup-dashboard', $scope.setupDashboard);
|
||||||
|
|
||||||
|
angular.element(window).bind('resize', function() {
|
||||||
|
$timeout(function() {
|
||||||
|
$scope.$broadcast('render');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.setupDashboard = function(event, dashboardData) {
|
$scope.setupDashboard = function(event, dashboardData) {
|
||||||
|
|||||||
@@ -97,4 +97,4 @@ function (angular, app, _, $, gfunc) {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -46,11 +46,6 @@ function (angular, $, kbn, moment, _) {
|
|||||||
render_panel();
|
render_panel();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Re-render if the window is resized
|
|
||||||
angular.element(window).bind('resize', function() {
|
|
||||||
render_panel();
|
|
||||||
});
|
|
||||||
|
|
||||||
function setElementHeight() {
|
function setElementHeight() {
|
||||||
try {
|
try {
|
||||||
var height = scope.height || scope.panel.height || scope.row.height;
|
var height = scope.height || scope.panel.height || scope.row.height;
|
||||||
@@ -118,7 +113,7 @@ function (angular, $, kbn, moment, _) {
|
|||||||
lines: {
|
lines: {
|
||||||
show: panel.lines,
|
show: panel.lines,
|
||||||
zero: false,
|
zero: false,
|
||||||
fill: panel.fill === 0 ? 0.001 : panel.fill/10,
|
fill: translateFillOption(panel.fill),
|
||||||
lineWidth: panel.linewidth,
|
lineWidth: panel.linewidth,
|
||||||
steps: panel.steppedLine
|
steps: panel.steppedLine
|
||||||
},
|
},
|
||||||
@@ -154,11 +149,12 @@ function (angular, $, kbn, moment, _) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (var i = 0; i < data.length; i++) {
|
for (var i = 0; i < data.length; i++) {
|
||||||
var _d = data[i].getFlotPairs(panel.nullPointMode, panel.y_formats);
|
var series = data[i];
|
||||||
data[i].data = _d;
|
series.applySeriesOverrides(panel.seriesOverrides);
|
||||||
|
series.data = series.getFlotPairs(panel.nullPointMode, panel.y_formats);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (panel.bars && data.length && data[0].info.timeStep) {
|
if (data.length && data[0].info.timeStep) {
|
||||||
options.series.bars.barWidth = data[0].info.timeStep / 1.5;
|
options.series.bars.barWidth = data[0].info.timeStep / 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,21 +163,27 @@ function (angular, $, kbn, moment, _) {
|
|||||||
addAnnotations(options);
|
addAnnotations(options);
|
||||||
configureAxisOptions(data, options);
|
configureAxisOptions(data, options);
|
||||||
|
|
||||||
|
var sortedSeries = _.sortBy(data, function(series) { return series.zindex; });
|
||||||
|
|
||||||
// if legend is to the right delay plot draw a few milliseconds
|
// if legend is to the right delay plot draw a few milliseconds
|
||||||
// so the legend width calculation can be done
|
// so the legend width calculation can be done
|
||||||
if (shouldDelayDraw(panel)) {
|
if (shouldDelayDraw(panel)) {
|
||||||
legendSideLastValue = panel.legend.rightSide;
|
legendSideLastValue = panel.legend.rightSide;
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
plot = $.plot(elem, data, options);
|
plot = $.plot(elem, sortedSeries, options);
|
||||||
addAxisLabels();
|
addAxisLabels();
|
||||||
}, 50);
|
}, 50);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
plot = $.plot(elem, data, options);
|
plot = $.plot(elem, sortedSeries, options);
|
||||||
addAxisLabels();
|
addAxisLabels();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function translateFillOption(fill) {
|
||||||
|
return fill === 0 ? 0.001 : fill/10;
|
||||||
|
}
|
||||||
|
|
||||||
function shouldDelayDraw(panel) {
|
function shouldDelayDraw(panel) {
|
||||||
if (panel.legend.rightSide) {
|
if (panel.legend.rightSide) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -34,12 +34,12 @@
|
|||||||
|
|
||||||
<div class="editor-row small" style="padding-bottom: 0;">
|
<div class="editor-row small" style="padding-bottom: 0;">
|
||||||
<label>Axis:</label>
|
<label>Axis:</label>
|
||||||
<button ng-click="toggleYAxis(series)"
|
<button ng-click="toggleYAxis(series);dismiss();"
|
||||||
class="btn btn-mini"
|
class="btn btn-mini"
|
||||||
ng-class="{'btn-success': series.yaxis === 1 }">
|
ng-class="{'btn-success': series.yaxis === 1 }">
|
||||||
Left
|
Left
|
||||||
</button>
|
</button>
|
||||||
<button ng-click="toggleYAxis(series)"
|
<button ng-click="toggleYAxis(series);dismiss();"
|
||||||
class="btn btn-mini"
|
class="btn btn-mini"
|
||||||
ng-class="{'btn-success': series.yaxis === 2 }">
|
ng-class="{'btn-success': series.yaxis === 2 }">
|
||||||
Right
|
Right
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-content" ng-repeat="tab in panelMeta.fullEditorTabs" ng-show="editorTabs[editor.index] == tab.title">
|
<div class="tab-content" ng-repeat="tab in panelMeta.fullEditorTabs" ng-if="editorTabs[editor.index] == tab.title">
|
||||||
<div ng-include src="tab.src"></div>
|
<div ng-include src="tab.src"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,16 +1,3 @@
|
|||||||
/** @scratch /panels/5
|
|
||||||
* include::panels/histogram.asciidoc[]
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @scratch /panels/histogram/0
|
|
||||||
* == Histogram
|
|
||||||
* Status: *Stable*
|
|
||||||
*
|
|
||||||
* The histogram panel allow for the display of time charts. It includes several modes and tranformations
|
|
||||||
* to display event counts, mean, min, max and total of numeric fields, and derivatives of counter
|
|
||||||
* fields.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
define([
|
define([
|
||||||
'angular',
|
'angular',
|
||||||
'app',
|
'app',
|
||||||
@@ -18,7 +5,8 @@ define([
|
|||||||
'lodash',
|
'lodash',
|
||||||
'kbn',
|
'kbn',
|
||||||
'moment',
|
'moment',
|
||||||
'./timeSeries',
|
'components/timeSeries',
|
||||||
|
'./seriesOverridesCtrl',
|
||||||
'services/panelSrv',
|
'services/panelSrv',
|
||||||
'services/annotationsSrv',
|
'services/annotationsSrv',
|
||||||
'services/datasourceSrv',
|
'services/datasourceSrv',
|
||||||
@@ -29,11 +17,10 @@ define([
|
|||||||
'jquery.flot.stack',
|
'jquery.flot.stack',
|
||||||
'jquery.flot.stackpercent'
|
'jquery.flot.stackpercent'
|
||||||
],
|
],
|
||||||
function (angular, app, $, _, kbn, moment, timeSeries) {
|
function (angular, app, $, _, kbn, moment, TimeSeries) {
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var module = angular.module('grafana.panels.graph', []);
|
var module = angular.module('grafana.panels.graph');
|
||||||
app.useModule(module);
|
app.useModule(module);
|
||||||
|
|
||||||
module.controller('GraphCtrl', function($scope, $rootScope, $timeout, panelSrv, annotationsSrv) {
|
module.controller('GraphCtrl', function($scope, $rootScope, $timeout, panelSrv, annotationsSrv) {
|
||||||
@@ -179,7 +166,8 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
|
|||||||
targets: [{}],
|
targets: [{}],
|
||||||
|
|
||||||
aliasColors: {},
|
aliasColors: {},
|
||||||
aliasYAxis: {},
|
|
||||||
|
seriesOverrides: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
_.defaults($scope.panel,_d);
|
_.defaults($scope.panel,_d);
|
||||||
@@ -258,18 +246,15 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
|
|||||||
var datapoints = seriesData.datapoints;
|
var datapoints = seriesData.datapoints;
|
||||||
var alias = seriesData.target;
|
var alias = seriesData.target;
|
||||||
var color = $scope.panel.aliasColors[alias] || $rootScope.colors[index];
|
var color = $scope.panel.aliasColors[alias] || $rootScope.colors[index];
|
||||||
var yaxis = $scope.panel.aliasYAxis[alias] || 1;
|
|
||||||
|
|
||||||
var seriesInfo = {
|
var seriesInfo = {
|
||||||
alias: alias,
|
alias: alias,
|
||||||
color: color,
|
color: color,
|
||||||
enable: true,
|
|
||||||
yaxis: yaxis
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.legend.push(seriesInfo);
|
$scope.legend.push(seriesInfo);
|
||||||
|
|
||||||
var series = new timeSeries.ZeroFilled({
|
var series = new TimeSeries({
|
||||||
datapoints: datapoints,
|
datapoints: datapoints,
|
||||||
info: seriesInfo,
|
info: seriesInfo,
|
||||||
});
|
});
|
||||||
@@ -347,8 +332,12 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
$scope.toggleYAxis = function(info) {
|
$scope.toggleYAxis = function(info) {
|
||||||
info.yaxis = info.yaxis === 2 ? 1 : 2;
|
var override = _.findWhere($scope.panel.seriesOverrides, { alias: info.alias });
|
||||||
$scope.panel.aliasYAxis[info.alias] = info.yaxis;
|
if (!override) {
|
||||||
|
override = { alias: info.alias };
|
||||||
|
$scope.panel.seriesOverrides.push(override);
|
||||||
|
}
|
||||||
|
override.yaxis = info.yaxis === 2 ? 1 : 2;
|
||||||
$scope.render();
|
$scope.render();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -357,6 +346,15 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
|
|||||||
$scope.render();
|
$scope.render();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.addSeriesOverride = function() {
|
||||||
|
$scope.panel.seriesOverrides.push({});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.removeSeriesOverride = function(override) {
|
||||||
|
$scope.panel.seriesOverrides = _.without($scope.panel.seriesOverrides, override);
|
||||||
|
$scope.render();
|
||||||
|
};
|
||||||
|
|
||||||
panelSrv.init($scope);
|
panelSrv.init($scope);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
define([
|
||||||
|
'angular',
|
||||||
|
'app',
|
||||||
|
'lodash',
|
||||||
|
], function(angular, app, _) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var module = angular.module('grafana.panels.graph', []);
|
||||||
|
app.useModule(module);
|
||||||
|
|
||||||
|
module.controller('SeriesOverridesCtrl', function($scope) {
|
||||||
|
$scope.overrideMenu = [];
|
||||||
|
$scope.currentOverrides = [];
|
||||||
|
$scope.override = $scope.override || {};
|
||||||
|
|
||||||
|
$scope.addOverrideOption = function(name, propertyName, values) {
|
||||||
|
var option = {};
|
||||||
|
option.text = name;
|
||||||
|
option.propertyName = propertyName;
|
||||||
|
option.index = $scope.overrideMenu.length;
|
||||||
|
option.values = values;
|
||||||
|
|
||||||
|
option.submenu = _.map(values, function(value, index) {
|
||||||
|
return {
|
||||||
|
text: String(value),
|
||||||
|
click: 'setOverride(' + option.index + ',' + index + ')'
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.overrideMenu.push(option);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.setOverride = function(optionIndex, valueIndex) {
|
||||||
|
var option = $scope.overrideMenu[optionIndex];
|
||||||
|
var value = option.values[valueIndex];
|
||||||
|
$scope.override[option.propertyName] = value;
|
||||||
|
$scope.updateCurrentOverrides();
|
||||||
|
$scope.render();
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.removeOverride = function(option) {
|
||||||
|
delete $scope.override[option.propertyName];
|
||||||
|
$scope.updateCurrentOverrides();
|
||||||
|
$scope.render();
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.getSeriesNames = function() {
|
||||||
|
return _.map($scope.legend, function(info) {
|
||||||
|
return info.alias;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.updateCurrentOverrides = function() {
|
||||||
|
$scope.currentOverrides = [];
|
||||||
|
_.each($scope.overrideMenu, function(option) {
|
||||||
|
var value = $scope.override[option.propertyName];
|
||||||
|
if (_.isUndefined(value)) { return; }
|
||||||
|
$scope.currentOverrides.push({
|
||||||
|
name: option.text,
|
||||||
|
propertyName: option.propertyName,
|
||||||
|
value: String(value)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.addOverrideOption('Bars', 'bars', [true, false]);
|
||||||
|
$scope.addOverrideOption('Lines', 'lines', [true, false]);
|
||||||
|
$scope.addOverrideOption('Line fill', 'fill', [0,1,2,3,4,5,6,7,8,9,10]);
|
||||||
|
$scope.addOverrideOption('Line width', 'linewidth', [0,1,2,3,4,5,6,7,8,9,10]);
|
||||||
|
$scope.addOverrideOption('Staircase line', 'steppedLine', [true, false]);
|
||||||
|
$scope.addOverrideOption('Points', 'points', [true, false]);
|
||||||
|
$scope.addOverrideOption('Points Radius', 'pointradius', [1,2,3,4,5]);
|
||||||
|
$scope.addOverrideOption('Stack', 'stack', [true, false]);
|
||||||
|
$scope.addOverrideOption('Y-axis', 'yaxis', [1, 2]);
|
||||||
|
$scope.addOverrideOption('Z-index', 'zindex', [-1,-2,-3,0,1,2,3]);
|
||||||
|
$scope.updateCurrentOverrides();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="editor-row">
|
<div class="editor-row">
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h5>Chart Options</h5>
|
<h5>Chart Options</h5>
|
||||||
@@ -64,3 +62,49 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="editor-row">
|
||||||
|
<div class="section">
|
||||||
|
<h5>Series specific overrides <tip>Regex match example: /server[0-3]/i </tip></h5>
|
||||||
|
<div>
|
||||||
|
<div class="grafana-target" ng-repeat="override in panel.seriesOverrides" ng-controller="SeriesOverridesCtrl">
|
||||||
|
<div class="grafana-target-inner-wrapper">
|
||||||
|
<div class="grafana-target-inner">
|
||||||
|
|
||||||
|
<ul class="grafana-target-controls-left">
|
||||||
|
<li class="grafana-target-segment">
|
||||||
|
<i class="icon-remove pointer" ng-click="removeSeriesOverride(override)"></i>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ul class="grafana-segment-list">
|
||||||
|
<li class="grafana-target-segment">
|
||||||
|
alias or regex
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="text"
|
||||||
|
ng-model="override.alias"
|
||||||
|
bs-typeahead="getSeriesNames"
|
||||||
|
ng-blur="render()"
|
||||||
|
data-min-length=0 data-items=100
|
||||||
|
class="input-medium grafana-target-segment-input" >
|
||||||
|
</li>
|
||||||
|
<li class="grafana-target-segment" ng-repeat="option in currentOverrides">
|
||||||
|
<i class="pointer icon-remove" ng-click="removeOverride(option)"></i>
|
||||||
|
{{option.name}}: {{option.value}}
|
||||||
|
</li>
|
||||||
|
<li class="dropdown">
|
||||||
|
<a class="dropdown-toggle grafana-target-segment" data-toggle="dropdown" gf-dropdown="overrideMenu" bs-tooltip="'set option to override'">
|
||||||
|
<i class="icon-plus"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn btn-success" style="margin-top: 20px" ng-click="addSeriesOverride()">Add series override rule</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -193,8 +193,8 @@ function (angular, app, _, moment, kbn) {
|
|||||||
moment(model.to.date).fromNow();
|
moment(model.to.date).fromNow();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
model.rangeString = $scope.dashboard.formatDate(model.from.date, 'MMM D, YYYY hh:mm:ss') + ' to ' +
|
model.rangeString = $scope.dashboard.formatDate(model.from.date, 'MMM D, YYYY HH:mm:ss') + ' to ' +
|
||||||
$scope.dashboard.formatDate(model.to.date, 'MMM D, YYYY hh:mm:ss');
|
$scope.dashboard.formatDate(model.to.date, 'MMM D, YYYY HH:mm:ss');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="dropdown grafana-menu-save">
|
<li class="dropdown grafana-menu-save">
|
||||||
<a href="#" bs-tooltip="'Save'" data-placement="bottom" class="dropdown-toggle" data-toggle="dropdown" ng-click="openSaveDropdown()">
|
<a bs-tooltip="'Save'" data-placement="bottom" class="dropdown-toggle" data-toggle="dropdown" ng-click="openSaveDropdown()">
|
||||||
<i class='icon-save'></i>
|
<i class='icon-save'></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-if="editor.index == 2" ng-controller="dashLoader">
|
<div ng-if="editor.index == 2">
|
||||||
<div class="editor-row">
|
<div class="editor-row">
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h5>Feature toggles</h5>
|
<h5>Feature toggles</h5>
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h6 ng-hide="results.dashboards.length || results.metrics.length">No dashboards or metrics matching your query found</h6>
|
<h6 ng-hide="results.dashboards.length">No dashboards matching your query were found.</h6>
|
||||||
|
|
||||||
<div class="search-results-container" ng-if="tagsOnly">
|
<div class="search-results-container" ng-if="tagsOnly">
|
||||||
<div ng-repeat="tag in results.tags"
|
<div ng-repeat="tag in results.tags"
|
||||||
@@ -64,11 +64,6 @@
|
|||||||
bindonce ng-repeat="row in results.dashboards"
|
bindonce ng-repeat="row in results.dashboards"
|
||||||
ng-class="{'selected': $index === selectedIndex }" ng-click="goToDashboard(row.id)">
|
ng-class="{'selected': $index === selectedIndex }" ng-click="goToDashboard(row.id)">
|
||||||
|
|
||||||
<a class="search-result-link" href="#/dashboard/db/{{row.id}}">
|
|
||||||
<i class="icon icon-th-large"></i>
|
|
||||||
<span bo-text="row.id"></span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<div class="search-result-actions">
|
<div class="search-result-actions">
|
||||||
<a ng-click="shareDashboard(row.id, row.id, $event)" config-modal="app/partials/dashLoaderShare.html">
|
<a ng-click="shareDashboard(row.id, row.id, $event)" config-modal="app/partials/dashLoaderShare.html">
|
||||||
<i class="icon-share"></i> share
|
<i class="icon-share"></i> share
|
||||||
@@ -84,6 +79,11 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<a class="search-result-link">
|
||||||
|
<i class="icon icon-th-large"></i>
|
||||||
|
<span bo-text="row.title"></span>
|
||||||
|
</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -16,10 +16,12 @@ function (angular) {
|
|||||||
.when('/dashboard/elasticsearch/:id', {
|
.when('/dashboard/elasticsearch/:id', {
|
||||||
templateUrl: 'app/partials/dashboard.html',
|
templateUrl: 'app/partials/dashboard.html',
|
||||||
controller : 'DashFromDBProvider',
|
controller : 'DashFromDBProvider',
|
||||||
|
reloadOnSearch: false,
|
||||||
})
|
})
|
||||||
.when('/dashboard/temp/:id', {
|
.when('/dashboard/temp/:id', {
|
||||||
templateUrl: 'app/partials/dashboard.html',
|
templateUrl: 'app/partials/dashboard.html',
|
||||||
controller : 'DashFromDBProvider',
|
controller : 'DashFromDBProvider',
|
||||||
|
reloadOnSearch: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ function (angular, $, config, _) {
|
|||||||
.when('/dashboard/file/:jsonFile', {
|
.when('/dashboard/file/:jsonFile', {
|
||||||
templateUrl: 'app/partials/dashboard.html',
|
templateUrl: 'app/partials/dashboard.html',
|
||||||
controller : 'DashFromFileProvider',
|
controller : 'DashFromFileProvider',
|
||||||
|
reloadOnSearch: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ function (angular, $, kbn, _, moment) {
|
|||||||
data = {};
|
data = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.id = data.id || null;
|
||||||
this.title = data.title || 'No Title';
|
this.title = data.title || 'No Title';
|
||||||
|
this.originalTitle = this.title;
|
||||||
this.tags = data.tags || [];
|
this.tags = data.tags || [];
|
||||||
this.style = data.style || "dark";
|
this.style = data.style || "dark";
|
||||||
this.timezone = data.timezone || 'browser';
|
this.timezone = data.timezone || 'browser';
|
||||||
@@ -31,7 +33,6 @@ function (angular, $, kbn, _, moment) {
|
|||||||
this.templating = data.templating || { list: [] };
|
this.templating = data.templating || { list: [] };
|
||||||
this.refresh = data.refresh;
|
this.refresh = data.refresh;
|
||||||
this.version = data.version || 0;
|
this.version = data.version || 0;
|
||||||
this.$state = data.$state;
|
|
||||||
|
|
||||||
if (this.nav.length === 0) {
|
if (this.nav.length === 0) {
|
||||||
this.nav.push({ type: 'timepicker' });
|
this.nav.push({ type: 'timepicker' });
|
||||||
@@ -144,80 +145,97 @@ function (angular, $, kbn, _, moment) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
p.updateSchema = function(old) {
|
p.updateSchema = function(old) {
|
||||||
var i, j, row, panel;
|
|
||||||
var oldVersion = this.version;
|
var oldVersion = this.version;
|
||||||
this.version = 3;
|
var panelUpgrades = [];
|
||||||
|
this.version = 4;
|
||||||
|
|
||||||
if (oldVersion === 3) {
|
if (oldVersion === 4) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Version 3 schema changes
|
// version 2 schema changes
|
||||||
// ensure panel ids
|
if (oldVersion < 2) {
|
||||||
var maxId = this.getNextPanelId();
|
|
||||||
for (i = 0; i < this.rows.length; i++) {
|
if (old.services) {
|
||||||
row = this.rows[i];
|
if (old.services.filter) {
|
||||||
for (j = 0; j < row.panels.length; j++) {
|
this.time = old.services.filter.time;
|
||||||
panel = row.panels[j];
|
this.templating.list = old.services.filter.list;
|
||||||
if (!panel.id) {
|
|
||||||
panel.id = maxId;
|
|
||||||
maxId += 1;
|
|
||||||
}
|
}
|
||||||
|
delete this.services;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (oldVersion === 2) {
|
panelUpgrades.push(function(panel) {
|
||||||
return;
|
// rename panel type
|
||||||
}
|
|
||||||
|
|
||||||
// Version 2 schema changes
|
|
||||||
if (old.services) {
|
|
||||||
if (old.services.filter) {
|
|
||||||
this.time = old.services.filter.time;
|
|
||||||
this.templating.list = old.services.filter.list;
|
|
||||||
}
|
|
||||||
delete this.services;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < this.rows.length; i++) {
|
|
||||||
row = this.rows[i];
|
|
||||||
for (j = 0; j < row.panels.length; j++) {
|
|
||||||
panel = row.panels[j];
|
|
||||||
if (panel.type === 'graphite') {
|
if (panel.type === 'graphite') {
|
||||||
panel.type = 'graph';
|
panel.type = 'graph';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (panel.type === 'graph') {
|
if (panel.type !== 'graph') {
|
||||||
if (_.isBoolean(panel.legend)) {
|
return;
|
||||||
panel.legend = { show: panel.legend };
|
}
|
||||||
|
|
||||||
|
if (_.isBoolean(panel.legend)) { panel.legend = { show: panel.legend }; }
|
||||||
|
|
||||||
|
if (panel.grid) {
|
||||||
|
if (panel.grid.min) {
|
||||||
|
panel.grid.leftMin = panel.grid.min;
|
||||||
|
delete panel.grid.min;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (panel.grid) {
|
if (panel.grid.max) {
|
||||||
if (panel.grid.min) {
|
panel.grid.leftMax = panel.grid.max;
|
||||||
panel.grid.leftMin = panel.grid.min;
|
delete panel.grid.max;
|
||||||
delete panel.grid.min;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (panel.grid.max) {
|
|
||||||
panel.grid.leftMax = panel.grid.max;
|
|
||||||
delete panel.grid.max;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (panel.y_format) {
|
if (panel.y_format) {
|
||||||
panel.y_formats[0] = panel.y_format;
|
panel.y_formats[0] = panel.y_format;
|
||||||
delete panel.y_format;
|
delete panel.y_format;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (panel.y2_format) {
|
if (panel.y2_format) {
|
||||||
panel.y_formats[1] = panel.y2_format;
|
panel.y_formats[1] = panel.y2_format;
|
||||||
delete panel.y2_format;
|
delete panel.y2_format;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// schema version 3 changes
|
||||||
|
if (oldVersion < 3) {
|
||||||
|
// ensure panel ids
|
||||||
|
var maxId = this.getNextPanelId();
|
||||||
|
panelUpgrades.push(function(panel) {
|
||||||
|
if (!panel.id) {
|
||||||
|
panel.id = maxId;
|
||||||
|
maxId += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// schema version 4 changes
|
||||||
|
if (oldVersion < 4) {
|
||||||
|
// move aliasYAxis changes
|
||||||
|
panelUpgrades.push(function(panel) {
|
||||||
|
if (panel.type !== 'graph') { return; }
|
||||||
|
_.each(panel.aliasYAxis, function(value, key) {
|
||||||
|
panel.seriesOverrides = [{ alias: key, yaxis: value }];
|
||||||
|
});
|
||||||
|
delete panel.aliasYAxis;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (panelUpgrades.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < this.rows.length; i++) {
|
||||||
|
var row = this.rows[i];
|
||||||
|
for (var j = 0; j < row.panels.length; j++) {
|
||||||
|
for (var k = 0; k < panelUpgrades.length; k++) {
|
||||||
|
panelUpgrades[k](row.panels[j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.version = 3;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -216,6 +216,7 @@ function (angular, _, $, config, kbn, moment) {
|
|||||||
for (var i = 0; i < results.hits.hits.length; i++) {
|
for (var i = 0; i < results.hits.hits.length; i++) {
|
||||||
hits.dashboards.push({
|
hits.dashboards.push({
|
||||||
id: results.hits.hits[i]._id,
|
id: results.hits.hits[i]._id,
|
||||||
|
title: results.hits.hits[i]._id,
|
||||||
tags: results.hits.hits[i]._source.tags
|
tags: results.hits.hits[i]._source.tags
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ function (angular, _, $, config, kbn, moment) {
|
|||||||
|
|
||||||
if (rounding === 'round-up') {
|
if (rounding === 'round-up') {
|
||||||
if (date.get('s')) {
|
if (date.get('s')) {
|
||||||
date.add('m', 1);
|
date.add(1, 'm');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (rounding === 'round-down') {
|
else if (rounding === 'round-down') {
|
||||||
@@ -159,7 +159,7 @@ function (angular, _, $, config, kbn, moment) {
|
|||||||
// to guarantee that we get all the data that
|
// to guarantee that we get all the data that
|
||||||
// exists for the specified range
|
// exists for the specified range
|
||||||
if (date.get('s')) {
|
if (date.get('s')) {
|
||||||
date.subtract('m', 1);
|
date.subtract(1, 'm');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -358,6 +358,7 @@ function (angular, _, kbn, InfluxSeries) {
|
|||||||
for (var i = 0; i < results.length; i++) {
|
for (var i = 0; i < results.length; i++) {
|
||||||
var hit = {
|
var hit = {
|
||||||
id: results[i].points[0][dashCol],
|
id: results[i].points[0][dashCol],
|
||||||
|
title: results[i].points[0][dashCol],
|
||||||
tags: results[i].points[0][tagsCol].split(",")
|
tags: results[i].points[0][tagsCol].split(",")
|
||||||
};
|
};
|
||||||
hit.tags = hit.tags[0] ? hit.tags : [];
|
hit.tags = hit.tags[0] ? hit.tags : [];
|
||||||
|
|||||||
@@ -52,6 +52,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.search-results-container {
|
.search-results-container {
|
||||||
|
max-height: 600px;
|
||||||
|
overflow: auto;
|
||||||
|
display: block;
|
||||||
.search-result-item a {
|
.search-result-item a {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -154,3 +154,17 @@
|
|||||||
.annotation-tags {
|
.annotation-tags {
|
||||||
color: @purple;
|
color: @purple;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.graph-series-override {
|
||||||
|
input {
|
||||||
|
float: left;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.graph-series-override-option {
|
||||||
|
float: left;
|
||||||
|
padding: 2px 6px;
|
||||||
|
}
|
||||||
|
.graph-series-override-selector {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ define([
|
|||||||
{
|
{
|
||||||
type: 'graphite',
|
type: 'graphite',
|
||||||
legend: true,
|
legend: true,
|
||||||
|
aliasYAxis: { test: 2 },
|
||||||
grid: { min: 1, max: 10 }
|
grid: { min: 1, max: 10 }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -134,8 +135,13 @@ define([
|
|||||||
expect(graph.grid.leftMax).to.be(10);
|
expect(graph.grid.leftMax).to.be(10);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('move aliasYAxis to series override', function() {
|
||||||
|
expect(graph.seriesOverrides[0].alias).to.be("test");
|
||||||
|
expect(graph.seriesOverrides[0].yaxis).to.be(2);
|
||||||
|
});
|
||||||
|
|
||||||
it('dashboard schema version should be set to latest', function() {
|
it('dashboard schema version should be set to latest', function() {
|
||||||
expect(model.version).to.be(3);
|
expect(model.version).to.be(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,147 @@
|
|||||||
|
define([
|
||||||
|
'./helpers',
|
||||||
|
'angular',
|
||||||
|
'jquery',
|
||||||
|
'components/timeSeries',
|
||||||
|
'directives/grafanaGraph'
|
||||||
|
], function(helpers, angular, $, TimeSeries) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
describe('grafanaGraph', function() {
|
||||||
|
|
||||||
|
beforeEach(module('grafana.directives'));
|
||||||
|
|
||||||
|
function graphScenario(desc, func) {
|
||||||
|
describe(desc, function() {
|
||||||
|
var ctx = {};
|
||||||
|
ctx.setup = function (setupFunc) {
|
||||||
|
beforeEach(inject(function($rootScope, $compile) {
|
||||||
|
var scope = $rootScope.$new();
|
||||||
|
var element = angular.element("<div style='width:500px' grafana-graph><div>");
|
||||||
|
|
||||||
|
scope.height = '200px';
|
||||||
|
scope.panel = {
|
||||||
|
legend: {},
|
||||||
|
grid: {},
|
||||||
|
y_formats: [],
|
||||||
|
seriesOverrides: []
|
||||||
|
};
|
||||||
|
scope.dashboard = { timezone: 'browser' };
|
||||||
|
scope.range = {
|
||||||
|
from: new Date('2014-08-09 10:00:00'),
|
||||||
|
to: new Date('2014-09-09 13:00:00')
|
||||||
|
};
|
||||||
|
ctx.data = [];
|
||||||
|
ctx.data.push(new TimeSeries({
|
||||||
|
datapoints: [[1,1],[2,2]],
|
||||||
|
info: { alias: 'series1', enable: true }
|
||||||
|
}));
|
||||||
|
ctx.data.push(new TimeSeries({
|
||||||
|
datapoints: [[1,1],[2,2]],
|
||||||
|
info: { alias: 'series2', enable: true }
|
||||||
|
}));
|
||||||
|
|
||||||
|
setupFunc(scope, ctx.data);
|
||||||
|
|
||||||
|
$compile(element)(scope);
|
||||||
|
scope.$digest();
|
||||||
|
$.plot = ctx.plotSpy = sinon.spy();
|
||||||
|
|
||||||
|
scope.$emit('render', ctx.data);
|
||||||
|
ctx.plotData = ctx.plotSpy.getCall(0).args[1];
|
||||||
|
ctx.plotOptions = ctx.plotSpy.getCall(0).args[2];
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
func(ctx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
graphScenario('simple lines options', function(ctx) {
|
||||||
|
ctx.setup(function(scope) {
|
||||||
|
scope.panel.lines = true;
|
||||||
|
scope.panel.fill = 5;
|
||||||
|
scope.panel.linewidth = 3;
|
||||||
|
scope.panel.steppedLine = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should configure plot with correct options', function() {
|
||||||
|
expect(ctx.plotOptions.series.lines.show).to.be(true);
|
||||||
|
expect(ctx.plotOptions.series.lines.fill).to.be(0.5);
|
||||||
|
expect(ctx.plotOptions.series.lines.lineWidth).to.be(3);
|
||||||
|
expect(ctx.plotOptions.series.lines.steps).to.be(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
graphScenario('grid thresholds 100, 200', function(ctx) {
|
||||||
|
ctx.setup(function(scope) {
|
||||||
|
scope.panel.grid = {
|
||||||
|
threshold1: 100,
|
||||||
|
threshold1Color: "#111",
|
||||||
|
threshold2: 200,
|
||||||
|
threshold2Color: "#222",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add grid markings', function() {
|
||||||
|
var markings = ctx.plotOptions.grid.markings;
|
||||||
|
expect(markings[0].yaxis.from).to.be(100);
|
||||||
|
expect(markings[0].yaxis.to).to.be(200);
|
||||||
|
expect(markings[0].color).to.be('#111');
|
||||||
|
expect(markings[1].yaxis.from).to.be(200);
|
||||||
|
expect(markings[1].yaxis.to).to.be(Infinity);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
graphScenario('inverted grid thresholds 200, 100', function(ctx) {
|
||||||
|
ctx.setup(function(scope) {
|
||||||
|
scope.panel.grid = {
|
||||||
|
threshold1: 200,
|
||||||
|
threshold1Color: "#111",
|
||||||
|
threshold2: 100,
|
||||||
|
threshold2Color: "#222",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add grid markings', function() {
|
||||||
|
var markings = ctx.plotOptions.grid.markings;
|
||||||
|
expect(markings[0].yaxis.from).to.be(200);
|
||||||
|
expect(markings[0].yaxis.to).to.be(100);
|
||||||
|
expect(markings[0].color).to.be('#111');
|
||||||
|
expect(markings[1].yaxis.from).to.be(100);
|
||||||
|
expect(markings[1].yaxis.to).to.be(-Infinity);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
graphScenario('series option overrides, fill & points', function(ctx) {
|
||||||
|
ctx.setup(function(scope, data) {
|
||||||
|
scope.panel.lines = true;
|
||||||
|
scope.panel.fill = 5;
|
||||||
|
scope.panel.seriesOverrides = [
|
||||||
|
{ alias: 'test', fill: 0, points: true }
|
||||||
|
];
|
||||||
|
|
||||||
|
data[1].info.alias = 'test';
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match second series and fill zero, and enable points', function() {
|
||||||
|
expect(ctx.plotOptions.series.lines.fill).to.be(0.5);
|
||||||
|
expect(ctx.plotData[1].lines.fill).to.be(0.001);
|
||||||
|
expect(ctx.plotData[1].points.show).to.be(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
graphScenario('should order series order according to zindex', function(ctx) {
|
||||||
|
ctx.setup(function(scope) {
|
||||||
|
scope.panel.seriesOverrides = [{ alias: 'series1', zindex: 2 }];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should move zindex 2 last', function() {
|
||||||
|
expect(ctx.plotData[0].info.alias).to.be('series2');
|
||||||
|
expect(ctx.plotData[1].info.alias).to.be('series1');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@@ -64,12 +64,12 @@ define([
|
|||||||
function FilterSrvStub() {
|
function FilterSrvStub() {
|
||||||
this.time = { from:'now-1h', to: 'now'};
|
this.time = { from:'now-1h', to: 'now'};
|
||||||
this.timeRange = function(parse) {
|
this.timeRange = function(parse) {
|
||||||
if (!parse) {
|
if (parse === false) {
|
||||||
return this.time;
|
return this.time;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
from : kbn.parseDate(this.time.from),
|
from : kbn.parseDate(this.time.from),
|
||||||
to : kbn.parseDate(this.time.to)
|
to : kbn.parseDate(this.time.to)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
define([
|
||||||
|
'./helpers',
|
||||||
|
'panels/graph/seriesOverridesCtrl'
|
||||||
|
], function(helpers) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
describe('SeriesOverridesCtrl', function() {
|
||||||
|
var ctx = new helpers.ControllerTestContext();
|
||||||
|
|
||||||
|
beforeEach(module('grafana.services'));
|
||||||
|
beforeEach(module('grafana.panels.graph'));
|
||||||
|
|
||||||
|
beforeEach(ctx.providePhase());
|
||||||
|
beforeEach(ctx.createControllerPhase('SeriesOverridesCtrl'));
|
||||||
|
beforeEach(function() {
|
||||||
|
ctx.scope.render = function() {};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Controller should init overrideMenu', function() {
|
||||||
|
it('click should include option and value index', function() {
|
||||||
|
expect(ctx.scope.overrideMenu[1].submenu[1].click).to.be('setOverride(1,1)');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('When setting an override', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
ctx.scope.setOverride(1, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set override property', function() {
|
||||||
|
expect(ctx.scope.override.lines).to.be(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update view model', function() {
|
||||||
|
expect(ctx.scope.currentOverrides[0].name).to.be('Lines');
|
||||||
|
expect(ctx.scope.currentOverrides[0].value).to.be('true');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('When removing overide', function() {
|
||||||
|
it('click should include option and value index', function() {
|
||||||
|
ctx.scope.setOverride(1,0);
|
||||||
|
ctx.scope.removeOverride({ propertyName: 'lines' });
|
||||||
|
expect(ctx.scope.currentOverrides.length).to.be(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
define([
|
||||||
|
'components/timeSeries'
|
||||||
|
], function(TimeSeries) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
describe("TimeSeries", function() {
|
||||||
|
var points, series;
|
||||||
|
var yAxisFormats = ['short', 'ms'];
|
||||||
|
var testData = {
|
||||||
|
info: { alias: 'test' },
|
||||||
|
datapoints: [
|
||||||
|
[1,2],[null,3],[10,4],[8,5]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('when getting flot pairs', function() {
|
||||||
|
it('with connected style, should ignore nulls', function() {
|
||||||
|
series = new TimeSeries(testData);
|
||||||
|
points = series.getFlotPairs('connected', yAxisFormats);
|
||||||
|
expect(points.length).to.be(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('with null as zero style, should replace nulls with zero', function() {
|
||||||
|
series = new TimeSeries(testData);
|
||||||
|
points = series.getFlotPairs('null as zero', yAxisFormats);
|
||||||
|
expect(points.length).to.be(4);
|
||||||
|
expect(points[1][1]).to.be(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('series overrides', function() {
|
||||||
|
var series;
|
||||||
|
beforeEach(function() {
|
||||||
|
series = new TimeSeries(testData);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('fill & points', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
series.info.alias = 'test';
|
||||||
|
series.applySeriesOverrides([{ alias: 'test', fill: 0, points: true }]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set fill zero, and enable points', function() {
|
||||||
|
expect(series.lines.fill).to.be(0.001);
|
||||||
|
expect(series.points.show).to.be(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('series option overrides, bars, true & lines false', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
series.info.alias = 'test';
|
||||||
|
series.applySeriesOverrides([{ alias: 'test', bars: true, lines: false }]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should disable lines, and enable bars', function() {
|
||||||
|
expect(series.lines.show).to.be(false);
|
||||||
|
expect(series.bars.show).to.be(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('series option overrides, linewidth, stack', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
series.info.alias = 'test';
|
||||||
|
series.applySeriesOverrides([{ alias: 'test', linewidth: 5, stack: false }]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should disable stack, and set lineWidth', function() {
|
||||||
|
expect(series.stack).to.be(false);
|
||||||
|
expect(series.lines.lineWidth).to.be(5);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('series option overrides, pointradius, steppedLine', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
series.info.alias = 'test';
|
||||||
|
series.applySeriesOverrides([{ alias: 'test', pointradius: 5, steppedLine: true }]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set pointradius, and set steppedLine', function() {
|
||||||
|
expect(series.points.radius).to.be(5);
|
||||||
|
expect(series.lines.steps).to.be(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('override match on regex', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
series.info.alias = 'test_01';
|
||||||
|
series.applySeriesOverrides([{ alias: '/.*01/', lines: false }]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match second series', function() {
|
||||||
|
expect(series.lines.show).to.be(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('override series y-axis, and z-index', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
series.info.alias = 'test';
|
||||||
|
series.applySeriesOverrides([{ alias: 'test', yaxis: 2, zindex: 2 }]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set yaxis', function() {
|
||||||
|
expect(series.info.yaxis).to.be(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set zindex', function() {
|
||||||
|
expect(series.zindex).to.be(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -118,10 +118,13 @@ require([
|
|||||||
'specs/lexer-specs',
|
'specs/lexer-specs',
|
||||||
'specs/parser-specs',
|
'specs/parser-specs',
|
||||||
'specs/gfunc-specs',
|
'specs/gfunc-specs',
|
||||||
|
'specs/timeSeries-specs',
|
||||||
'specs/row-ctrl-specs',
|
'specs/row-ctrl-specs',
|
||||||
'specs/graphiteTargetCtrl-specs',
|
'specs/graphiteTargetCtrl-specs',
|
||||||
'specs/influxdb-datasource-specs',
|
'specs/influxdb-datasource-specs',
|
||||||
'specs/graph-ctrl-specs',
|
'specs/graph-ctrl-specs',
|
||||||
|
'specs/grafanaGraph-specs',
|
||||||
|
'specs/seriesOverridesCtrl-specs',
|
||||||
'specs/filterSrv-specs',
|
'specs/filterSrv-specs',
|
||||||
'specs/kbn-format-specs',
|
'specs/kbn-format-specs',
|
||||||
'specs/dashboardSrv-specs',
|
'specs/dashboardSrv-specs',
|
||||||
|
|||||||
Vendored
+2
-1
@@ -753,7 +753,8 @@
|
|||||||
|
|
||||||
if (!selector) {
|
if (!selector) {
|
||||||
selector = $this.attr('href')
|
selector = $this.attr('href')
|
||||||
selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
|
// grafana backport fix from bootstrap 3
|
||||||
|
selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
|
||||||
}
|
}
|
||||||
|
|
||||||
$parent = selector && $(selector)
|
$parent = selector && $(selector)
|
||||||
|
|||||||
Vendored
+869
-461
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user