diff --git a/CHANGELOG.md b/CHANGELOG.md index 90a158907f5..682904a08d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,13 @@ **UI Improvements* - [Issue #770](https://github.com/grafana/grafana/issues/770). UI: Panel dropdown menu replaced with a new panel menu +**Graph** - [Issue #877](https://github.com/grafana/grafana/issues/877). Graph: Smart auto decimal precision when using scaled unit formats - [Issue #850](https://github.com/grafana/grafana/issues/850). Graph: Shared tooltip that shows multiple series & crosshair line, thx @toni-moreno +- [Issue #940](https://github.com/grafana/grafana/issues/940). Graph: New series style override option "Fill below to", useful to visualize max & min as a shadow for the mean + +**Misc** +- [Issue #938](https://github.com/grafana/grafana/issues/938). Panel: Plugin panels now reside outside of app/panels directory **Fixes** - [Issue #925](https://github.com/grafana/grafana/issues/925). Graph: bar width calculation fix for some edge cases (bars would render on top of each other) diff --git a/src/app/app.js b/src/app/app.js index ab5c8fd6355..0b75d459435 100644 --- a/src/app/app.js +++ b/src/app/app.js @@ -57,7 +57,6 @@ function (angular, $, _, appLevelRequire, config) { register_fns.factory = $provide.factory; register_fns.service = $provide.service; register_fns.filter = $filterProvider.register; - }); var apps_deps = [ diff --git a/src/app/components/require.config.js b/src/app/components/require.config.js index a478fc76ff2..5d8da32846f 100644 --- a/src/app/components/require.config.js +++ b/src/app/components/require.config.js @@ -40,6 +40,7 @@ require.config({ 'jquery.flot.stackpercent':'../vendor/jquery/jquery.flot.stackpercent', 'jquery.flot.time': '../vendor/jquery/jquery.flot.time', 'jquery.flot.crosshair': '../vendor/jquery/jquery.flot.crosshair', + 'jquery.flot.fillbelow': '../vendor/jquery/jquery.flot.fillbelow', modernizr: '../vendor/modernizr-2.6.1', @@ -83,6 +84,7 @@ require.config({ 'jquery.flot.stackpercent':['jquery', 'jquery.flot'], 'jquery.flot.time': ['jquery', 'jquery.flot'], 'jquery.flot.crosshair':['jquery', 'jquery.flot'], + 'jquery.flot.fillbelow':['jquery', 'jquery.flot'], 'angular-cookies': ['angular'], 'angular-dragdrop': ['jquery', 'angular'], 'angular-loader': ['angular'], diff --git a/src/app/components/settings.js b/src/app/components/settings.js index 291879e7404..01f40c2e761 100644 --- a/src/app/components/settings.js +++ b/src/app/components/settings.js @@ -15,7 +15,10 @@ function (_, crypto) { var defaults = { datasources : {}, window_title_prefix : 'Grafana - ', - panels : ['graph', 'text', 'stats'], + panels : { + 'graph': { path: 'panels/graph' }, + 'text': { path: 'panels/text' } + }, plugins : {}, default_route : '/dashboard/file/default.json', playlist_timespan : "1m", @@ -76,7 +79,7 @@ function (_, crypto) { }); if (settings.plugins.panels) { - settings.panels = _.union(settings.panels, settings.plugins.panels); + _.extend(settings.panels, settings.plugins.panels); } if (!settings.plugins.dependencies) { diff --git a/src/app/components/timeSeries.js b/src/app/components/timeSeries.js index 79032fd69dd..4422e744d45 100644 --- a/src/app/components/timeSeries.js +++ b/src/app/components/timeSeries.js @@ -9,6 +9,7 @@ function (_, kbn) { this.datapoints = opts.datapoints; this.info = opts.info; this.label = opts.info.alias; + this.id = opts.info.alias; this.valueFormater = kbn.valueFormats.none; this.stats = {}; } @@ -50,6 +51,8 @@ function (_, kbn) { 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.fillBelowTo !== void 0) { this.fillBelowTo = override.fillBelowTo; } + if (override.yaxis !== void 0) { this.info.yaxis = override.yaxis; } diff --git a/src/app/controllers/dashboardCtrl.js b/src/app/controllers/dashboardCtrl.js index dc2d880ca19..294862d5fae 100644 --- a/src/app/controllers/dashboardCtrl.js +++ b/src/app/controllers/dashboardCtrl.js @@ -21,7 +21,7 @@ function (angular, $, config, _) { $timeout) { $scope.editor = { index: 0 }; - $scope.panelNames = config.panels; + $scope.panelNames = _.map(config.panels, function(value, key) { return key; }); var resizeEventTimeout; this.init = function(dashboardData) { @@ -90,21 +90,12 @@ function (angular, $, config, _) { }; }; - $scope.edit_path = function(type) { - var p = $scope.panel_path(type); - if(p) { - return p+'/editor.html'; - } else { - return false; - } + $scope.panelEditorPath = function(type) { + return 'app/' + config.panels[type].path + '/editor.html'; }; - $scope.panel_path =function(type) { - if(type) { - return 'app/panels/'+type.replace(".","/"); - } else { - return false; - } + $scope.pulldownEditorPath = function(type) { + return 'app/panels/'+type+'/editor.html'; }; $scope.showJsonEditor = function(evt, options) { diff --git a/src/app/controllers/graphiteTarget.js b/src/app/controllers/graphiteTarget.js index 27299474bc0..3d6368a7793 100644 --- a/src/app/controllers/graphiteTarget.js +++ b/src/app/controllers/graphiteTarget.js @@ -201,7 +201,7 @@ function (angular, _, config, gfunc, Parser) { $scope.targetTextChanged = function() { parseTarget(); - $scope.$parent.get_data(); + $scope.get_data(); }; $scope.targetChanged = function() { diff --git a/src/app/directives/all.js b/src/app/directives/all.js index 35d718fc942..7f7836728a6 100644 --- a/src/app/directives/all.js +++ b/src/app/directives/all.js @@ -18,5 +18,6 @@ define([ './templateParamSelector', './graphiteSegment', './grafanaVersionCheck', + './dropdown.typeahead', './influxdbFuncEditor' ], function () {}); diff --git a/src/app/directives/dropdown.typeahead.js b/src/app/directives/dropdown.typeahead.js new file mode 100644 index 00000000000..e02b90b2576 --- /dev/null +++ b/src/app/directives/dropdown.typeahead.js @@ -0,0 +1,94 @@ +define([ + 'angular', + 'app', + 'lodash', + 'jquery', +], +function (angular, app, _, $) { + 'use strict'; + + angular + .module('grafana.directives') + .directive('dropdownTypeahead', function($compile) { + + var inputTemplate = ''; + + var buttonTemplate = ''; + + return { + scope: { + "menuItems": "=dropdownTypeahead", + "dropdownTypeaheadOnSelect": "&dropdownTypeaheadOnSelect" + }, + link: function($scope, elem) { + var $input = $(inputTemplate); + var $button = $(buttonTemplate); + $input.appendTo(elem); + $button.appendTo(elem); + + var typeaheadValues = _.reduce($scope.menuItems, function(memo, value) { + _.each(value.submenu, function(item) { + memo.push(value.text + ' ' + item.text); + }); + return memo; + }, []); + + $input.attr('data-provide', 'typeahead'); + $input.typeahead({ + source: typeaheadValues, + minLength: 1, + items: 10, + updater: function (value) { + var result = {}; + _.each($scope.menuItems, function(menuItem, optionIndex) { + _.each(menuItem.submenu, function(submenuItem, valueIndex) { + if (value === (menuItem.text + ' ' + submenuItem.text)) { + result.$item = submenuItem; + result.$optionIndex = optionIndex; + result.$valueIndex = valueIndex; + } + }); + }); + + if (result.$item) { + $scope.$apply(function() { + $scope.dropdownTypeaheadOnSelect(result); + }); + } + + $input.trigger('blur'); + return ''; + } + }); + + $button.click(function() { + $button.hide(); + $input.show(); + $input.focus(); + }); + + $input.keyup(function() { + elem.toggleClass('open', $input.val() === ''); + }); + + $input.blur(function() { + $input.hide(); + $input.val(''); + $button.show(); + $button.focus(); + // clicking the function dropdown menu wont + // work if you remove class at once + setTimeout(function() { + elem.removeClass('open'); + }, 200); + }); + + $compile(elem.contents())($scope); + } + }; + }); +}); diff --git a/src/app/directives/grafanaGraph.js b/src/app/directives/grafanaGraph.js index 635a287dfa3..bf7264cc16f 100755 --- a/src/app/directives/grafanaGraph.js +++ b/src/app/directives/grafanaGraph.js @@ -177,6 +177,7 @@ function (angular, $, kbn, moment, _, GraphTooltip) { var series = data[i]; series.applySeriesOverrides(panel.seriesOverrides); series.data = series.getFlotPairs(panel.nullPointMode, panel.y_formats); + // if hidden remove points and disable stack if (scope.hiddenSeries[series.info.alias]) { series.data = []; diff --git a/src/app/directives/grafanaPanel.js b/src/app/directives/grafanaPanel.js index e7587ace5c5..9e3c87fde3d 100644 --- a/src/app/directives/grafanaPanel.js +++ b/src/app/directives/grafanaPanel.js @@ -1,9 +1,10 @@ define([ 'angular', 'jquery', + 'config', './panelMenu', ], -function (angular, $) { +function (angular, $, config) { 'use strict'; angular @@ -68,10 +69,12 @@ function (angular, $) { elem.addClass('ng-cloak'); + var panelPath = config.panels[panelType].path; + $scope.require([ 'jquery', - 'text!panels/'+panelType+'/module.html', - 'panels/' + panelType + "/module", + 'text!'+panelPath+'/module.html', + panelPath + "/module", ], function ($, moduleTemplate) { var $module = $(moduleTemplate); $module.prepend(panelHeader); diff --git a/src/app/panels/graph/module.js b/src/app/panels/graph/module.js index f97a3489a27..34388a00173 100644 --- a/src/app/panels/graph/module.js +++ b/src/app/panels/graph/module.js @@ -16,6 +16,7 @@ define([ 'jquery.flot.time', 'jquery.flot.stack', 'jquery.flot.stackpercent', + 'jquery.flot.fillbelow', 'jquery.flot.crosshair' ], function (angular, app, $, _, kbn, moment, TimeSeries) { @@ -340,8 +341,8 @@ function (angular, app, $, _, kbn, moment, TimeSeries) { $scope.render(); }; - $scope.addSeriesOverride = function() { - $scope.panel.seriesOverrides.push({}); + $scope.addSeriesOverride = function(override) { + $scope.panel.seriesOverrides.push(override || {}); }; $scope.removeSeriesOverride = function(override) { diff --git a/src/app/panels/graph/seriesOverridesCtrl.js b/src/app/panels/graph/seriesOverridesCtrl.js index 1b6b1dfc144..bf46b086bea 100644 --- a/src/app/panels/graph/seriesOverridesCtrl.js +++ b/src/app/panels/graph/seriesOverridesCtrl.js @@ -34,6 +34,14 @@ define([ var option = $scope.overrideMenu[optionIndex]; var value = option.values[valueIndex]; $scope.override[option.propertyName] = value; + + // automatically disable lines for this series and the fill bellow to series + // can be removed by the user if they still want lines + if (option.propertyName === 'fillBelowTo') { + $scope.override['lines'] = false; + $scope.addSeriesOverride({ alias: value, lines: false }); + } + $scope.updateCurrentOverrides(); $scope.render(); }; @@ -67,6 +75,7 @@ define([ $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('Fill below to', 'fillBelowTo', $scope.getSeriesNames()); $scope.addOverrideOption('Staircase line', 'steppedLine', [true, false]); $scope.addOverrideOption('Points', 'points', [true, false]); $scope.addOverrideOption('Points Radius', 'pointradius', [1,2,3,4,5]); diff --git a/src/app/panels/graph/styleEditor.html b/src/app/panels/graph/styleEditor.html index 494d6bc1f7f..f230c2ba20a 100644 --- a/src/app/panels/graph/styleEditor.html +++ b/src/app/panels/graph/styleEditor.html @@ -88,11 +88,10 @@ {{option.name}}: {{option.value}} -