瀏覽代碼

Merge branch 'master' into sql-uint64

Marcus Efraimsson 7 年之前
父節點
當前提交
224a0eb0d3

+ 34 - 0
docs/sources/features/datasources/cloudwatch.md

@@ -43,6 +43,40 @@ server is running on AWS you can use IAM Roles and authentication will be handle
 
 Checkout AWS docs on [IAM Roles](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html)
 
+## IAM Policies
+
+Grafana needs permissions granted via IAM to be able to read CloudWatch metrics
+and EC2 tags/instances. You can attach these permissions to IAM roles and
+utilize Grafana's built-in support for assuming roles.
+
+Here is a minimal policy example:
+
+```json
+{
+    "Version": "2012-10-17",
+    "Statement": [
+        {
+            "Sid": "AllowReadingMetricsFromCloudWatch",
+            "Effect": "Allow",
+            "Action": [
+                "cloudwatch:ListMetrics",
+                "cloudwatch:GetMetricStatistics"
+            ],
+            "Resource": "*"
+        },
+        {
+            "Sid": "AllowReadingTagsFromEC2",
+            "Effect": "Allow",
+            "Action": [
+                "ec2:DescribeTags",
+                "ec2:DescribeInstances"
+            ],
+            "Resource": "*"
+        }
+    ]
+}
+```
+
 ### AWS credentials file
 
 Create a file at `~/.aws/credentials`. That is the `HOME` path for user running grafana-server.

+ 1 - 1
package.json

@@ -136,6 +136,7 @@
     "angular-route": "^1.6.6",
     "angular-sanitize": "^1.6.6",
     "babel-polyfill": "^6.26.0",
+    "baron": "^3.0.3",
     "brace": "^0.10.0",
     "classnames": "^2.2.5",
     "clipboard": "^1.7.1",
@@ -151,7 +152,6 @@
     "moment": "^2.18.1",
     "mousetrap": "^1.6.0",
     "mousetrap-global-bind": "^1.1.0",
-    "perfect-scrollbar": "^1.2.0",
     "prop-types": "^15.6.0",
     "react": "^16.2.0",
     "react-dom": "^16.2.0",

+ 21 - 10
public/app/core/components/ScrollBar/ScrollBar.tsx

@@ -1,5 +1,5 @@
 import React from 'react';
