瀏覽代碼

Merge branch 'fix-11053' of https://github.com/alexanderzobnin/grafana into alexanderzobnin-fix-11053

Daniel Lee 7 年之前
父節點
當前提交
4ac290215e
共有 41 個文件被更改,包括 512 次插入425 次删除
  1. 3 0
      CHANGELOG.md
  2. 1 1
      pkg/cmd/grafana-server/server.go
  3. 19 6
      pkg/setting/setting.go
  4. 7 0
      pkg/setting/setting_test.go
  5. 1 0
      public/app/core/components/grafana_app.ts
  6. 9 3
      public/app/core/components/scroll/scroll.ts
  7. 1 1
      public/app/core/components/search/search_results.html
  8. 0 13
      public/app/core/services/all.js
  9. 10 0
      public/app/core/services/all.ts
  10. 9 1
      public/app/core/services/keybindingSrv.ts
  11. 0 15
      public/app/features/all.js
  12. 13 0
      public/app/features/all.ts
  13. 10 4
      public/app/features/dashboard/timepicker/timepicker.ts
  14. 2 2
      public/app/features/dashboard/unsaved_changes_srv.ts
  15. 0 9
      public/app/features/panel/all.js
  16. 7 0
      public/app/features/panel/all.ts
  17. 0 7
      public/app/features/playlist/all.js
  18. 5 0
      public/app/features/playlist/all.ts
  19. 0 39
      public/app/features/playlist/playlist_routes.js
  20. 33 0
      public/app/features/playlist/playlist_routes.ts
  21. 2 0
      public/app/features/templating/editor_ctrl.ts
  22. 4 0
      public/app/features/templating/query_variable.ts
  23. 35 6
      public/app/features/templating/specs/query_variable.jest.ts
  24. 1 0
      public/app/plugins/datasource/graphite/add_graphite_func.ts
  25. 1 0
      public/app/plugins/datasource/graphite/func_editor.ts
  26. 0 292
      public/app/plugins/panel/graph/graph_tooltip.js
  27. 289 0
      public/app/plugins/panel/graph/graph_tooltip.ts
  28. 8 5
      public/app/plugins/panel/graph/legend.ts
  29. 3 2
      public/app/plugins/panel/graph/specs/tooltip_specs.ts
  30. 3 1
      public/app/plugins/panel/graph/template.ts
  31. 3 3
      public/app/plugins/panel/table/column_options.html
  32. 1 2
      public/sass/_variables.light.scss
  33. 4 2
      public/sass/base/_icons.scss
  34. 7 1
      public/sass/components/_panel_graph.scss
  35. 2 2
      public/sass/components/_scrollbar.scss
  36. 7 4
      public/sass/components/_search.scss
  37. 1 0
      public/sass/components/_sidemenu.scss
  38. 1 1
      public/sass/components/_tabbed_view.scss
  39. 2 1
      public/sass/pages/_alerting.scss
  40. 1 1
      public/sass/pages/_dashboard.scss
  41. 7 1
      public/sass/pages/_login.scss

+ 3 - 0
CHANGELOG.md

