diff --git a/package.json b/package.json
index 31c552fe6c2..eecf826776e 100644
--- a/package.json
+++ b/package.json
@@ -34,7 +34,7 @@
"grunt-string-replace": "~0.2.4",
"grunt-usemin": "^2.1.1",
"jshint-stylish": "~0.1.5",
- "karma": "~0.12.16",
+ "karma": "~0.12.21",
"karma-chrome-launcher": "~0.1.4",
"karma-coffee-preprocessor": "~0.1.2",
"karma-coverage": "^0.2.5",
diff --git a/src/app/directives/grafanaGraph.js b/src/app/directives/grafanaGraph.js
index f0ffd14dd67..c2be3d90d13 100755
--- a/src/app/directives/grafanaGraph.js
+++ b/src/app/directives/grafanaGraph.js
@@ -156,7 +156,7 @@ function (angular, $, kbn, moment, _) {
for (var i = 0; i < data.length; i++) {
var _d = data[i].getFlotPairs(panel.nullPointMode, panel.y_formats);
data[i].data = _d;
- data[0].lines = { show: false };
+ data[0].lines = { show: false, dashes: true };
data[0].bars = { show: true };
}
diff --git a/src/app/panels/graph/module.html b/src/app/panels/graph/module.html
index 102cad84a40..0d418e5e8cd 100644
--- a/src/app/panels/graph/module.html
+++ b/src/app/panels/graph/module.html
@@ -27,7 +27,7 @@
-
diff --git a/src/app/panels/graph/module.js b/src/app/panels/graph/module.js
index d8a31f59312..557503023a1 100644
--- a/src/app/panels/graph/module.js
+++ b/src/app/panels/graph/module.js
@@ -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([
'angular',
'app',
@@ -19,6 +6,7 @@ define([
'kbn',
'moment',
'./timeSeries',
+ './seriesOverridesCtrl',
'services/panelSrv',
'services/annotationsSrv',
'services/datasourceSrv',
@@ -30,10 +18,9 @@ define([
'jquery.flot.stackpercent'
],
function (angular, app, $, _, kbn, moment, timeSeries) {
-
'use strict';
- var module = angular.module('grafana.panels.graph', []);
+ var module = angular.module('grafana.panels.graph');
app.useModule(module);
module.controller('GraphCtrl', function($scope, $rootScope, $timeout, panelSrv, annotationsSrv) {
@@ -363,49 +350,11 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
$scope.panel.seriesOverrides.push({});
};
+ $scope.removeSeriesOverride = function(override) {
+ $scope.panel.seriesOverrides = _.without($scope.panel.seriesOverrides, override);
+ };
+
panelSrv.init($scope);
});
- angular
- .module('grafana.directives')
- .directive('seriesOverrideOption', function($compile) {
- var template =
- '';
-
- return {
- scope: true,
- link: function($scope, elem, attrs) {
- var $template = $(template);
- elem.append($template);
- var $link = $(elem).find('a');
-
- $scope.options = $scope.$eval(attrs.options);
- $scope.options.unshift(null);
-
- $scope.options = _.map($scope.options, function(option, index) {
- return {
- text: option === null ? '' : String(option),
- value: option,
- click: 'setValue(' + index + ')'
- };
- });
-
- $scope.setValue = function(index) {
- var value = $scope.options[index].value;
- if (value === null) {
- $link.html('');
- }
- else {
- $link.html(value);
- }
- };
-
- $compile(elem.contents())($scope);
- }
- };
- });
-
});
diff --git a/src/app/panels/graph/seriesOverridesCtrl.js b/src/app/panels/graph/seriesOverridesCtrl.js
new file mode 100644
index 00000000000..2123510400b
--- /dev/null
+++ b/src/app/panels/graph/seriesOverridesCtrl.js
@@ -0,0 +1,66 @@
+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.removeOverride = function(option) {
+ delete $scope.override[option.propertyName];
+ $scope.updateCurrentOverrides();
+ };
+
+ $scope.updateCurrentOverrides = function() {
+ $scope.currentOverrides = [];
+ _.each($scope.overrideMenu, function(option) {
+ if (!_.isUndefined($scope.override[option.propertyName])) {
+ $scope.currentOverrides.push({
+ name: option.text,
+ propertyName: option.propertyName,
+ value: String($scope.override[option.propertyName])
+ });
+ }
+ });
+ };
+
+ $scope.addOverrideOption('Bars', 'bars', [true, false]);
+ $scope.addOverrideOption('Lines', 'lines', [true, false]);
+ $scope.addOverrideOption('Points', 'points', [true, false]);
+ $scope.addOverrideOption('Line fill', 'fill', [1,2,3,4,5,6,7,8]);
+ $scope.updateCurrentOverrides();
+
+ });
+
+});
diff --git a/src/app/panels/graph/styleEditor.html b/src/app/panels/graph/styleEditor.html
index c1b68e25beb..c436ba10ceb 100644
--- a/src/app/panels/graph/styleEditor.html
+++ b/src/app/panels/graph/styleEditor.html
@@ -66,36 +66,43 @@
Series specific overrides
-
-
+
+
+
diff --git a/src/css/less/graph.less b/src/css/less/graph.less
index 79e87ab8f47..8f049a44ae5 100644
--- a/src/css/less/graph.less
+++ b/src/css/less/graph.less
@@ -154,3 +154,17 @@
.annotation-tags {
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;
+ }
+}
diff --git a/src/test/specs/grafanaGraph-specs.js b/src/test/specs/grafanaGraph-specs.js
new file mode 100644
index 00000000000..f8b5878ce66
--- /dev/null
+++ b/src/test/specs/grafanaGraph-specs.js
@@ -0,0 +1,68 @@
+define([
+ './helpers',
+ 'angular',
+ 'jquery',
+ 'directives/grafanaGraph'
+], function(helpers, angular, $) {
+ '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("");
+
+ scope.height = '200px';
+ scope.panel = {
+ legend: {},
+ grid: {},
+ y_formats: []
+ };
+ scope.dashboard = { timezone: 'browser' };
+ scope.range = {
+ from: new Date('2014-08-09 10:00:00'),
+ to: new Date('2014-09-09 13:00:00')
+ };
+
+ setupFunc(scope);
+
+ $compile(element)(scope);
+ scope.$digest();
+ $.plot = ctx.plotSpy = sinon.spy();
+
+ scope.$emit('render', []);
+ 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);
+ });
+
+ });
+
+ });
+});
+
diff --git a/src/test/specs/helpers.js b/src/test/specs/helpers.js
index cd31779cc99..de2be527f9f 100644
--- a/src/test/specs/helpers.js
+++ b/src/test/specs/helpers.js
@@ -69,7 +69,7 @@ define([
}
return {
from : kbn.parseDate(this.time.from),
- to : kbn.parseDate(this.time.to)
+ to : kbn.parseDate(this.time.to)
};
};
diff --git a/src/test/specs/seriesOverridesCtrl-specs.js b/src/test/specs/seriesOverridesCtrl-specs.js
new file mode 100644
index 00000000000..04f0dcdca99
--- /dev/null
+++ b/src/test/specs/seriesOverridesCtrl-specs.js
@@ -0,0 +1,40 @@
+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'));
+
+ 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');
+ });
+ });
+
+ });
+
+});
+
diff --git a/src/test/test-main.js b/src/test/test-main.js
index 9c7cfe73341..8d2014b0664 100644
--- a/src/test/test-main.js
+++ b/src/test/test-main.js
@@ -122,6 +122,8 @@ require([
'specs/graphiteTargetCtrl-specs',
'specs/influxdb-datasource-specs',
'specs/graph-ctrl-specs',
+ 'specs/grafanaGraph-specs',
+ 'specs/seriesOverridesCtrl-specs',
'specs/filterSrv-specs',
'specs/kbn-format-specs',
'specs/dashboardSrv-specs',