-import PerfectScrollbar from 'perfect-scrollbar';
+import baron from 'baron';
 
 export interface Props {
   children: any;
@@ -8,31 +8,36 @@ export interface Props {
 
 export default class ScrollBar extends React.Component<Props, any> {
   private container: any;
-  private ps: PerfectScrollbar;
+  private scrollbar: baron;
 
   constructor(props) {
     super(props);
   }
 
   componentDidMount() {
-    this.ps = new PerfectScrollbar(this.container, {
-      wheelPropagation: true,
+    this.scrollbar = baron({
+      root: this.container.parentElement,
+      scroller: this.container,
+      bar: '.baron__bar',
+      barOnCls: '_scrollbar',
+      scrollingCls: '_scrolling',
+      track: '.baron__track',
     });
   }
 
   componentDidUpdate() {
-    this.ps.update();
+    this.scrollbar.update();
   }
 
   componentWillUnmount() {
-    this.ps.destroy();
+    this.scrollbar.dispose();
   }
 
   // methods can be invoked by outside
   setScrollTop(top) {
     if (this.container) {
       this.container.scrollTop = top;
-      this.ps.update();
+      this.scrollbar.update();
 
       return true;
     }
@@ -42,7 +47,7 @@ export default class ScrollBar extends React.Component<Props, any> {
   setScrollLeft(left) {
     if (this.container) {
       this.container.scrollLeft = left;
-      this.ps.update();
+      this.scrollbar.update();
 
       return true;
     }
@@ -55,8 +60,14 @@ export default class ScrollBar extends React.Component<Props, any> {
 
   render() {
     return (
-      <div className={this.props.className} ref={this.handleRef}>
-        {this.props.children}
+      <div className="baron baron__root baron__clipper">
+        <div className={this.props.className + ' baron__scroller'} ref={this.handleRef}>
+          {this.props.children}
+        </div>
+
+        <div className="baron__track">
+          <div className="baron__bar" />
+        </div>
       </div>
     );
   }

+ 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);

+ 41 - 0
public/app/core/components/scroll/page_scroll.ts

@@ -0,0 +1,41 @@
+import coreModule from 'app/core/core_module';
+import appEvents from 'app/core/app_events';
+
+export function pageScrollbar() {
+  return {
+    restrict: 'A',
+    link: function(scope, elem, attrs) {
+      let lastPos = 0;
+
+      appEvents.on(
+        'dash-scroll',
+        evt => {
+          if (evt.restore) {
+            elem[0].scrollTop = lastPos;
+            return;
+          }
+
+          lastPos = elem[0].scrollTop;
+
+          if (evt.animate) {
+            elem.animate({ scrollTop: evt.pos }, 500);
+          } else {
+            elem[0].scrollTop = evt.pos;
+          }
+        },
+        scope
+      );
+
+      scope.$on('$routeChangeSuccess', () => {
+        lastPos = 0;
+        elem[0].scrollTop = 0;
+        elem[0].focus();
+      });
+
+      elem[0].tabIndex = -1;
+      elem[0].focus();
+    },
+  };
+}
+
+coreModule.directive('pageScrollbar', pageScrollbar);

+ 46 - 6
public/app/core/components/scroll/scroll.ts

@@ -1,15 +1,44 @@
-import PerfectScrollbar from 'perfect-scrollbar';
+import $ from 'jquery';
+import baron from 'baron';
 import coreModule from 'app/core/core_module';
 import appEvents from 'app/core/app_events';
 
+const scrollBarHTML = `
+<div class="baron__track">
+  <div class="baron__bar"></div>
+</div>
+`;
+
+const scrollRootClass = 'baron baron__root';
+const scrollerClass = 'baron__scroller';
+
 export function geminiScrollbar() {
   return {
     restrict: 'A',
     link: function(scope, elem, attrs) {
-      let scrollbar = new PerfectScrollbar(elem[0], {
-        wheelPropagation: true,
-        wheelSpeed: 3,
-      });
+      let scrollRoot = elem.parent();
+      let scroller = elem;
+
+      if (attrs.grafanaScrollbar && attrs.grafanaScrollbar === 'scrollonroot') {
+        scrollRoot = scroller;
+      }
+
+      scrollRoot.addClass(scrollRootClass);
+      $(scrollBarHTML).appendTo(scrollRoot);
+      elem.addClass(scrollerClass);
+
+      let scrollParams = {
+        root: scrollRoot[0],
+        scroller: scroller[0],
+        bar: '.baron__bar',
+        barOnCls: '_scrollbar',
+        scrollingCls: '_scrolling',
+        track: '.baron__track',
+        direction: 'v',
+      };
+
+      let scrollbar = baron(scrollParams);
+
       let lastPos = 0;
 
       appEvents.on(
@@ -31,13 +60,24 @@ export function geminiScrollbar() {
         scope
       );
 
+      // 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;
         elem[0].scrollTop = 0;
       });
 
       scope.$on('$destroy', () => {
-        scrollbar.destroy();
+        scrollbar.dispose();
       });
     },
   };

+ 2 - 0
public/app/core/components/search/search.html

@@ -19,6 +19,7 @@
 
 	<div class="search-dropdown">
     <div class="search-dropdown__col_1">
+      <div class="search-results-scroller">
         <div class="search-results-container" grafana-scrollbar>
           <h6 ng-show="!ctrl.isLoading && ctrl.results.length === 0">No dashboards matching your query were found.</h6>
           <dashboard-search-results
@@ -27,6 +28,7 @@
             on-folder-expanding="ctrl.folderExpanding()"
             on-folder-expanded="ctrl.folderExpanded($folder)" />
         </div>
+      </div>
     </div>
 
     <div class="search-dropdown__col_2">

+ 2 - 0
public/app/core/core.ts

@@ -47,6 +47,7 @@ import { NavModelSrv, NavModel } from './nav_model_srv';
 import { userPicker } from './components/user_picker';
 import { teamPicker } from './components/team_picker';
 import { geminiScrollbar } from './components/scroll/scroll';
+import { pageScrollbar } from './components/scroll/page_scroll';
 import { gfPageDirective } from './components/gf_page';
 import { orgSwitcher } from './components/org_switcher';
 import { profiler } from './profiler';
@@ -85,6 +86,7 @@ export {
   userPicker,
   teamPicker,
   geminiScrollbar,
+  pageScrollbar,
   gfPageDirective,
   orgSwitcher,
   manageDashboardsDirective,

+ 1 - 1
public/app/features/dashboard/dashgrid/AddPanelPanel.tsx

@@ -103,7 +103,7 @@ export class AddPanelPanel extends React.Component<AddPanelPanelProps, AddPanelP
 
   render() {
     return (
-      <div className="panel-container">
+      <div className="panel-container add-panel-container">
         <div className="add-panel">
           <div className="add-panel__header">
             <i className="gicon gicon-add-panel" />

+ 2 - 1
public/app/features/dashboard/view_state_srv.ts

@@ -196,9 +196,10 @@ export class DashboardViewState {
     this.oldTimeRange = ctrl.range;
     this.fullscreenPanel = panelScope;
 
+    // Firefox doesn't return scrollTop postion properly if 'dash-scroll' is emitted after setViewMode()
+    this.$scope.appEvent('dash-scroll', { animate: false, pos: 0 });
     this.dashboard.setViewMode(ctrl.panel, true, ctrl.editMode);
     this.$scope.appEvent('panel-fullscreen-enter', { panelId: ctrl.panel.id });
-    this.$scope.appEvent('dash-scroll', { animate: false, pos: 0 });
   }
 
   registerPanel(panelScope) {

+ 30 - 4
public/app/features/panel/panel_directive.ts

@@ -1,6 +1,7 @@
 import angular from 'angular';
+import $ from 'jquery';
 import Drop from 'tether-drop';
-import PerfectScrollbar from 'perfect-scrollbar';
+import baron from 'baron';
 
 var module = angular.module('grafana.directives');
 
@@ -86,6 +87,9 @@ module.directive('grafanaPanel', function($rootScope, $document, $timeout) {
 
       function panelHeightUpdated() {
         panelContent.css({ height: ctrl.height + 'px' });
+      }
+
+      function resizeScrollableContent() {
         if (panelScrollbar) {
           panelScrollbar.update();
         }
@@ -100,9 +104,30 @@ module.directive('grafanaPanel', function($rootScope, $document, $timeout) {
       // update scrollbar after mounting
       ctrl.events.on('component-did-mount', () => {
         if (ctrl.__proto__.constructor.scrollable) {
-          panelScrollbar = new PerfectScrollbar(panelContent[0], {
-            wheelPropagation: true,
+          const scrollRootClass = 'baron baron__root baron__clipper panel-content--scrollable';
+          const scrollerClass = 'baron__scroller';
+          const scrollBarHTML = `
+            <div class="baron__track">
+              <div class="baron__bar"></div>
+            </div>
+          `;
+
+          let scrollRoot = panelContent;
+          let scroller = panelContent.find(':first').find(':first');
+
+          scrollRoot.addClass(scrollRootClass);
+          $(scrollBarHTML).appendTo(scrollRoot);
+          scroller.addClass(scrollerClass);
+
+          panelScrollbar = baron({
+            root: scrollRoot[0],
+            scroller: scroller[0],
+            bar: '.baron__bar',
+            barOnCls: '_scrollbar',
+            scrollingCls: '_scrolling',
           });
+
+          panelScrollbar.scroll();
         }
       });
 
@@ -110,6 +135,7 @@ module.directive('grafanaPanel', function($rootScope, $document, $timeout) {
         ctrl.calculatePanelHeight();
         panelHeightUpdated();
         $timeout(() => {
+          resizeScrollableContent();
           ctrl.render();
         });
       });
@@ -199,7 +225,7 @@ module.directive('grafanaPanel', function($rootScope, $document, $timeout) {
         }
 
         if (panelScrollbar) {
-          panelScrollbar.update();
+          panelScrollbar.dispose();
         }
       });
     },

+ 3 - 1
public/app/features/templating/query_variable.ts

@@ -198,7 +198,9 @@ export class QueryVariable implements Variable {
         }
       });
     } else if (sortType === 3) {
-      options = _.sortBy(options, opt => { return _.toLower(opt.text); });
+      options = _.sortBy(options, opt => {
+        return _.toLower(opt.text);
+      });
     }
 
     if (reverseSort) {

+ 12 - 12
public/app/partials/dashboard.html

@@ -1,18 +1,18 @@
 <div dash-class ng-if="ctrl.dashboard">
 	<dashnav dashboard="ctrl.dashboard"></dashnav>
 
-	<div class="scroll-canvas scroll-canvas--dashboard" grafana-scrollbar>
-		<dashboard-settings dashboard="ctrl.dashboard"
-											  ng-if="ctrl.dashboardViewState.state.editview"
-												class="dashboard-settings">
-		</dashboard-settings>
+	<div class="scroll-canvas scroll-canvas--dashboard" page-scrollbar>
+    <dashboard-settings dashboard="ctrl.dashboard"
+                        ng-if="ctrl.dashboardViewState.state.editview"
+                        class="dashboard-settings">
+    </dashboard-settings>
 
-		<div class="dashboard-container">
-			<dashboard-submenu ng-if="ctrl.dashboard.meta.submenuEnabled" dashboard="ctrl.dashboard">
-			</dashboard-submenu>
+    <div class="dashboard-container">
+      <dashboard-submenu ng-if="ctrl.dashboard.meta.submenuEnabled" dashboard="ctrl.dashboard">
+      </dashboard-submenu>
 
-			<dashboard-grid get-panel-container="ctrl.getPanelContainer">
-			</dashboard-grid>
-		</div>
-	</div>
+      <dashboard-grid get-panel-container="ctrl.getPanelContainer">
+      </dashboard-grid>
+    </div>
+  </div>
 </div>

+ 16 - 14
public/app/plugins/panel/dashlist/module.html

@@ -1,17 +1,19 @@
-<div class="dashlist" ng-repeat="group in ctrl.groups">
-  <div class="dashlist-section" ng-if="group.show">
-    <h6 class="dashlist-section-header" ng-show="ctrl.panel.headings">
-      {{group.header}}
-    </h6>
-    <div class="dashlist-item" ng-repeat="dash in group.list">
-      <a class="dashlist-link dashlist-link-{{dash.type}}" href="{{dash.url}}">
-        <span class="dashlist-title">
-          {{dash.title}}
-        </span>
-        <span class="dashlist-star" ng-click="ctrl.starDashboard(dash, $event)">
-          <i class="fa" ng-class="{'fa-star': dash.isStarred, 'fa-star-o': dash.isStarred === false}"></i>
-        </span>
-      </a>
+<div>
+  <div class="dashlist" ng-repeat="group in ctrl.groups">
+    <div class="dashlist-section" ng-if="group.show">
+      <h6 class="dashlist-section-header" ng-show="ctrl.panel.headings">
+        {{group.header}}
+      </h6>
+      <div class="dashlist-item" ng-repeat="dash in group.list">
+        <a class="dashlist-link dashlist-link-{{dash.type}}" href="{{dash.url}}">
+          <span class="dashlist-title">
+            {{dash.title}}
+          </span>
+          <span class="dashlist-star" ng-click="ctrl.starDashboard(dash, $event)">
+            <i class="fa" ng-class="{'fa-star': dash.isStarred, 'fa-star-o': dash.isStarred === false}"></i>
+          </span>
+        </a>
+      </div>
     </div>
   </div>
 </div>

+ 39 - 16
public/app/plugins/panel/graph/legend.ts

@@ -1,7 +1,7 @@
 import angular from 'angular';
 import _ from 'lodash';
 import $ from 'jquery';
-import PerfectScrollbar from 'perfect-scrollbar';
+import baron from 'baron';
 
 var module = angular.module('grafana.directives');
 
@@ -16,11 +16,10 @@ module.directive('graphLegend', function(popoverSrv, $timeout) {
       var i;
       var legendScrollbar;
       const legendRightDefaultWidth = 10;
+      let legendElem = elem.parent();
 
       scope.$on('$destroy', function() {
-        if (legendScrollbar) {
-          legendScrollbar.destroy();
-        }
+        destroyScrollbar();
       });
 
       ctrl.events.on('render-legend', () => {
@@ -112,7 +111,7 @@ module.directive('graphLegend', function(popoverSrv, $timeout) {
       }
 
       function render() {
-        let legendWidth = elem.width();
+        let legendWidth = legendElem.width();
         if (!ctrl.panel.legend.show) {
           elem.empty();
           firstRender = true;
@@ -134,8 +133,8 @@ module.directive('graphLegend', function(popoverSrv, $timeout) {
         // 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);
+        legendElem.css('min-width', width);
+        legendElem.css('width', ieWidth);
 
         elem.toggleClass('graph-legend-table', panel.legend.alignAsTable === true);
 
@@ -241,8 +240,10 @@ module.directive('graphLegend', function(popoverSrv, $timeout) {
           tbodyElem.append(tableHeaderElem);
           tbodyElem.append(seriesElements);
           elem.append(tbodyElem);
+          tbodyElem.wrap('<div class="graph-legend-scroll"></div>');
         } else {
-          elem.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)) {
@@ -253,23 +254,45 @@ module.directive('graphLegend', function(popoverSrv, $timeout) {
       }
 
       function addScrollbar() {
-        const scrollbarOptions = {
-          // Number of pixels the content height can surpass the container height without enabling the scroll bar.
-          scrollYMarginOffset: 2,
-          suppressScrollX: true,
-          wheelPropagation: true,
+        const scrollRootClass = 'baron baron__root';
+        const scrollerClass = 'baron__scroller';
+        const scrollBarHTML = `
+          <div class="baron__track">
+            <div class="baron__bar"></div>
+          </div>
+        `;
+
+        let scrollRoot = elem;
+        let scroller = elem.find('.graph-legend-scroll');
+
+        // clear existing scroll bar track to prevent duplication
+        scrollRoot.find('.baron__track').remove();
+
+        scrollRoot.addClass(scrollRootClass);
+        $(scrollBarHTML).appendTo(scrollRoot);
+        scroller.addClass(scrollerClass);
+
+        let scrollbarParams = {
+          root: scrollRoot[0],
+          scroller: scroller[0],
+          bar: '.baron__bar',
+          track: '.baron__track',
+          barOnCls: '_scrollbar',
+          scrollingCls: '_scrolling',
         };
 
         if (!legendScrollbar) {
-          legendScrollbar = new PerfectScrollbar(elem[0], scrollbarOptions);
+          legendScrollbar = baron(scrollbarParams);
         } else {
-          legendScrollbar.update();
+          destroyScrollbar();
+          legendScrollbar = baron(scrollbarParams);
         }
+        legendScrollbar.scroll();
       }
 
       function destroyScrollbar() {
         if (legendScrollbar) {
-          legendScrollbar.destroy();
+          legendScrollbar.dispose();
           legendScrollbar = undefined;
         }
       }

+ 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>
 `;
 

+ 8 - 1
public/sass/components/_panel_add_panel.scss

@@ -1,5 +1,13 @@
+.add-panel-container {
+  height: 100%;
+}
+
 .add-panel {
   height: 100%;
+
+  .baron__root {
+    height: calc(100% - 43px);
+  }
 }
 
 .add-panel__header {
@@ -39,7 +47,6 @@
   flex-direction: row;
   flex-wrap: wrap;
   overflow: auto;
-  height: calc(100% - 43px);
   align-content: flex-start;
   justify-content: space-around;
   position: relative;

+ 30 - 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,11 +57,27 @@
   padding-top: 6px;
   position: relative;
 
+  // fix for Firefox (white stripe on the right of scrollbar)
+  width: calc(100% - 1px);
+
   .popover-content {
     padding: 0;
   }
 }
 
+.graph-legend-content {
+  position: relative;
+
+  // fix for Firefox (white stripe on the right of scrollbar)
+  width: calc(100% - 1px);
+}
+
+.graph-legend-scroll {
+  position: relative;
+  overflow: auto !important;
+  padding: 1px;
+}
+
 .graph-legend-icon {
   position: relative;
   padding-right: 4px;
@@ -115,8 +132,20 @@
 // fix for phantomjs
 .body--phantomjs {
   .graph-panel--legend-right {
+    .graph-legend {
+      display: inline-block;
+    }
+
+    .graph-panel__chart {
+      display: flex;
+    }
+
     .graph-legend-table {
       display: table;
+
+      .graph-legend-scroll {
+        display: table;
+      }
     }
   }
 }
@@ -124,9 +153,9 @@
 .graph-legend-table {
   tbody {
     display: block;
+    position: relative;
     overflow-y: auto;
     overflow-x: hidden;
-    height: 100%;
     padding-bottom: 1px;
     padding-right: 5px;
     padding-left: 5px;

+ 123 - 1
public/sass/components/_scrollbar.scss

@@ -9,6 +9,11 @@
   -ms-touch-action: auto;
 }
 
+// ._scrollbar {
+//   overflow-x: hidden !important;
+//   overflow-y: auto;
+// }
+
 /*
  * Scrollbar rail styles
  */
@@ -101,7 +106,7 @@
   opacity: 0.9;
 }
 
-// Srollbars
+// Scrollbars
 //
 
 ::-webkit-scrollbar {
@@ -172,3 +177,120 @@
   border-top: 1px solid $scrollbarBorder;
   border-left: 1px solid $scrollbarBorder;
 }
+
+// Baron styles
+
+.baron {
+  // display: inline-block; // this brakes phantomjs rendering (width becomes 0)
+  overflow: hidden;
+}
+
+// Fix for side menu on mobile devices
+.main-view.baron {
+  width: unset;
+}
+
+.baron__clipper {
+  position: relative;
+  overflow: hidden;
+}
+
+.baron__scroller {
+  overflow-y: scroll;
+  -ms-overflow-style: none;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  margin: 0;
+  border: 0;
+  padding: 0;
+  width: 100%;
+  height: 100%;
+  -webkit-overflow-scrolling: touch;
+  /* remove line to customize scrollbar in iOs */
+}
+
+.baron__scroller::-webkit-scrollbar {
+  width: 0;
+  height: 0;
+}
+
+.baron__track {
+  display: none;
+  position: absolute;
+  top: 0;
+  right: 0;
+  bottom: 0;
+}
+
+.baron._scrollbar .baron__track {
+  display: block;
+}
+
+.baron__free {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  right: 0;
+}
+
+.baron__bar {
+  display: none;
+  position: absolute;
+  right: 0;
+  z-index: 1;
+  // width: 10px;
+  background: #999;
+
+  // height: 15px;
+  width: 15px;
+  transition: background-color 0.2s linear, opacity 0.2s linear;
+  opacity: 0;
+}
+
+.baron._scrollbar .baron__bar {
+  display: block;
+
+  @include gradient-vertical($scrollbarBackground, $scrollbarBackground2);
+  border-radius: 6px;
+  width: 6px;
+  /* there must be 'right' for ps__thumb-y */
+  right: 0px;
+  /* please don't change 'position' */
+  position: absolute;
+
+  // background-color: transparent;
+  // opacity: 0.6;
+
+  &:hover,
+  &:focus {
+    // background-color: transparent;
+    opacity: 0.9;
+  }
+}
+
+.panel-hover-highlight .baron__track .baron__bar {
+  opacity: 0.6;
+}
+
+.baron._scrolling > .baron__track .baron__bar {
+  opacity: 0.9;
+}
+
+// fix for phantomjs
+.body--phantomjs .baron__track .baron__bar {
+  opacity: 0 !important;
+}
+
+.baron__control {
+  display: none;
+}
+
+.baron.panel-content--scrollable {
+  // Width needs to be set to prevent content width issues
+  // Set to less than 100% for fixing Firefox issue (white stripe on the right of scrollbar)
+  width: calc(100% - 2px);
+
+  .baron__scroller {
+    padding-top: 1px;
+  }
+}

+ 8 - 1
public/sass/components/_search.scss

@@ -102,14 +102,21 @@
   }
 }
 
+.search-results-scroller {
+  display: flex;
+  position: relative;
+}
+
 .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;

+ 8 - 0
public/sass/layout/_page.scss

@@ -28,12 +28,20 @@
   width: 100%;
   overflow: auto;
   height: 100%;
+  -webkit-overflow-scrolling: touch;
 
   &--dashboard {
     height: calc(100% - 56px);
   }
 }
 
+// fix for phantomjs
+.body--phantomjs {
+  .scroll-canvas {
+    overflow: hidden;
+  }
+}
+
 .page-body {
   padding-top: $spacer*2;
   min-height: 500px;

+ 2 - 2
public/views/index.template.html

@@ -16,7 +16,7 @@
   <link rel="icon" type="image/png" href="public/img/fav32.png">
   <link rel="mask-icon" href="public/img/grafana_mask_icon.svg" color="#F05A28">
   <link rel="apple-touch-icon" href="public/img/fav32.png">
-  
+
 </head>
 
 <body ng-cloak class="theme-[[ .Theme ]]">
@@ -40,7 +40,7 @@
     </div>
 
     <div class="main-view">
-      <div class="scroll-canvas" grafana-scrollbar>
+      <div class="scroll-canvas" page-scrollbar>
         <div ng-view></div>
 
         <footer class="footer">

+ 1 - 14
scripts/circle-test-backend.sh

@@ -20,17 +20,4 @@ echo "building backend with install to cache pkgs"
 exit_if_fail time go install ./pkg/cmd/grafana-server
 
 echo "running go test"
-
-set -e
-echo "" > coverage.txt
-
-time for d in $(go list ./pkg/...); do
-  exit_if_fail go test -coverprofile=profile.out -covermode=atomic $d
-  if [ -f profile.out ]; then
-    cat profile.out >> coverage.txt
-    rm profile.out
-  fi
-done
-
-echo "Publishing go code coverage"
-bash <(curl -s https://codecov.io/bash) -cF go
+go test ./pkg/...

+ 4 - 4
yarn.lock

@@ -1162,6 +1162,10 @@ balanced-match@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
 
+baron@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/baron/-/baron-3.0.3.tgz#0f0a08a567062882e130a0ecfd41a46d52103f4a"
+
 base64-arraybuffer@0.1.5:
   version "0.1.5"
   resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
@@ -7503,10 +7507,6 @@ pend@~1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
 
-perfect-scrollbar@^1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.2.0.tgz#ad23a2529c17f4535f21d1486f8bc3046e31a9d2"
-
 performance-now@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"