@@ -13,6 +13,8 @@
 * **Prometheus**: Show template variable candidate in query editor [#9210](https://github.com/grafana/grafana/issues/9210), thx [@mtanda](https://github.com/mtanda)
 * **Prometheus**: Support POST for query and query_range [#9859](https://github.com/grafana/grafana/pull/9859), thx [@mtanda](https://github.com/mtanda)
 * **Alerting**: Add support for retries on alert queries [#5855](https://github.com/grafana/grafana/issues/5855), thx [@Thib17](https://github.com/Thib17)
+* **Table**: Table plugin value mappings [#7119](https://github.com/grafana/grafana/issues/7119), thx [infernix](https://github.com/infernix)
+* **IE11**: IE 11 compatibility [#11165](https://github.com/grafana/grafana/issues/11165)
 
 ### Minor
 * **OpsGenie**: Add triggered alerts as description [#11046](https://github.com/grafana/grafana/pull/11046), thx [@llamashoes](https://github.com/llamashoes)
@@ -26,6 +28,7 @@
 * **AuthProxy**: Support IPv6 in Auth proxy white list [#11330](https://github.com/grafana/grafana/pull/11330), thx [@corny](https://github.com/corny)
 * **SMTP**: Don't connect to STMP server using TLS unless configured. [#7189](https://github.com/grafana/grafana/issues/7189)
 * **Prometheus**: Escape backslash in labels correctly. [#10555](https://github.com/grafana/grafana/issues/10555), thx [@roidelapluie](https://github.com/roidelapluie)
+* **Variables** Case-insensitive sorting for template values [#11128](https://github.com/grafana/grafana/issues/11128) thx [@cross](https://github.com/cross)
 
 # 5.0.4 (2018-03-28)
 

+ 1 - 1
pkg/cmd/grafana-server/server.go

@@ -111,7 +111,7 @@ func (g *GrafanaServerImpl) initLogging() {
 	})
 
 	if err != nil {
-		g.log.Error(err.Error())
+		fmt.Fprintf(os.Stderr, "Failed to start grafana. error: %s\n", err.Error())
 		os.Exit(1)
 	}
 

+ 19 - 6
pkg/setting/setting.go

@@ -223,7 +223,7 @@ func shouldRedactURLKey(s string) bool {
 	return strings.Contains(uppercased, "DATABASE_URL")
 }
 
-func applyEnvVariableOverrides() {
+func applyEnvVariableOverrides() error {
 	appliedEnvOverrides = make([]string, 0)
 	for _, section := range Cfg.Sections() {
 		for _, key := range section.Keys() {
@@ -238,7 +238,10 @@ func applyEnvVariableOverrides() {
 					envValue = "*********"
 				}
 				if shouldRedactURLKey(envKey) {
-					u, _ := url.Parse(envValue)
+					u, err := url.Parse(envValue)
+					if err != nil {
+						return fmt.Errorf("could not parse environment variable. key: %s, value: %s. error: %v", envKey, envValue, err)
+					}
 					ui := u.User
 					if ui != nil {
 						_, exists := ui.Password()
@@ -252,6 +255,8 @@ func applyEnvVariableOverrides() {
 			}
 		}
 	}
+
+	return nil
 }
 
 func applyCommandLineDefaultProperties(props map[string]string) {
@@ -377,7 +382,7 @@ func loadSpecifedConfigFile(configFile string) error {
 	return nil
 }
 
-func loadConfiguration(args *CommandLineArgs) {
+func loadConfiguration(args *CommandLineArgs) error {
 	var err error
 
 	// load config defaults
@@ -395,7 +400,7 @@ func loadConfiguration(args *CommandLineArgs) {
 	if err != nil {
 		fmt.Println(fmt.Sprintf("Failed to parse defaults.ini, %v", err))
 		os.Exit(1)
-		return
+		return err
 	}
 
 	Cfg.BlockMode = false
@@ -413,7 +418,10 @@ func loadConfiguration(args *CommandLineArgs) {
 	}
 
 	// apply environment overrides
-	applyEnvVariableOverrides()
+	err = applyEnvVariableOverrides()
+	if err != nil {
+		return err
+	}
 
 	// apply command line overrides
 	applyCommandLineProperties(commandLineProps)
@@ -424,6 +432,8 @@ func loadConfiguration(args *CommandLineArgs) {
 	// update data path and logging config
 	DataPath = makeAbsolute(Cfg.Section("paths").Key("data").String(), HomePath)
 	initLogging()
+
+	return err
 }
 
 func pathExists(path string) bool {
@@ -471,7 +481,10 @@ func validateStaticRootPath() error {
 
 func NewConfigContext(args *CommandLineArgs) error {
 	setHomePath(args)
-	loadConfiguration(args)
+	err := loadConfiguration(args)
+	if err != nil {
+		return err
+	}
 
 	Env = Cfg.Section("").Key("app_mode").MustString("development")
 	InstanceName = Cfg.Section("").Key("instance_name").MustString("unknown_instance_name")

+ 7 - 0
pkg/setting/setting_test.go

@@ -37,6 +37,13 @@ func TestLoadingSettings(t *testing.T) {
 			So(appliedEnvOverrides, ShouldContain, "GF_SECURITY_ADMIN_PASSWORD=*********")
 		})
 
+		Convey("Should return an error when url is invalid", func() {
+			os.Setenv("GF_DATABASE_URL", "postgres.%31://grafana:secret@postgres:5432/grafana")
+			err := NewConfigContext(&CommandLineArgs{HomePath: "../../"})
+
+			So(err, ShouldNotBeNil)
+		})
+
 		Convey("Should replace password in URL when url environment is defined", func() {
 			os.Setenv("GF_DATABASE_URL", "mysql://user:secret@localhost:3306/database")
 			NewConfigContext(&CommandLineArgs{HomePath: "../../"})

+ 1 - 0
public/app/core/components/grafana_app.ts

@@ -167,6 +167,7 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
           if (sidemenuHidden) {
             sidemenuHidden = false;
             body.addClass('sidemenu-open');
+            appEvents.emit('toggle-inactive-mode');
             $timeout(function() {
               $rootScope.$broadcast('render');
             }, 100);

+ 9 - 3
public/app/core/components/scroll/scroll.ts

@@ -60,10 +60,16 @@ export function geminiScrollbar() {
         scope
       );
 
-      appEvents.on('toggle-sidemenu', evt => {
-        // force updating dashboard width
+      // force updating dashboard width
+      appEvents.on('toggle-sidemenu', forceUpdate, scope);
+      appEvents.on('toggle-sidemenu-hidden', forceUpdate, scope);
+      appEvents.on('toggle-view-mode', forceUpdate, scope);
+      appEvents.on('toggle-kiosk-mode', forceUpdate, scope);
+      appEvents.on('toggle-inactive-mode', forceUpdate, scope);
+
+      function forceUpdate() {
         scrollbar.scroll();
-      });
+      }
 
       scope.$on('$routeChangeSuccess', () => {
         lastPos = 0;

+ 1 - 1
public/app/core/components/search/search_results.html

@@ -20,7 +20,7 @@
   <div class="search-section__header" ng-show="section.hideHeader"></div>
 
   <div ng-if="section.expanded">
-    <a ng-repeat="item in section.items" class="search-item" ng-class="{'selected': item.selected}" ng-href="{{::item.url}}" >
+    <a ng-repeat="item in section.items" class="search-item search-item--indent" ng-class="{'selected': item.selected}" ng-href="{{::item.url}}" >
       <div ng-click="ctrl.toggleSelection(item, $event)">
         <gf-form-switch
            ng-show="ctrl.editable"

+ 0 - 13
public/app/core/services/all.js

@@ -1,13 +0,0 @@
-define([
-  './alert_srv',
-  './util_srv',
-  './context_srv',
-  './timer',
-  './analytics',
-  './popover_srv',
-  './segment_srv',
-  './backend_srv',
-  './dynamic_directive_srv',
-  './bridge_srv'
-],
-function () {});

+ 10 - 0
public/app/core/services/all.ts

@@ -0,0 +1,10 @@
+import './alert_srv';
+import './util_srv';
+import './context_srv';
+import './timer';
+import './analytics';
+import './popover_srv';
+import './segment_srv';
+import './backend_srv';
+import './dynamic_directive_srv';
+import './bridge_srv';

+ 9 - 1
public/app/core/services/keybindingSrv.ts

@@ -10,6 +10,7 @@ import 'mousetrap-global-bind';
 export class KeybindingSrv {
   helpModal: boolean;
   modalOpen = false;
+  timepickerOpen = false;
 
   /** @ngInject */
   constructor(private $rootScope, private $location) {
@@ -22,6 +23,8 @@ export class KeybindingSrv {
 
     this.setupGlobal();
     appEvents.on('show-modal', () => (this.modalOpen = true));
+    $rootScope.onAppEvent('timepickerOpen', () => (this.timepickerOpen = true));
+    $rootScope.onAppEvent('timepickerClosed', () => (this.timepickerOpen = false));
   }
 
   setupGlobal() {
@@ -73,7 +76,12 @@ export class KeybindingSrv {
     appEvents.emit('hide-modal');
 
     if (!this.modalOpen) {
-      this.$rootScope.appEvent('panel-change-view', { fullscreen: false, edit: false });
+      if (this.timepickerOpen) {
+        this.$rootScope.appEvent('closeTimepicker');
+        this.timepickerOpen = false;
+      } else {
+        this.$rootScope.appEvent('panel-change-view', { fullscreen: false, edit: false });
+      }
     } else {
       this.modalOpen = false;
     }

+ 0 - 15
public/app/features/all.js

@@ -1,15 +0,0 @@
-define([
-  './panellinks/module',
-  './dashlinks/module',
-  './annotations/all',
-  './templating/all',
-  './plugins/all',
-  './dashboard/all',
-  './playlist/all',
-  './snapshot/all',
-  './panel/all',
-  './org/all',
-  './admin/admin',
-  './alerting/all',
-  './styleguide/styleguide',
-], function () {});

+ 13 - 0
public/app/features/all.ts

@@ -0,0 +1,13 @@
+import './panellinks/module';
+import './dashlinks/module';
+import './annotations/all';
+import './templating/all';
+import './plugins/all';
+import './dashboard/all';
+import './playlist/all';
+import './snapshot/all';
+import './panel/all';
+import './org/all';
+import './admin/admin';
+import './alerting/all';
+import './styleguide/styleguide';

+ 10 - 4
public/app/features/dashboard/timepicker/timepicker.ts

@@ -22,7 +22,6 @@ export class TimePickerCtrl {
   refresh: any;
   isUtc: boolean;
   firstDayOfWeek: number;
-  closeDropdown: any;
   isOpen: boolean;
 
   /** @ngInject */
@@ -32,6 +31,7 @@ export class TimePickerCtrl {
     $rootScope.onAppEvent('shift-time-forward', () => this.move(1), $scope);
     $rootScope.onAppEvent('shift-time-backward', () => this.move(-1), $scope);
     $rootScope.onAppEvent('refresh', this.onRefresh.bind(this), $scope);
+    $rootScope.onAppEvent('closeTimepicker', this.openDropdown.bind(this), $scope);
 
     // init options
     this.panel = this.dashboard.timepicker;
@@ -96,7 +96,7 @@ export class TimePickerCtrl {
 
   openDropdown() {
     if (this.isOpen) {
-      this.isOpen = false;
+      this.closeDropdown();
       return;
     }
 
@@ -112,6 +112,12 @@ export class TimePickerCtrl {
 
     this.refresh.options.unshift({ text: 'off' });
     this.isOpen = true;
+    this.$rootScope.appEvent('timepickerOpen');
+  }
+
+  closeDropdown() {
+    this.isOpen = false;
+    this.$rootScope.appEvent('timepickerClosed');
   }
 
   applyCustom() {
@@ -120,7 +126,7 @@ export class TimePickerCtrl {
     }
 
     this.timeSrv.setTime(this.editTimeRaw);
-    this.isOpen = false;
+    this.closeDropdown();
   }
 
   absoluteFromChanged() {
@@ -143,7 +149,7 @@ export class TimePickerCtrl {
     }
 
     this.timeSrv.setTime(range);
-    this.isOpen = false;
+    this.closeDropdown();
   }
 }
 

+ 2 - 2
public/app/features/dashboard/unsaved_changes_srv.ts

@@ -35,12 +35,12 @@ export class Tracker {
 
     $window.onbeforeunload = () => {
       if (this.ignoreChanges()) {
-        return null;
+        return undefined;
       }
       if (this.hasChanges()) {
         return 'There are unsaved changes to this dashboard';
       }
-      return null;
+      return undefined;
     };
 
     scope.$on('$locationChangeStart', (event, next) => {

+ 0 - 9
public/app/features/panel/all.js

@@ -1,9 +0,0 @@
-define([
-  './panel_header',
-  './panel_directive',
-  './solo_panel_ctrl',
-  './query_ctrl',
-  './panel_editor_tab',
-  './query_editor_row',
-  './query_troubleshooter',
-], function () {});

+ 7 - 0
public/app/features/panel/all.ts

@@ -0,0 +1,7 @@
+import './panel_header';
+import './panel_directive';
+import './solo_panel_ctrl';
+import './query_ctrl';
+import './panel_editor_tab';
+import './query_editor_row';
+import './query_troubleshooter';

+ 0 - 7
public/app/features/playlist/all.js

@@ -1,7 +0,0 @@
-define([
-  './playlists_ctrl',
-  './playlist_search',
-  './playlist_srv',
-  './playlist_edit_ctrl',
-  './playlist_routes'
-], function () {});

+ 5 - 0
public/app/features/playlist/all.ts

@@ -0,0 +1,5 @@
+import './playlists_ctrl';
+import './playlist_search';
+import './playlist_srv';
+import './playlist_edit_ctrl';
+import './playlist_routes';

+ 0 - 39
public/app/features/playlist/playlist_routes.js

@@ -1,39 +0,0 @@
-define([
-  'angular',
-  'lodash'
-],
-function (angular) {
-  'use strict';
-
-  var module = angular.module('grafana.routes');
-
-  module.config(function($routeProvider) {
-    $routeProvider
-      .when('/playlists', {
-        templateUrl: 'public/app/features/playlist/partials/playlists.html',
-        controllerAs: 'ctrl',
-        controller : 'PlaylistsCtrl'
-      })
-      .when('/playlists/create', {
-        templateUrl: 'public/app/features/playlist/partials/playlist.html',
-        controllerAs: 'ctrl',
-        controller : 'PlaylistEditCtrl'
-      })
-      .when('/playlists/edit/:id', {
-        templateUrl: 'public/app/features/playlist/partials/playlist.html',
-        controllerAs: 'ctrl',
-        controller : 'PlaylistEditCtrl'
-      })
-      .when('/playlists/play/:id', {
-        templateUrl: 'public/app/features/playlist/partials/playlists.html',
-        controllerAs: 'ctrl',
-        controller : 'PlaylistsCtrl',
-        resolve: {
-          init: function(playlistSrv, $route) {
-            var playlistId = $route.current.params.id;
-            playlistSrv.start(playlistId);
-          }
-        }
-      });
-  });
-});

+ 33 - 0
public/app/features/playlist/playlist_routes.ts

@@ -0,0 +1,33 @@
+import angular from 'angular';
+
+function grafanaRoutes($routeProvider) {
+  $routeProvider
+    .when('/playlists', {
+      templateUrl: 'public/app/features/playlist/partials/playlists.html',
+      controllerAs: 'ctrl',
+      controller: 'PlaylistsCtrl',
+    })
+    .when('/playlists/create', {
+      templateUrl: 'public/app/features/playlist/partials/playlist.html',
+      controllerAs: 'ctrl',
+      controller: 'PlaylistEditCtrl',
+    })
+    .when('/playlists/edit/:id', {
+      templateUrl: 'public/app/features/playlist/partials/playlist.html',
+      controllerAs: 'ctrl',
+      controller: 'PlaylistEditCtrl',
+    })
+    .when('/playlists/play/:id', {
+      templateUrl: 'public/app/features/playlist/partials/playlists.html',
+      controllerAs: 'ctrl',
+      controller: 'PlaylistsCtrl',
+      resolve: {
+        init: function(playlistSrv, $route) {
+          let playlistId = $route.current.params.id;
+          playlistSrv.start(playlistId);
+        },
+      },
+    });
+}
+
+angular.module('grafana.routes').config(grafanaRoutes);

+ 2 - 0
public/app/features/templating/editor_ctrl.ts

@@ -23,6 +23,8 @@ export class VariableEditorCtrl {
       { value: 2, text: 'Alphabetical (desc)' },
       { value: 3, text: 'Numerical (asc)' },
       { value: 4, text: 'Numerical (desc)' },
+      { value: 5, text: 'Alphabetical (case-insensitive, asc)' },
+      { value: 6, text: 'Alphabetical (case-insensitive, desc)' },
     ];
 
     $scope.hideOptions = [{ value: 0, text: '' }, { value: 1, text: 'Label' }, { value: 2, text: 'Variable' }];

+ 4 - 0
public/app/features/templating/query_variable.ts

@@ -197,6 +197,10 @@ export class QueryVariable implements Variable {
           return parseInt(matches[1], 10);
         }
       });
+    } else if (sortType === 3) {
+      options = _.sortBy(options, opt => {
+        return _.toLower(opt.text);
+      });
     }
 
     if (reverseSort) {

+ 35 - 6
public/app/features/templating/specs/query_variable.jest.ts

@@ -40,11 +40,11 @@ describe('QueryVariable', () => {
   });
 
   describe('can convert and sort metric names', () => {
-    var variable = new QueryVariable({}, null, null, null, null);
-    variable.sort = 3; // Numerical (asc)
+    const variable = new QueryVariable({}, null, null, null, null);
+    let input;
 
-    describe('can sort a mixed array of metric variables', () => {
-      var input = [
+    beforeEach(() => {
+      input = [
         { text: '0', value: '0' },
         { text: '1', value: '1' },
         { text: null, value: 3 },
@@ -58,11 +58,18 @@ describe('QueryVariable', () => {
         { text: '', value: undefined },
         { text: undefined, value: '' },
       ];
+    });
+
+    describe('can sort a mixed array of metric variables in numeric order', () => {
+      let result;
+
+      beforeEach(() => {
+        variable.sort = 3; // Numerical (asc)
+        result = variable.metricNamesToVariableValues(input);
+      });
 
-      var result = variable.metricNamesToVariableValues(input);
       it('should return in same order', () => {
         var i = 0;
-
         expect(result.length).toBe(11);
         expect(result[i++].text).toBe('');
         expect(result[i++].text).toBe('0');
@@ -73,5 +80,27 @@ describe('QueryVariable', () => {
         expect(result[i++].text).toBe('6');
       });
     });
+
+    describe('can sort a mixed array of metric variables in alphabetical order', () => {
+      let result;
+
+      beforeEach(() => {
+        variable.sort = 5; // Alphabetical CI (asc)
+        result = variable.metricNamesToVariableValues(input);
+      });
+
+      it('should return in same order', () => {
+        var i = 0;
+        console.log(result);
+        expect(result.length).toBe(11);
+        expect(result[i++].text).toBe('');
+        expect(result[i++].text).toBe('0');
+        expect(result[i++].text).toBe('1');
+        expect(result[i++].text).toBe('10');
+        expect(result[i++].text).toBe('3');
+        expect(result[i++].text).toBe('4');
+        expect(result[i++].text).toBe('5');
+      });
+    });
   });
 });

+ 1 - 0
public/app/plugins/datasource/graphite/add_graphite_func.ts

@@ -4,6 +4,7 @@ import $ from 'jquery';
 import rst2html from 'rst2html';
 import Drop from 'tether-drop';
 
+/** @ngInject */
 export function graphiteAddFunc($compile) {
   const inputTemplate =
     '<input type="text"' + ' class="gf-form-input"' + ' spellcheck="false" style="display:none"></input>';

+ 1 - 0
public/app/plugins/datasource/graphite/func_editor.ts

@@ -3,6 +3,7 @@ import _ from 'lodash';
 import $ from 'jquery';
 import rst2html from 'rst2html';
 
+/** @ngInject */
 export function graphiteFuncEditor($compile, templateSrv, popoverSrv) {
   const funcSpanTemplate = '<a ng-click="">{{func.def.name}}</a><span>(</span>';
   const paramTemplate =

+ 0 - 292
public/app/plugins/panel/graph/graph_tooltip.js

@@ -1,292 +0,0 @@
-define([
-  'jquery',
-  'app/core/core',
-],
-function ($, core) {
-  'use strict';
-
-  var appEvents = core.appEvents;
-
-  function GraphTooltip(elem, dashboard, scope, getSeriesFn) {
-    var self = this;
-    var ctrl = scope.ctrl;
-    var panel = ctrl.panel;
-
-    var $tooltip = $('<div class="graph-tooltip">');
-
-    this.destroy = function() {
-      $tooltip.remove();
-    };
-
-    this.findHoverIndexFromDataPoints = function(posX, series, last) {
-      var ps = series.datapoints.pointsize;
-      var initial = last*ps;
-      var len = series.datapoints.points.length;
-      for (var j = initial; j < len; j += ps) {
-        // Special case of a non stepped line, highlight the very last point just before a null point
-        if ((!series.lines.steps && series.datapoints.points[initial] != null && series.datapoints.points[j] == null)
-            //normal case
-            || series.datapoints.points[j] > posX) {
-          return Math.max(j - ps,  0)/ps;
-        }
-      }
-      return j/ps - 1;
-    };
-
-    this.findHoverIndexFromData = function(posX, series) {
-      var lower = 0;
-      var upper = series.data.length - 1;
-      var middle;
-      while (true) {
-        if (lower > upper) {
-          return Math.max(upper, 0);
-        }
-        middle = Math.floor((lower + upper) / 2);
-        if (series.data[middle][0] === posX) {
-          return middle;
-        } else if (series.data[middle][0] < posX) {
-          lower = middle + 1;
-        } else {
-          upper = middle - 1;
-        }
-      }
-    };
-
-    this.renderAndShow = function(absoluteTime, innerHtml, pos, xMode) {
-      if (xMode === 'time') {
-        innerHtml = '<div class="graph-tooltip-time">'+ absoluteTime + '</div>' + innerHtml;
-      }
-      $tooltip.html(innerHtml).place_tt(pos.pageX + 20, pos.pageY);
-    };
-
-    this.getMultiSeriesPlotHoverInfo = function(seriesList, pos) {
-      var value, i, series, hoverIndex, hoverDistance, pointTime, yaxis;
-      // 3 sub-arrays, 1st for hidden series, 2nd for left yaxis, 3rd for right yaxis.
-      var results = [[],[],[]];
-
-      //now we know the current X (j) position for X and Y values
-      var last_value = 0; //needed for stacked values
-
-      var minDistance, minTime;
-
-      for (i = 0; i < seriesList.length; i++) {
-        series = seriesList[i];
-
-        if (!series.data.length || (panel.legend.hideEmpty && series.allIsNull)) {
-          // Init value so that it does not brake series sorting
-          results[0].push({ hidden: true, value: 0 });
-          continue;
-        }
-
-        if (!series.data.length || (panel.legend.hideZero && series.allIsZero)) {
-          // Init value so that it does not brake series sorting
-          results[0].push({ hidden: true, value: 0 });
-          continue;
-        }
-
-        hoverIndex = this.findHoverIndexFromData(pos.x, series);
-        hoverDistance = pos.x - series.data[hoverIndex][0];
-        pointTime = series.data[hoverIndex][0];
-
-        // Take the closest point before the cursor, or if it does not exist, the closest after
-        if (! minDistance
-            || (hoverDistance >=0 && (hoverDistance < minDistance || minDistance < 0))
-            || (hoverDistance < 0 && hoverDistance > minDistance)) {
-          minDistance = hoverDistance;
-          minTime = pointTime;
-        }
-
-        if (series.stack) {
-          if (panel.tooltip.value_type === 'individual') {
-            value = series.data[hoverIndex][1];
-          } else if (!series.stack) {
-            value = series.data[hoverIndex][1];
-          } else {
-            last_value += series.data[hoverIndex][1];
-            value = last_value;
-          }
-        } else {
-          value = series.data[hoverIndex][1];
-        }
-
-        // Highlighting multiple Points depending on the plot type
-        if (series.lines.steps || series.stack) {
-          // stacked and steppedLine plots can have series with different length.
-          // Stacked series can increase its length on each new stacked serie if null points found,
-          // to speed the index search we begin always on the last found hoverIndex.
-          hoverIndex = this.findHoverIndexFromDataPoints(pos.x, series, hoverIndex);
-        }
-
-        // Be sure we have a yaxis so that it does not brake series sorting
-        yaxis = 0;
-        if (series.yaxis) {
-          yaxis = series.yaxis.n;
-        }
-
-        results[yaxis].push({
-          value: value,
-          hoverIndex: hoverIndex,
-          color: series.color,
-          label: series.aliasEscaped,
-          time: pointTime,
-          distance: hoverDistance,
-          index: i
-        });
-      }
-
-      // Contat the 3 sub-arrays
-      results = results[0].concat(results[1],results[2]);
-
-      // Time of the point closer to pointer
-      results.time = minTime;
-
-      return results;
-    };
-
-    elem.mouseleave(function () {
-      if (panel.tooltip.shared) {
-        var plot = elem.data().plot;
-        if (plot) {
-          $tooltip.detach();
-          plot.unhighlight();
-        }
-      }
-      appEvents.emit('graph-hover-clear');
-    });
-
-    elem.bind("plothover", function (event, pos, item) {
-      self.show(pos, item);
-
-      // broadcast to other graph panels that we are hovering!
-      pos.panelRelY = (pos.pageY - elem.offset().top) / elem.height();
-      appEvents.emit('graph-hover', {pos: pos, panel: panel});
-    });
-
-    elem.bind("plotclick", function (event, pos, item) {
-      appEvents.emit('graph-click', {pos: pos, panel: panel, item: item});
-    });
-
-    this.clear = function(plot) {
-      $tooltip.detach();
-      plot.clearCrosshair();
-      plot.unhighlight();
-    };
-
-    this.show = function(pos, item) {
-      var plot = elem.data().plot;
-      var plotData = plot.getData();
-      var xAxes = plot.getXAxes();
-      var xMode = xAxes[0].options.mode;
-      var seriesList = getSeriesFn();
-      var allSeriesMode = panel.tooltip.shared;
-      var group, value, absoluteTime, hoverInfo, i, series, seriesHtml, tooltipFormat;
-
-      // if panelRelY is defined another panel wants us to show a tooltip
-      // get pageX from position on x axis and pageY from relative position in original panel
-      if (pos.panelRelY) {
-        var pointOffset = plot.pointOffset({x: pos.x});
-        if (Number.isNaN(pointOffset.left) || pointOffset.left < 0 || pointOffset.left > elem.width()) {
-          self.clear(plot);
-          return;
-        }
-        pos.pageX = elem.offset().left + pointOffset.left;
-        pos.pageY = elem.offset().top + elem.height() * pos.panelRelY;
-        var isVisible = pos.pageY >= $(window).scrollTop() && pos.pageY <= $(window).innerHeight() + $(window).scrollTop();
-        if (!isVisible) {
-          self.clear(plot);
-          return;
-        }
-        plot.setCrosshair(pos);
-        allSeriesMode = true;
-
-        if (dashboard.sharedCrosshairModeOnly()) {
-          // if only crosshair mode we are done
-          return;
-        }
-      }
-
-      if (seriesList.length === 0) {
-        return;
-      }
-
-      if (seriesList[0].hasMsResolution) {
-        tooltipFormat = 'YYYY-MM-DD HH:mm:ss.SSS';
-      } else {
-        tooltipFormat = 'YYYY-MM-DD HH:mm:ss';
-      }
-
-      if (allSeriesMode) {
-        plot.unhighlight();
-
-        var seriesHoverInfo = self.getMultiSeriesPlotHoverInfo(plotData, pos);
-
-        seriesHtml = '';
-
-        absoluteTime = dashboard.formatDate(seriesHoverInfo.time, tooltipFormat);
-
-        // Dynamically reorder the hovercard for the current time point if the
-        // option is enabled.
-        if (panel.tooltip.sort === 2) {
-          seriesHoverInfo.sort(function(a, b) {
-            return b.value - a.value;
-          });
-        } else if (panel.tooltip.sort === 1) {
-          seriesHoverInfo.sort(function(a, b) {
-            return a.value - b.value;
-          });
-        }
-
-        for (i = 0; i < seriesHoverInfo.length; i++) {
-          hoverInfo = seriesHoverInfo[i];
-
-          if (hoverInfo.hidden) {
-            continue;
-          }
-
-          var highlightClass = '';
-          if (item && hoverInfo.index === item.seriesIndex) {
-            highlightClass = 'graph-tooltip-list-item--highlight';
-          }
-
-          series = seriesList[hoverInfo.index];
-
-          value = series.formatValue(hoverInfo.value);
-
-          seriesHtml += '<div class="graph-tooltip-list-item ' + highlightClass + '"><div class="graph-tooltip-series-name">';
-          seriesHtml += '<i class="fa fa-minus" style="color:' + hoverInfo.color +';"></i> ' + hoverInfo.label + ':</div>';
-          seriesHtml += '<div class="graph-tooltip-value">' + value + '</div></div>';
-          plot.highlight(hoverInfo.index, hoverInfo.hoverIndex);
-        }
-
-        self.renderAndShow(absoluteTime, seriesHtml, pos, xMode);
-      }
-      // single series tooltip
-      else if (item) {
-        series = seriesList[item.seriesIndex];
-        group = '<div class="graph-tooltip-list-item"><div class="graph-tooltip-series-name">';
-        group += '<i class="fa fa-minus" style="color:' + item.series.color +';"></i> ' + series.aliasEscaped + ':</div>';
-
-        if (panel.stack && panel.tooltip.value_type === 'individual') {
-          value = item.datapoint[1] - item.datapoint[2];
-        }
-        else {
-          value = item.datapoint[1];
-        }
-
-        value = series.formatValue(value);
-
-        absoluteTime = dashboard.formatDate(item.datapoint[0], tooltipFormat);
-
-        group += '<div class="graph-tooltip-value">' + value + '</div>';
-
-        self.renderAndShow(absoluteTime, group, pos, xMode);
-      }
-      // no hit
-      else {
-        $tooltip.detach();
-      }
-    };
-  }
-
-  return GraphTooltip;
-});

+ 289 - 0
public/app/plugins/panel/graph/graph_tooltip.ts

@@ -0,0 +1,289 @@
+import $ from 'jquery';
+import { appEvents } from 'app/core/core';
+
+export default function GraphTooltip(elem, dashboard, scope, getSeriesFn) {
+  let self = this;
+  let ctrl = scope.ctrl;
+  let panel = ctrl.panel;
+
+  let $tooltip = $('<div class="graph-tooltip">');
+
+  this.destroy = function() {
+    $tooltip.remove();
+  };
+
+  this.findHoverIndexFromDataPoints = function(posX, series, last) {
+    let ps = series.datapoints.pointsize;
+    let initial = last * ps;
+    let len = series.datapoints.points.length;
+    let j;
+    for (j = initial; j < len; j += ps) {
+      // Special case of a non stepped line, highlight the very last point just before a null point
+      if (
+        (!series.lines.steps && series.datapoints.points[initial] != null && series.datapoints.points[j] == null) ||
+        //normal case
+        series.datapoints.points[j] > posX
+      ) {
+        return Math.max(j - ps, 0) / ps;
+      }
+    }
+    return j / ps - 1;
+  };
+
+  this.findHoverIndexFromData = function(posX, series) {
+    let lower = 0;
+    let upper = series.data.length - 1;
+    let middle;
+    while (true) {
+      if (lower > upper) {
+        return Math.max(upper, 0);
+      }
+      middle = Math.floor((lower + upper) / 2);
+      if (series.data[middle][0] === posX) {
+        return middle;
+      } else if (series.data[middle][0] < posX) {
+        lower = middle + 1;
+      } else {
+        upper = middle - 1;
+      }
+    }
+  };
+
+  this.renderAndShow = function(absoluteTime, innerHtml, pos, xMode) {
+    if (xMode === 'time') {
+      innerHtml = '<div class="graph-tooltip-time">' + absoluteTime + '</div>' + innerHtml;
+    }
+    $tooltip.html(innerHtml).place_tt(pos.pageX + 20, pos.pageY);
+  };
+
+  this.getMultiSeriesPlotHoverInfo = function(seriesList, pos) {
+    let value, i, series, hoverIndex, hoverDistance, pointTime, yaxis;
+    // 3 sub-arrays, 1st for hidden series, 2nd for left yaxis, 3rd for right yaxis.
+    let results: any = [[], [], []];
+
+    //now we know the current X (j) position for X and Y values
+    let last_value = 0; //needed for stacked values
+
+    let minDistance, minTime;
+
+    for (i = 0; i < seriesList.length; i++) {
+      series = seriesList[i];
+
+      if (!series.data.length || (panel.legend.hideEmpty && series.allIsNull)) {
+        // Init value so that it does not brake series sorting
+        results[0].push({ hidden: true, value: 0 });
+        continue;
+      }
+
+      if (!series.data.length || (panel.legend.hideZero && series.allIsZero)) {
+        // Init value so that it does not brake series sorting
+        results[0].push({ hidden: true, value: 0 });
+        continue;
+      }
+
+      hoverIndex = this.findHoverIndexFromData(pos.x, series);
+      hoverDistance = pos.x - series.data[hoverIndex][0];
+      pointTime = series.data[hoverIndex][0];
+
+      // Take the closest point before the cursor, or if it does not exist, the closest after
+      if (
+        !minDistance ||
+        (hoverDistance >= 0 && (hoverDistance < minDistance || minDistance < 0)) ||
+        (hoverDistance < 0 && hoverDistance > minDistance)
+      ) {
+        minDistance = hoverDistance;
+        minTime = pointTime;
+      }
+
+      if (series.stack) {
+        if (panel.tooltip.value_type === 'individual') {
+          value = series.data[hoverIndex][1];
+        } else if (!series.stack) {
+          value = series.data[hoverIndex][1];
+        } else {
+          last_value += series.data[hoverIndex][1];
+          value = last_value;
+        }
+      } else {
+        value = series.data[hoverIndex][1];
+      }
+
+      // Highlighting multiple Points depending on the plot type
+      if (series.lines.steps || series.stack) {
+        // stacked and steppedLine plots can have series with different length.
+        // Stacked series can increase its length on each new stacked serie if null points found,
+        // to speed the index search we begin always on the last found hoverIndex.
+        hoverIndex = this.findHoverIndexFromDataPoints(pos.x, series, hoverIndex);
+      }
+
+      // Be sure we have a yaxis so that it does not brake series sorting
+      yaxis = 0;
+      if (series.yaxis) {
+        yaxis = series.yaxis.n;
+      }
+
+      results[yaxis].push({
+        value: value,
+        hoverIndex: hoverIndex,
+        color: series.color,
+        label: series.aliasEscaped,
+        time: pointTime,
+        distance: hoverDistance,
+        index: i,
+      });
+    }
+
+    // Contat the 3 sub-arrays
+    results = results[0].concat(results[1], results[2]);
+
+    // Time of the point closer to pointer
+    results.time = minTime;
+
+    return results;
+  };
+
+  elem.mouseleave(function() {
+    if (panel.tooltip.shared) {
+      let plot = elem.data().plot;
+      if (plot) {
+        $tooltip.detach();
+        plot.unhighlight();
+      }
+    }
+    appEvents.emit('graph-hover-clear');
+  });
+
+  elem.bind('plothover', function(event, pos, item) {
+    self.show(pos, item);
+
+    // broadcast to other graph panels that we are hovering!
+    pos.panelRelY = (pos.pageY - elem.offset().top) / elem.height();
+    appEvents.emit('graph-hover', { pos: pos, panel: panel });
+  });
+
+  elem.bind('plotclick', function(event, pos, item) {
+    appEvents.emit('graph-click', { pos: pos, panel: panel, item: item });
+  });
+
+  this.clear = function(plot) {
+    $tooltip.detach();
+    plot.clearCrosshair();
+    plot.unhighlight();
+  };
+
+  this.show = function(pos, item) {
+    let plot = elem.data().plot;
+    let plotData = plot.getData();
+    let xAxes = plot.getXAxes();
+    let xMode = xAxes[0].options.mode;
+    let seriesList = getSeriesFn();
+    let allSeriesMode = panel.tooltip.shared;
+    let group, value, absoluteTime, hoverInfo, i, series, seriesHtml, tooltipFormat;
+
+    // if panelRelY is defined another panel wants us to show a tooltip
+    // get pageX from position on x axis and pageY from relative position in original panel
+    if (pos.panelRelY) {
+      let pointOffset = plot.pointOffset({ x: pos.x });
+      if (Number.isNaN(pointOffset.left) || pointOffset.left < 0 || pointOffset.left > elem.width()) {
+        self.clear(plot);
+        return;
+      }
+      pos.pageX = elem.offset().left + pointOffset.left;
+      pos.pageY = elem.offset().top + elem.height() * pos.panelRelY;
+      let isVisible =
+        pos.pageY >= $(window).scrollTop() && pos.pageY <= $(window).innerHeight() + $(window).scrollTop();
+      if (!isVisible) {
+        self.clear(plot);
+        return;
+      }
+      plot.setCrosshair(pos);
+      allSeriesMode = true;
+
+      if (dashboard.sharedCrosshairModeOnly()) {
+        // if only crosshair mode we are done
+        return;
+      }
+    }
+
+    if (seriesList.length === 0) {
+      return;
+    }
+
+    if (seriesList[0].hasMsResolution) {
+      tooltipFormat = 'YYYY-MM-DD HH:mm:ss.SSS';
+    } else {
+      tooltipFormat = 'YYYY-MM-DD HH:mm:ss';
+    }
+
+    if (allSeriesMode) {
+      plot.unhighlight();
+
+      let seriesHoverInfo = self.getMultiSeriesPlotHoverInfo(plotData, pos);
+
+      seriesHtml = '';
+
+      absoluteTime = dashboard.formatDate(seriesHoverInfo.time, tooltipFormat);
+
+      // Dynamically reorder the hovercard for the current time point if the
+      // option is enabled.
+      if (panel.tooltip.sort === 2) {
+        seriesHoverInfo.sort(function(a, b) {
+          return b.value - a.value;
+        });
+      } else if (panel.tooltip.sort === 1) {
+        seriesHoverInfo.sort(function(a, b) {
+          return a.value - b.value;
+        });
+      }
+
+      for (i = 0; i < seriesHoverInfo.length; i++) {
+        hoverInfo = seriesHoverInfo[i];
+
+        if (hoverInfo.hidden) {
+          continue;
+        }
+
+        let highlightClass = '';
+        if (item && hoverInfo.index === item.seriesIndex) {
+          highlightClass = 'graph-tooltip-list-item--highlight';
+        }
+
+        series = seriesList[hoverInfo.index];
+
+        value = series.formatValue(hoverInfo.value);
+
+        seriesHtml +=
+          '<div class="graph-tooltip-list-item ' + highlightClass + '"><div class="graph-tooltip-series-name">';
+        seriesHtml +=
+          '<i class="fa fa-minus" style="color:' + hoverInfo.color + ';"></i> ' + hoverInfo.label + ':</div>';
+        seriesHtml += '<div class="graph-tooltip-value">' + value + '</div></div>';
+        plot.highlight(hoverInfo.index, hoverInfo.hoverIndex);
+      }
+
+      self.renderAndShow(absoluteTime, seriesHtml, pos, xMode);
+    } else if (item) {
+      // single series tooltip
+      series = seriesList[item.seriesIndex];
+      group = '<div class="graph-tooltip-list-item"><div class="graph-tooltip-series-name">';
+      group +=
+        '<i class="fa fa-minus" style="color:' + item.series.color + ';"></i> ' + series.aliasEscaped + ':</div>';
+
+      if (panel.stack && panel.tooltip.value_type === 'individual') {
+        value = item.datapoint[1] - item.datapoint[2];
+      } else {
+        value = item.datapoint[1];
+      }
+
+      value = series.formatValue(value);
+
+      absoluteTime = dashboard.formatDate(item.datapoint[0], tooltipFormat);
+
+      group += '<div class="graph-tooltip-value">' + value + '</div>';
+
+      self.renderAndShow(absoluteTime, group, pos, xMode);
+    } else {
+      // no hit
+      $tooltip.detach();
+    }
+  };
+}

+ 8 - 5
public/app/plugins/panel/graph/legend.ts

@@ -131,8 +131,11 @@ module.directive('graphLegend', function(popoverSrv, $timeout) {
         elem.empty();
 
         // Set min-width if side style and there is a value, otherwise remove the CSS propery
-        var width = panel.legend.rightSide && panel.legend.sideWidth ? panel.legend.sideWidth + 'px' : '';
+        // Set width so it works with IE11
+        var width: any = panel.legend.rightSide && panel.legend.sideWidth ? panel.legend.sideWidth + 'px' : '';
+        var ieWidth: any = panel.legend.rightSide && panel.legend.sideWidth ? panel.legend.sideWidth - 1 + 'px' : '';
         elem.css('min-width', width);
+        elem.css('width', ieWidth);
 
         elem.toggleClass('graph-legend-table', panel.legend.alignAsTable === true);
 
@@ -238,10 +241,10 @@ module.directive('graphLegend', function(popoverSrv, $timeout) {
           tbodyElem.append(tableHeaderElem);
           tbodyElem.append(seriesElements);
           elem.append(tbodyElem);
-          tbodyElem.wrap('<div class="graph-legend-content"></div>');
+          tbodyElem.wrap('<div class="graph-legend-scroll"></div>');
         } else {
-          elem.append('<div class="graph-legend-content"></div>');
-          elem.find('.graph-legend-content').append(seriesElements);
+          elem.append('<div class="graph-legend-scroll"></div>');
+          elem.find('.graph-legend-scroll').append(seriesElements);
         }
 
         if (!panel.legend.rightSide || (panel.legend.rightSide && legendWidth !== legendRightDefaultWidth)) {
@@ -261,7 +264,7 @@ module.directive('graphLegend', function(popoverSrv, $timeout) {
         `;
 
         let scrollRoot = elem;
-        let scroller = elem.find('.graph-legend-content');
+        let scroller = elem.find('.graph-legend-scroll');
 
         // clear existing scroll bar track to prevent duplication
         scrollRoot.find('.baron__track').remove();

+ 3 - 2
public/app/plugins/panel/graph/specs/tooltip_specs.ts

@@ -11,6 +11,7 @@ var scope = {
 
 var elem = $('<div></div>');
 var dashboard = {};
+var getSeriesFn;
 
 function describeSharedTooltip(desc, fn) {
   var ctx: any = {};
@@ -30,7 +31,7 @@ function describeSharedTooltip(desc, fn) {
   describe(desc, function() {
     beforeEach(function() {
       ctx.setupFn();
-      var tooltip = new GraphTooltip(elem, dashboard, scope);
+      var tooltip = new GraphTooltip(elem, dashboard, scope, getSeriesFn);
       ctx.results = tooltip.getMultiSeriesPlotHoverInfo(ctx.data, ctx.pos);
     });
 
@@ -39,7 +40,7 @@ function describeSharedTooltip(desc, fn) {
 }
 
 describe('findHoverIndexFromData', function() {
-  var tooltip = new GraphTooltip(elem, dashboard, scope);
+  var tooltip = new GraphTooltip(elem, dashboard, scope, getSeriesFn);
   var series = {
     data: [[100, 0], [101, 0], [102, 0], [103, 0], [104, 0], [105, 0], [106, 0], [107, 0]],
   };

+ 3 - 1
public/app/plugins/panel/graph/template.ts

@@ -3,7 +3,9 @@ var template = `
   <div class="graph-panel__chart" grafana-graph ng-dblclick="ctrl.zoomOut()">
   </div>
 
-  <div class="graph-legend" graph-legend></div>
+  <div class="graph-legend">
+    <div class="graph-legend-content" graph-legend></div>
+  </div>
 </div>
 `;
 

+ 3 - 3
public/app/plugins/panel/table/column_options.html

@@ -163,10 +163,10 @@
           <span>
             Use special variables to specify cell values:
             <br>
-            <em>$__cell</em> refers to current cell value
+            <em>${__cell}</em> refers to current cell value
             <br>
-            <em>$__cell_n</em> refers to Nth column value in current row. Column indexes are started from 0. For instance,
-            <em>$__cell_1</em> refers to second column's value.
+            <em>${__cell_n}</em> refers to Nth column value in current row. Column indexes are started from 0. For instance,
+            <em>${__cell_1}</em> refers to second column's value.
           </span>
         </info-popover>
       </div>

+ 1 - 2
public/sass/_variables.light.scss

@@ -59,9 +59,8 @@ $critical: #ec2128;
 $body-bg: $gray-7;
 $page-bg: $gray-7;
 $body-color: $gray-1;
-//$text-color: $dark-4;
 $text-color: $gray-1;
-$text-color-strong: $white;
+$text-color-strong: $dark-2;
 $text-color-weak: $gray-2;
 $text-color-faint: $gray-4;
 $text-color-emphasis: $dark-5;

+ 4 - 2
public/sass/base/_icons.scss

@@ -1,8 +1,10 @@
 .gicon {
   line-height: 1;
   display: inline-block;
-  width: 1.1057142857em;
-  height: 1.1057142857em;
+  //width: 1.1057142857em;
+  //height: 1.1057142857em;
+  height: 22px;
+  width: 22px;
   text-align: center;
   background-repeat: no-repeat;
   background-position: center;

+ 7 - 1
public/sass/components/_panel_graph.scss

@@ -49,6 +49,7 @@
 }
 
 .graph-legend {
+  display: flex;
   flex: 0 1 auto;
   max-height: 30%;
   margin: 0;
@@ -56,7 +57,8 @@
   padding-top: 6px;
   position: relative;
 
-  height: 100%;
+  // fix for Firefox (white stripe on the right of scrollbar)
+  width: 99%;
 
   .popover-content {
     padding: 0;
@@ -67,6 +69,10 @@
   position: relative;
 }
 
+.graph-legend-scroll {
+  position: relative;
+}
+
 .graph-legend-icon {
   position: relative;
   padding-right: 4px;

+ 2 - 2
public/sass/components/_scrollbar.scss

@@ -192,12 +192,12 @@
 
   // Width needs to be set to prevent content width issues
   // Set to 99% instead of 100% for fixing Firefox issue (white stripe on the right of scrollbar)
-  min-width: 99%;
+  width: 99%;
 }
 
 // Fix for side menu on mobile devices
 .main-view.baron {
-  min-width: unset;
+  width: unset;
 }
 
 .baron__clipper {

+ 7 - 4
public/sass/components/_search.scss

@@ -31,7 +31,6 @@
     //padding: 0.5rem 1.5rem 0.5rem 0;
     padding: 1rem 1rem 0.75rem 1rem;
     height: 51px;
-    line-height: 51px;
     box-sizing: border-box;
     outline: none;
     background: $side-menu-bg;
@@ -62,7 +61,9 @@
   flex-direction: column;
   flex-grow: 1;
 
-  // overflow-y: scroll;
+  .search-item--indent {
+    margin-left: 14px;
+  }
 }
 
 .search-dropdown__col_2 {
@@ -102,18 +103,20 @@
 }
 
 .search-results-scroller {
+  display: flex;
   position: relative;
-  height: 100%;
 }
 
 .search-results-container {
-  height: 100%;
   display: block;
   padding: $spacer;
   position: relative;
   flex-grow: 10;
   margin-bottom: 1rem;
 
+  // Fix for search scroller in mobile view
+  height: unset;
+
   .label-tag {
     margin-left: 6px;
     font-size: 11px;

+ 1 - 0
public/sass/components/_sidemenu.scss

@@ -178,6 +178,7 @@ li.sidemenu-org-switcher {
   padding: 0.4rem 1rem 0.4rem 0.65rem;
   min-height: $navbarHeight;
   position: relative;
+  height: $navbarHeight - 1px;
 
   &:hover {
     background: $navbarButtonBackgroundHighlight;

+ 1 - 1
public/sass/components/_tabbed_view.scss

@@ -43,7 +43,7 @@
     font-size: 120%;
   }
   &:hover {
-    color: $white;
+    color: $text-color-strong;
   }
 }
 

+ 2 - 1
public/sass/pages/_alerting.scss

@@ -108,7 +108,8 @@
   justify-content: center;
   align-items: center;
   width: 40px;
-  padding: 0 28px 0 16px;
+  //margin-right: 8px;
+  padding: 0 4px 0 2px;
   .icon-gf,
   .fa {
     font-size: 200%;

+ 1 - 1
public/sass/pages/_dashboard.scss

@@ -33,7 +33,7 @@ div.flot-text {
   border: $panel-border;
   position: relative;
   border-radius: 3px;
-  height: 100%;
+  //height: 100%;
 
   &.panel-transparent {
     background-color: transparent;

+ 7 - 1
public/sass/pages/_login.scss

@@ -3,6 +3,7 @@ $login-border: #8daac5;
 .login {
   background-position: center;
   min-height: 85vh;
+  height: 80vh;
   background-repeat: no-repeat;
   min-width: 100%;
   margin-left: 0;
@@ -290,9 +291,14 @@ select:-webkit-autofill:focus {
 }
 
 @include media-breakpoint-up(md) {
+  .login-content {
+    flex: 1 0 100%;
+  }
+
   .login-branding {
     width: 45%;
     padding: 2rem 4rem;
+    flex-grow: 1;
 
     .logo-icon {
       width: 130px;
@@ -371,7 +377,7 @@ select:-webkit-autofill:focus {
       left: 0;
       right: 0;
       height: 100%;
-      content: "";
+      content: '';
       display: block;
     }